Skip to content

Commit e74eddc

Browse files
authored
Merge pull request #62 from ipinfo/silvano/eng-648-add-resproxy-support-in-ipinfojava-library
Add Residential Proxy API support
2 parents dbaf079 + 4d8636e commit e74eddc

File tree

4 files changed

+298
-81
lines changed

4 files changed

+298
-81
lines changed

src/main/java/io/ipinfo/api/IPinfo.java

Lines changed: 143 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
import io.ipinfo.api.model.ASNResponse;
1111
import io.ipinfo.api.model.IPResponse;
1212
import io.ipinfo.api.model.MapResponse;
13+
import io.ipinfo.api.model.ResproxyResponse;
1314
import io.ipinfo.api.request.ASNRequest;
1415
import io.ipinfo.api.request.IPRequest;
1516
import io.ipinfo.api.request.MapRequest;
16-
import okhttp3.*;
17-
18-
import javax.annotation.ParametersAreNonnullByDefault;
17+
import io.ipinfo.api.request.ResproxyRequest;
1918
import java.io.IOException;
2019
import java.lang.reflect.Type;
2120
import java.time.Duration;
@@ -26,15 +25,19 @@
2625
import java.util.concurrent.CountDownLatch;
2726
import java.util.concurrent.TimeUnit;
2827
import java.util.function.BiConsumer;
28+
import javax.annotation.ParametersAreNonnullByDefault;
29+
import okhttp3.*;
2930

