Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit 1b3b412

Browse files
committed
Merge pull request #27 from algolia/feat/HttpURLConnection
Refactoring with HTTPUrlConnexion
2 parents e72f93f + ab0a8d9 commit 1b3b412

File tree

1 file changed

+84
-67
lines changed

1 file changed

+84
-67
lines changed

algoliasearch/src/main/java/com/algolia/search/saas/BaseAPIClient.java

Lines changed: 84 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,9 @@
2323

2424
package com.algolia.search.saas;
2525

26-
import org.apache.http.HttpEntity;
27-
import org.apache.http.HttpResponse;
28-
import org.apache.http.client.methods.HttpDelete;
29-
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
30-
import org.apache.http.client.methods.HttpGet;
31-
import org.apache.http.client.methods.HttpPost;
32-
import org.apache.http.client.methods.HttpPut;
33-
import org.apache.http.client.methods.HttpRequestBase;
3426
import org.apache.http.entity.StringEntity;
35-
import org.apache.http.impl.client.DefaultHttpClient;
3627
import org.apache.http.message.BasicHeader;
3728
import org.apache.http.protocol.HTTP;
38-
import org.apache.http.util.EntityUtils;
3929
import org.json.JSONArray;
4030
import org.json.JSONException;
4131
import org.json.JSONObject;
@@ -45,8 +35,8 @@
4535
import java.io.InputStream;
4636
import java.io.InputStreamReader;
4737
import java.io.UnsupportedEncodingException;
48-
import java.net.URI;
49-
import java.net.URISyntaxException;
38+
import java.net.HttpURLConnection;
39+
import java.net.URL;
5040
import java.net.URLEncoder;
5141
import java.util.Arrays;
5242
import java.util.HashMap;
@@ -68,7 +58,6 @@ abstract class BaseAPIClient {
6858
private final String apiKey;
6959
private final List<String> readHostsArray;
7060
private final List<String> writeHostsArray;
71-
private final DefaultHttpClient httpClient;
7261
private String tagFilters;
7362
private String userToken;
7463
private HashMap<String, String> headers;
@@ -102,7 +91,6 @@ protected BaseAPIClient(String applicationID, String apiKey, List<String> hostsA
10291
} else {
10392
readHostsArray = writeHostsArray = hostsArray;
10493
}
105-
httpClient = new DefaultHttpClient();
10694
headers = new HashMap<String, String>();
10795
}
10896

@@ -329,7 +317,7 @@ protected JSONObject putRequest(String url, String obj) throws AlgoliaException
329317
return _request(Method.PUT, url, obj, writeHostsArray, httpConnectTimeoutMS, httpSocketTimeoutMS);
330318
}
331319

332-
private JSONObject _getAnswerObject(InputStream istream) throws IOException, JSONException {
320+
private String _getAnswer(InputStream istream) throws IOException {
333321
InputStreamReader is = new InputStreamReader(istream, "UTF-8");
334322
StringBuilder builder= new StringBuilder();
335323
char[] buf = new char[1000];
@@ -338,121 +326,144 @@ private JSONObject _getAnswerObject(InputStream istream) throws IOException, JSO
338326
builder.append(buf, 0, l);
339327
l = is.read(buf);
340328
}
341-
JSONTokener tokener = new JSONTokener(builder.toString());
342-
JSONObject res = new JSONObject(tokener);
343329
is.close();
344-
return res;
330+
return builder.toString();
331+
}
332+
333+
private JSONObject _getJSONObject(String input) throws JSONException {
334+
return new JSONObject(new JSONTokener(input));
335+
}
336+
337+
private JSONObject _getAnswerObject(InputStream istream) throws IOException, JSONException {
338+
return _getJSONObject(_getAnswer(istream));
345339
}
346340