3031
public class IPinfo {
32+
3133
private static final int batchMaxSize = 1000;
3234
private static final int batchReqTimeoutDefault = 5;
33-
private static final BatchReqOpts defaultBatchReqOpts = new BatchReqOpts.Builder()
35+
private static final BatchReqOpts defaultBatchReqOpts =
36+
new BatchReqOpts.Builder()
3437
.setBatchSize(batchMaxSize)
3538
.setTimeoutPerBatch(batchReqTimeoutDefault)
3639
.build();
37-
private final static Gson gson = new Gson();
40+
private static final Gson gson = new Gson();
3841

3942
private final OkHttpClient client;
4043
private final Context context;
@@ -49,7 +52,9 @@ public class IPinfo {
4952
}
5053

5154
public static void main(String... args) {
52-
System.out.println("This library is not meant to be run as a standalone jar.");
55+
System.out.println(
56+
"This library is not meant to be run as a standalone jar."
57+
);
5358
System.exit(0);
5459
}
5560

@@ -61,7 +66,7 @@ public static void main(String... args) {
6166
* @throws RateLimitedException an exception when your api key has been rate limited.
6267
*/
6368
public IPResponse lookupIP(String ip) throws RateLimitedException {
64-
IPResponse response = (IPResponse)cache.get(cacheKey(ip));
69+
IPResponse response = (IPResponse) cache.get(cacheKey(ip));
6570
if (response != null) {
6671
return response;
6772
}
@@ -81,7 +86,7 @@ public IPResponse lookupIP(String ip) throws RateLimitedException {
8186
* @throws RateLimitedException an exception when your api key has been rate limited.
8287
*/
8388
public ASNResponse lookupASN(String asn) throws RateLimitedException {
84-
ASNResponse response = (ASNResponse)cache.get(cacheKey(asn));
89+
ASNResponse response = (ASNResponse) cache.get(cacheKey(asn));
8590
if (response != null) {
8691
return response;
8792
}
@@ -93,6 +98,29 @@ public ASNResponse lookupASN(String asn) throws RateLimitedException {
9398
return response;
9499
}
95100

101+
/**
102+
* Lookup residential proxy information using the IP.
103+
*
104+
* @param ip the ip string to lookup - accepts both ipv4 and ipv6.
105+
* @return ResproxyResponse response from the api.
106+
* @throws RateLimitedException an exception when your api key has been rate limited.
107+
*/
108+
public ResproxyResponse lookupResproxy(String ip)
109+
throws RateLimitedException {
110+
String cacheKeyStr = "resproxy:" + ip;
111+
ResproxyResponse response = (ResproxyResponse) cache.get(
112+
cacheKey(cacheKeyStr)
113+
);
114+
if (response != null) {
115+
return response;
116+
}
117+
118+
response = new ResproxyRequest(client, token, ip).handle();
119+
120+
cache.set(cacheKey(cacheKeyStr), response);
121+
return response;
122+
}
123+
96124
/**
97125
* Get a map of a list of IPs.
98126
*
@@ -112,9 +140,8 @@ public String getMap(List<String> ips) throws RateLimitedException {
112140
* @return the result where each URL is the key and the value is the data for that URL.
113141
* @throws RateLimitedException an exception when your API key has been rate limited.
114142
*/
115-
public ConcurrentHashMap<String, Object> getBatch(
116-
List<String> urls
117-
) throws RateLimitedException {
143+
public ConcurrentHashMap<String, Object> getBatch(List<String> urls)
144+
throws RateLimitedException {
118145
return this.getBatchGeneric(urls, defaultBatchReqOpts);
119146
}
120147

@@ -127,8 +154,8 @@ public ConcurrentHashMap<String, Object> getBatch(
127154
* @throws RateLimitedException an exception when your API key has been rate limited.
128155
*/
129156
public ConcurrentHashMap<String, Object> getBatch(
130-
List<String> urls,
131-
BatchReqOpts opts
157+
List<String> urls,
158+
BatchReqOpts opts
132159
) throws RateLimitedException {
133160
return this.getBatchGeneric(urls, opts);
134161
}
@@ -140,10 +167,11 @@ public ConcurrentHashMap<String, Object> getBatch(
140167
* @return the result where each IP is the key and the value is the data for that IP.
141168
* @throws RateLimitedException an exception when your API key has been rate limited.
142169
*/
143-
public ConcurrentHashMap<String, IPResponse> getBatchIps(
144-
List<String> ips
145-
) throws RateLimitedException {
146-
return new ConcurrentHashMap(this.getBatchGeneric(ips, defaultBatchReqOpts));
170+
public ConcurrentHashMap<String, IPResponse> getBatchIps(List<String> ips)
171+
throws RateLimitedException {
172+
return new ConcurrentHashMap(
173+
this.getBatchGeneric(ips, defaultBatchReqOpts)
174+
);
147175
}
148176

149177
/**
@@ -155,8 +183,8 @@ public ConcurrentHashMap<String, IPResponse> getBatchIps(
155183
* @throws RateLimitedException an exception when your API key has been rate limited.
156184
*/
157185
public ConcurrentHashMap<String, IPResponse> getBatchIps(
158-
List<String> ips,
159-
BatchReqOpts opts
186+
List<String> ips,
187+
BatchReqOpts opts
160188
) throws RateLimitedException {
161189
return new ConcurrentHashMap(this.getBatchGeneric(ips, opts));
162190
}
@@ -169,9 +197,11 @@ public ConcurrentHashMap<String, IPResponse> getBatchIps(
169197
* @throws RateLimitedException an exception when your API key has been rate limited.
170198
*/
171199
public ConcurrentHashMap<String, ASNResponse> getBatchAsns(
172-
List<String> asns
200+
List<String> asns
173201
) throws RateLimitedException {
174-
return new ConcurrentHashMap(this.getBatchGeneric(asns, defaultBatchReqOpts));
202+
return new ConcurrentHashMap(
203+
this.getBatchGeneric(asns, defaultBatchReqOpts)
204+
);
175205
}
176206

177207
/**
@@ -183,15 +213,15 @@ public ConcurrentHashMap<String, ASNResponse> getBatchAsns(
183213
* @throws RateLimitedException an exception when your API key has been rate limited.
184214
*/
185215
public ConcurrentHashMap<String, ASNResponse> getBatchAsns(
186-
List<String> asns,
187-
BatchReqOpts opts
216+
List<String> asns,
217+
BatchReqOpts opts
188218
) throws RateLimitedException {
189219
return new ConcurrentHashMap(this.getBatchGeneric(asns, opts));
190220
}
191221

192222
private ConcurrentHashMap<String, Object> getBatchGeneric(
193-
List<String> urls,
194-
BatchReqOpts opts
223+
List<String> urls,
224+
BatchReqOpts opts
195225
) throws RateLimitedException {
196226
int batchSize;
197227
int timeoutPerBatch;
@@ -201,7 +231,7 @@ private ConcurrentHashMap<String, Object> getBatchGeneric(
201231
// if the cache is available, filter out URLs already cached.
202232
result = new ConcurrentHashMap<>(urls.size());
203233
if (this.cache != null) {
204-
lookupUrls = new ArrayList<>(urls.size()/2);
234+
lookupUrls = new ArrayList<>(urls.size() / 2);
205235
for (String url : urls) {
206236
Object val = cache.get(cacheKey(url));
207237
if (val != null) {
@@ -244,12 +274,14 @@ private ConcurrentHashMap<String, Object> getBatchGeneric(
244274

245275
// prepare latch & common request.
246276
// each request, when complete, will countdown the latch.
247-
CountDownLatch latch = new CountDownLatch((int)Math.ceil(lookupUrls.size()/1000.0));
277+
CountDownLatch latch = new CountDownLatch(
278+
(int) Math.ceil(lookupUrls.size() / 1000.0)
279+
);
248280
Request.Builder reqCommon = new Request.Builder()
249-
.url(postUrl)
250-
.addHeader("Content-Type", "application/json")
251-
.addHeader("Authorization", Credentials.basic(token, ""))
252-
.addHeader("User-Agent", "IPinfoClient/Java/3.2.0");
281+
.url(postUrl)
282+
.addHeader("Content-Type", "application/json")
283+
.addHeader("Authorization", Credentials.basic(token, ""))
284+
.addHeader("User-Agent", "IPinfoClient/Java/3.2.0");
253285

254286
for (int i = 0; i < lookupUrls.size(); i += batchSize) {
255287
// create chunk.
@@ -263,57 +295,83 @@ private ConcurrentHashMap<String, Object> getBatchGeneric(
263295
String urlListJson = gson.toJson(urlsChunk);
264296
RequestBody requestBody = RequestBody.create(null, urlListJson);
265297
Request req = reqCommon.post(requestBody).build();
266-
OkHttpClient chunkClient = client.newBuilder()
267-
.connectTimeout(timeoutPerBatch, TimeUnit.SECONDS)
268-
.readTimeout(timeoutPerBatch, TimeUnit.SECONDS)
269-
.build();
270-
chunkClient.newCall(req).enqueue(new Callback() {
271-
@Override
272-
@ParametersAreNonnullByDefault
273-
public void onFailure(Call call, IOException e) {
274-
latch.countDown();
275-
}
276-
277-
@Override
278-
@ParametersAreNonnullByDefault
279-
public void onResponse(Call call, Response response) throws IOException {
280-
if (response.body() == null || response.code() == 429) {
281-
return;
282-
}
298+
OkHttpClient chunkClient = client
299+
.newBuilder()
300+
.connectTimeout(timeoutPerBatch, TimeUnit.SECONDS)
301+
.readTimeout(timeoutPerBatch, TimeUnit.SECONDS)
302+
.build();
303+
chunkClient
304+
.newCall(req)
305+
.enqueue(
306+
new Callback() {
307+
@Override
308+
@ParametersAreNonnullByDefault
309+
public void onFailure(Call call, IOException e) {
310+
latch.countDown();
311+
}
283312

284-
Type respType = new TypeToken<HashMap<String, Object>>() {}.getType();
285-
HashMap<String, Object> localResult
286-
= gson.fromJson(response.body().string(), respType);
287-
localResult.forEach(new BiConsumer<String, Object>() {
288313
@Override
289-
public void accept(String k, Object v) {
290-
if (k.startsWith("AS")) {
291-
String vStr = gson.toJson(v);
292-
ASNResponse vCasted = gson.fromJson(vStr, ASNResponse.class);
293-
vCasted.setContext(context);
294-
result.put(k, vCasted);
295-
} else if (InetAddresses.isInetAddress(k)) {
296-
String vStr = gson.toJson(v);
297-
IPResponse vCasted = gson.fromJson(vStr, IPResponse.class);
298-
vCasted.setContext(context);
299-
result.put(k, vCasted);
300-
} else {
301-
result.put(k, v);
314+
@ParametersAreNonnullByDefault
315+
public void onResponse(Call call, Response response)
316+
throws IOException {
317+
if (
318+
response.body() == null ||
319+
response.code() == 429
320+
) {
321+
return;
302322
}
303-
}
304-
});
305323

306-
latch.countDown();
307-
}
308-
});
324+
Type respType = new TypeToken<
325+
HashMap<String, Object>
326+
>() {}.getType();
327+
HashMap<String, Object> localResult = gson.fromJson(
328+
response.body().string(),
329+
respType
330+
);
331+
localResult.forEach(
332+
new BiConsumer<String, Object>() {
333+
@Override
334+
public void accept(String k, Object v) {
335+
if (k.startsWith("AS")) {
336+
String vStr = gson.toJson(v);
337+
ASNResponse vCasted = gson.fromJson(
338+
vStr,
339+
ASNResponse.class
340+
);
341+
vCasted.setContext(context);
342+
result.put(k, vCasted);
343+
} else if (
344+
InetAddresses.isInetAddress(k)
345+
) {
346+
String vStr = gson.toJson(v);
347+
IPResponse vCasted = gson.fromJson(
348+
vStr,
349+
IPResponse.class
350+
);
351+
vCasted.setContext(context);
352+
result.put(k, vCasted);
353+
} else {
354+
result.put(k, v);
355+
}
356+
}
357+
}
358+
);
359+
360+
latch.countDown();
361+
}
362+
}
363+
);
309364
}
310365

311366
// wait for all requests to finish.
312367
try {
313368
if (opts.timeoutTotal == 0) {
314369
latch.await();
315370
} else {
316-
boolean success = latch.await(opts.timeoutTotal, TimeUnit.SECONDS);
371+
boolean success = latch.await(
372+
opts.timeoutTotal,
373+
TimeUnit.SECONDS
374+
);
317375
if (!success) {
318376
if (result.size() == 0) {
319377
return null;
@@ -350,10 +408,11 @@ public void accept(String k, Object v) {
350408
* @return the versioned cache key.
351409
*/
352410
public static String cacheKey(String k) {
353-
return k+":1";
411+
return k + ":1";
354412
}
355413

356414
public static class Builder {
415+
357416
private OkHttpClient client = new OkHttpClient.Builder().build();
358417
private String token = "";
359418
private Cache cache = new SimpleCache(Duration.ofDays(1));
@@ -379,16 +438,17 @@ public IPinfo build() {
379438
}
380439

381440
public static class BatchReqOpts {
441+
382442
public final int batchSize;
383443
public final int timeoutPerBatch;
384444
public final int timeoutTotal;
385445
public final boolean filter;
386446

387447
public BatchReqOpts(
388-
int batchSize,
389-
int timeoutPerBatch,
390-
int timeoutTotal,
391-
boolean filter
448+
int batchSize,
449+
int timeoutPerBatch,
450+
int timeoutTotal,
451+
boolean filter
392452
) {
393453
this.batchSize = batchSize;
394454
this.timeoutPerBatch = timeoutPerBatch;
@@ -397,6 +457,7 @@ public BatchReqOpts(
397457
}
398458

399459
public static class Builder {
460+
400461
private int batchSize = 1000;
401462
private int timeoutPerBatch = 5;
402463
private int timeoutTotal = 0;
@@ -462,7 +523,12 @@ public Builder setFilter(boolean filter) {
462523
}
463524

464525
public IPinfo.BatchReqOpts build() {
465-
return new IPinfo.BatchReqOpts(batchSize, timeoutPerBatch, timeoutTotal, filter);
526+
return new IPinfo.BatchReqOpts(
527+
batchSize,
528+
timeoutPerBatch,
529+
timeoutTotal,
530+
filter
531+
);
466532
}
467533
}
468534
}

0 commit comments

Comments
 (0)