347341
private synchronized JSONObject _request(Method m, String url, String json, List<String> hostsArray, int connectTimeout, int readTimeout) throws AlgoliaException {
348-
HttpRequestBase req;
342+
String requestMethod;
349343
HashMap<String, String> errors = new HashMap<String, String>();
350344
// for each host
351345
for (String host : hostsArray) {
352346
switch (m) {
353347
case DELETE:
354-
req = new HttpDelete();
348+
requestMethod = "DELETE";
355349
break;
356350
case GET:
357-
req = new HttpGet();
351+
requestMethod = "GET";
358352
break;
359353
case POST:
360-
req = new HttpPost();
354+
requestMethod = "POST";
361355
break;
362356
case PUT:
363-
req = new HttpPut();
357+
requestMethod = "PUT";
364358
break;
365359
default:
366360
throw new IllegalArgumentException("Method " + m + " is not supported");
367361
}
368362

369363
// set URL
370-
try {
371-
req.setURI(new URI("https://" + host + url));
372-
} catch (URISyntaxException e) {
373-
// never reached
374-
throw new IllegalStateException(e);
364+
URL hostURL;
365+
HttpURLConnection hostConnection;
366+
try{
367+
hostURL = new URL("https://" + host + url);
368+
hostConnection = (HttpURLConnection) hostURL.openConnection();
369+
hostConnection.setRequestMethod(requestMethod);
370+
} catch (IOException e) {
371+
// on error continue on the next host
372+
addError(errors, host, e);
373+
continue;
375374
}
376375

376+
hostConnection.setConnectTimeout(connectTimeout);
377+
hostConnection.setReadTimeout(readTimeout);
378+
377379
// set auth headers
378-
req.setHeader("X-Algolia-Application-Id", this.applicationID);
379-
req.setHeader("X-Algolia-API-Key", this.apiKey);
380+
hostConnection.setRequestProperty("X-Algolia-Application-Id", this.applicationID);
381+
hostConnection.setRequestProperty("X-Algolia-API-Key", this.apiKey);
380382
for (Map.Entry<String, String> entry : headers.entrySet()) {
381-
req.setHeader(entry.getKey(), entry.getValue());
383+
hostConnection.setRequestProperty(entry.getKey(), entry.getValue());
382384
}
383385

384386
// set user agent
385-
req.setHeader("User-Agent", "Algolia for Android " + version);
387+
hostConnection.setRequestProperty("User-Agent", "Algolia for Android " + version);
388+
386389

387390
// set optional headers
388391
if (this.userToken != null) {
389-
req.setHeader("X-Algolia-UserToken", this.userToken);
392+
hostConnection.setRequestProperty("X-Algolia-UserToken", this.userToken);
390393
}
391394
if (this.tagFilters != null) {
392-
req.setHeader("X-Algolia-TagFilters", this.tagFilters);
395+
hostConnection.setRequestProperty("X-Algolia-TagFilters", this.tagFilters);
393396
}
394-
req.addHeader("Accept-Encoding","gzip");
395397

396-
// set JSON entity
398+
// write JSON entity
397399
if (json != null) {
398-
if (!(req instanceof HttpEntityEnclosingRequestBase)) {
400+
if (!(requestMethod.equals("PUT") || requestMethod.equals("POST"))) {
399401
throw new IllegalArgumentException("Method " + m + " cannot enclose entity");
400402
}
401-
req.setHeader("Content-type", "application/json");
403+
hostConnection.setRequestProperty("Content-type", "application/json");
404+
hostConnection.setDoOutput(true);
402405
try {
403406
StringEntity se = new StringEntity(json, "UTF-8");
404407
se.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
405-
((HttpEntityEnclosingRequestBase) req).setEntity(se);
408+
se.writeTo(hostConnection.getOutputStream());
406409
} catch (UnsupportedEncodingException e) {
407410
throw new AlgoliaException("Invalid JSON Object: " + json);
411+
} catch (IOException e) {
412+
throw new AlgoliaException("Could not open output stream: " + e.getLocalizedMessage());
408413
}
409414
}
410415

411-
httpClient.getParams().setParameter("http.socket.timeout", readTimeout);
412-
httpClient.getParams().setParameter("http.connection.timeout", connectTimeout);
413-
414-
HttpResponse response;
416+
int code;
415417
try {
416-
response = httpClient.execute(req);
418+
code = hostConnection.getResponseCode();
417419
} catch (IOException e) {
418420
// on error continue on the next host
419-
errors.put(host, String.format("%s=%s", e.getClass().getName(), e.getMessage()));
421+
addError(errors, host, e);
420422
continue;
421423
}
422-
int code = response.getStatusLine().getStatusCode();
424+
425+
InputStream stream = hostConnection.getErrorStream(); // Response is in ErrorStream unless code = 200
423426
if (code / 100 == 2) {
424427
// OK
428+
try {
429+
stream = hostConnection.getInputStream();
430+
} catch (IOException e) {
431+
throw new AlgoliaException("Could not open input stream: " + e.getLocalizedMessage());
432+
}
425433
} else if (code / 100 == 4) {
426434
String message = "Error detected in backend";
427435
try {
428-
message = _getAnswerObject(response.getEntity().getContent()).getString("message");
436+
message = _getAnswerObject(stream).getString("message");
429437
} catch (IOException e) {
438+
addError(errors, host, e);
430439
continue;
431440
} catch (JSONException e) {
432441
throw new AlgoliaException("JSON decode error:" + e.getMessage());
433442
}
434-
consumeQuietly(response.getEntity());
443+
consumeQuietly(hostConnection);
435444
throw new AlgoliaException(message);
436445
} else {
437446
try {
438-
errors.put(host, EntityUtils.toString(response.getEntity()));
447+
errors.put(host, _getAnswer(stream));
439448
} catch (IOException e) {
440449
errors.put(host, String.valueOf(code));
441450
}
442-
consumeQuietly(response.getEntity());
451+
consumeQuietly(hostConnection);
443452
// KO, continue
444453
continue;
445454
}
446455
try {
447-
String encoding = response.getEntity().getContentEncoding() != null ? response.getEntity().getContentEncoding().getValue() : null;
448-
if (encoding != null && encoding.contains("gzip"))
449-
return _getAnswerObject(new GZIPInputStream(response.getEntity().getContent()));
450-
else
451-
return _getAnswerObject(response.getEntity().getContent());
452-
} catch (IOException e) {
453-
continue;
456+
String encoding = hostConnection.getContentEncoding();
457+
if (encoding != null && encoding.contains("gzip")) {
458+
return _getAnswerObject(new GZIPInputStream(stream));
459+
}
460+
else {
461+
return _getAnswerObject(stream);
462+
}
454463
} catch (JSONException e) {
455464
throw new AlgoliaException("JSON decode error:" + e.getMessage());
465+
} catch (IOException e) {
466+
throw new AlgoliaException("Data decoding error:" + e.getMessage());
456467
}
457468
}
458469
StringBuilder builder = new StringBuilder("Hosts unreachable: ");
@@ -467,23 +478,29 @@ private synchronized JSONObject _request(Method m, String url, String json, List
467478
throw new AlgoliaException(builder.toString());
468479
}
469480

481+
private void addError(HashMap<String, String> errors, String host, IOException e) {
482+
errors.put(host, String.format("%s=%s", e.getClass().getName(), e.getMessage()));
483+
}
484+
470485
/**
471486
* Ensures that the entity content is fully consumed and the content stream, if exists,
472487
* is closed.
473488
*/
474-
private void consumeQuietly(final HttpEntity entity) {
475-
if (entity == null) {
476-
return;
477-
}
489+
private void consumeQuietly(final HttpURLConnection connection) {
478490
try {
479-
if (entity.isStreaming()) {
480-
InputStream instream = entity.getContent();
481-
if (instream != null) {
482-
instream.close();
483-
}
491+
int read = 0;
492+
while (read != -1) {
493+
read = connection.getInputStream().read();
494+
}
495+
connection.getInputStream().close();
496+
read = 0;
497+
while (read != -1) {
498+
read = connection.getErrorStream().read();
484499
}
500+
connection.getErrorStream().close();
501+
connection.disconnect();
485502
} catch (IOException e) {
486-
// not fatal
503+
// no inputStream to close
487504
}
488505
}
489506
}

0 commit comments

Comments
 (0)