From d47fbe1e3773e018786bb699be48551ed1856731 Mon Sep 17 00:00:00 2001 From: sbansla <104902068+sbansla@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:02:58 +0530 Subject: [PATCH] fix: added new httpclient for noauth and bearertoken requests (#808) * added new httpclient for noauth and bearertoken requests --- src/main/java/com/twilio/http/HttpClient.java | 9 +- .../com/twilio/http/NetworkHttpClient.java | 35 ++-- .../com/twilio/http/ValidationClient.java | 3 +- .../bearertoken/BearerTokenHttpClient.java | 109 ++++++++++ .../BearerTokenNetworkHttpClient.java | 179 ++++++++++++++++ .../BearerTokenTwilioRestClient.java | 12 +- .../twilio/http/noauth/NoAuthHttpClient.java | 195 ++++++++---------- .../http/noauth/NoAuthNetworkHttpClient.java | 134 ++++++++++++ .../http/noauth/NoAuthTwilioRestClient.java | 8 +- .../com/twilio/http/CustomHttpClient.java | 2 +- 10 files changed, 533 insertions(+), 153 deletions(-) create mode 100644 src/main/java/com/twilio/http/bearertoken/BearerTokenHttpClient.java create mode 100644 src/main/java/com/twilio/http/bearertoken/BearerTokenNetworkHttpClient.java create mode 100644 src/main/java/com/twilio/http/noauth/NoAuthNetworkHttpClient.java diff --git a/src/main/java/com/twilio/http/HttpClient.java b/src/main/java/com/twilio/http/HttpClient.java index 46938dfad2..71e595ff34 100644 --- a/src/main/java/com/twilio/http/HttpClient.java +++ b/src/main/java/com/twilio/http/HttpClient.java @@ -39,7 +39,7 @@ public abstract class HttpClient { @Getter private Response lastResponse; @Getter - private IRequest lastRequest; + private Request lastRequest; /** * Make a request. @@ -47,7 +47,7 @@ public abstract class HttpClient { * @param request request to make * @return Response of the HTTP request */ - public Response reliableRequest(final IRequest request) { + public Response reliableRequest(final Request request) { return reliableRequest(request, RETRY_CODES, RETRIES, DELAY_MILLIS); } @@ -60,7 +60,7 @@ public Response reliableRequest(final IRequest request) { * @param delayMillis delays between retries * @return Response of the HTTP request */ - public Response reliableRequest(final IRequest request, final int[] retryCodes, int retries, + public Response reliableRequest(final Request request, final int[] retryCodes, int retries, final long delayMillis) { lastRequest = request; Response response = null; @@ -131,6 +131,5 @@ protected boolean shouldRetry(final Response response, final int[] retryCodes) { return false; } - public abstract Response makeRequest(final T request); - + public abstract Response makeRequest(final Request request); } diff --git a/src/main/java/com/twilio/http/NetworkHttpClient.java b/src/main/java/com/twilio/http/NetworkHttpClient.java index 1abfc1e981..2b557a273b 100644 --- a/src/main/java/com/twilio/http/NetworkHttpClient.java +++ b/src/main/java/com/twilio/http/NetworkHttpClient.java @@ -3,7 +3,14 @@ import com.twilio.Twilio; import com.twilio.constant.EnumConstants; import com.twilio.exception.ApiException; -import com.twilio.http.bearertoken.BearerTokenRequest; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + import org.apache.http.HttpEntity; import org.apache.http.HttpHeaders; import org.apache.http.HttpResponse; @@ -19,13 +26,6 @@ import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeader; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; -import java.util.Map; - public class NetworkHttpClient extends HttpClient { protected final org.apache.http.client.HttpClient client; @@ -57,8 +57,7 @@ public NetworkHttpClient(final RequestConfig requestConfig) { public NetworkHttpClient(final RequestConfig requestConfig, final SocketConfig socketConfig) { Collection headers = Arrays.asList( new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION), - // new BasicHeader(HttpHeaders.ACCEPT, "application/json"), - //new BasicHeader(HttpHeaders.ACCEPT, "application/scim+json"), + new BasicHeader(HttpHeaders.ACCEPT, "application/json"), new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8") ); @@ -114,24 +113,16 @@ public NetworkHttpClient(HttpClientBuilder clientBuilder) { * @param request request to make * @return Response of the HTTP request */ - public Response makeRequest(final T request) { + public Response makeRequest(final Request request) { HttpMethod method = request.getMethod(); RequestBuilder builder = RequestBuilder.create(method.toString()) .setUri(request.constructURL().toString()) .setVersion(HttpVersion.HTTP_1_1) .setCharset(StandardCharsets.UTF_8); - if (request instanceof Request) { - Request basicRequest = (Request) request; - // builder.setHeader(HttpHeaders.AUTHORIZATION, basicRequest.getAuthString(accessToken)); - if (basicRequest.requiresAuthentication()) { - builder.addHeader(HttpHeaders.AUTHORIZATION, basicRequest.getAuthString()); - } - } else if (request instanceof BearerTokenRequest) { - BearerTokenRequest bearerTokenRequest = (BearerTokenRequest) request; - if (bearerTokenRequest.requiresAuthentication()) { - builder.addHeader(HttpHeaders.AUTHORIZATION, bearerTokenRequest.getAuthString()); - } + + if (request.requiresAuthentication()) { + builder.addHeader(HttpHeaders.AUTHORIZATION, request.getAuthString()); } for (Map.Entry> entry : request.getHeaderParams().entrySet()) { diff --git a/src/main/java/com/twilio/http/ValidationClient.java b/src/main/java/com/twilio/http/ValidationClient.java index 6473c1bff1..b455db435e 100644 --- a/src/main/java/com/twilio/http/ValidationClient.java +++ b/src/main/java/com/twilio/http/ValidationClient.java @@ -164,8 +164,7 @@ public ValidationClient(final String accountSid, } @Override - public Response makeRequest(IRequest iRequest) { - Request request = (Request)iRequest; + public Response makeRequest(Request request) { RequestBuilder builder = RequestBuilder.create(request.getMethod().toString()) .setUri(request.constructURL().toString()) .setVersion(HttpVersion.HTTP_1_1) diff --git a/src/main/java/com/twilio/http/bearertoken/BearerTokenHttpClient.java b/src/main/java/com/twilio/http/bearertoken/BearerTokenHttpClient.java new file mode 100644 index 0000000000..919e8b0054 --- /dev/null +++ b/src/main/java/com/twilio/http/bearertoken/BearerTokenHttpClient.java @@ -0,0 +1,109 @@ +package com.twilio.http.bearertoken; + +import com.twilio.http.HttpClient; +import com.twilio.http.Request; +import com.twilio.http.Response; +import lombok.Getter; +import lombok.Setter; +import org.apache.http.client.RedirectStrategy; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.SocketConfig; +import org.apache.http.impl.client.DefaultRedirectStrategy; + +public abstract class BearerTokenHttpClient { + // Use constants from HttpClient + public static final RequestConfig DEFAULT_REQUEST_CONFIG = RequestConfig + .custom() + .setConnectTimeout(HttpClient.CONNECTION_TIMEOUT) + .setSocketTimeout(HttpClient.SOCKET_TIMEOUT) + .build(); + public static final SocketConfig DEFAULT_SOCKET_CONFIG = SocketConfig + .custom() + .setSoTimeout(HttpClient.SOCKET_TIMEOUT) + .build(); + @Getter + @Setter + private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(new String[0]); + + @Getter + private Response lastResponse; + @Getter + private BearerTokenRequest lastRequest; + + public Response reliableRequest(final BearerTokenRequest request) { + return reliableRequest(request, HttpClient.RETRY_CODES, HttpClient.RETRIES, HttpClient.DELAY_MILLIS); + } + + public Response reliableRequest(final BearerTokenRequest request, final int[] retryCodes, int retries, + final long delayMillis) { + lastRequest = request; + Response response = null; + while (retries > 0) { + response = makeRequest(request); + + if (!shouldRetry(response, retryCodes)) { + break; + } + + try { + Thread.sleep(delayMillis); + } catch (final InterruptedException e) { + // Delay failed, continue + } + + // Decrement retries + retries--; + } + + lastResponse = response; + + return response; + } + + protected boolean shouldRetry(final Response response, final int[] retryCodes) { + if (response == null) { + return true; + } + + int statusCode = response.getStatusCode(); + int category = (int) Math.floor(statusCode / 100.0); + + for (final int retryCode : retryCodes) { + switch (retryCode) { + case HttpClient.ANY_100: + if (category == 1) { + return true; + } + break; + case HttpClient.ANY_200: + if (category == 2) { + return true; + } + break; + case HttpClient.ANY_300: + if (category == 3) { + return true; + } + break; + case HttpClient.ANY_400: + if (category == 4) { + return true; + } + break; + case HttpClient.ANY_500: + if (category == 5) { + return true; + } + break; + default: + if (statusCode == retryCode) { + return true; + } + break; + } + } + return false; + } + + public abstract Response makeRequest(final BearerTokenRequest request); +} diff --git a/src/main/java/com/twilio/http/bearertoken/BearerTokenNetworkHttpClient.java b/src/main/java/com/twilio/http/bearertoken/BearerTokenNetworkHttpClient.java new file mode 100644 index 0000000000..dbbd4b6d19 --- /dev/null +++ b/src/main/java/com/twilio/http/bearertoken/BearerTokenNetworkHttpClient.java @@ -0,0 +1,179 @@ +package com.twilio.http.bearertoken; + +import com.twilio.Twilio; +import com.twilio.constant.EnumConstants; +import com.twilio.exception.ApiException; +import com.twilio.http.HttpClient; +import com.twilio.http.HttpMethod; +import com.twilio.http.HttpUtility; +import com.twilio.http.Request; +import com.twilio.http.Response; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.client.utils.HttpClientUtils; +import org.apache.http.config.SocketConfig; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class BearerTokenNetworkHttpClient extends BearerTokenHttpClient { + + protected final org.apache.http.client.HttpClient client; + + private boolean isCustomClient; + + /** + * Create a new HTTP Client. + */ + public BearerTokenNetworkHttpClient() { + this(DEFAULT_REQUEST_CONFIG); + } + + /** + * Create a new HTTP Client with a custom request config. + * + * @param requestConfig a RequestConfig. + */ + public BearerTokenNetworkHttpClient(final RequestConfig requestConfig) { + this(requestConfig, DEFAULT_SOCKET_CONFIG); + } + + /** + * Create a new HTTP Client with a custom request and socket config. + * + * @param requestConfig a RequestConfig. + * @param socketConfig a SocketConfig. + */ + public BearerTokenNetworkHttpClient(final RequestConfig requestConfig, final SocketConfig socketConfig) { + Collection headers = Arrays.asList( + new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION), + //new BasicHeader(HttpHeaders.ACCEPT, "application/json"), Orgs API has scim or json support. + new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8") + ); + + String googleAppEngineVersion = System.getProperty("com.google.appengine.runtime.version"); + boolean isGoogleAppEngine = googleAppEngineVersion != null && !googleAppEngineVersion.isEmpty(); + + org.apache.http.impl.client.HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + + if (!isGoogleAppEngine) { + clientBuilder.useSystemProperties(); + } + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setDefaultSocketConfig(socketConfig); + /* + * Example: Lets say client has one server. + * There are 4 servers on edge handling client request. + * Each request takes on an average 500ms (2 request per second) + * Total number request can be server in a second from a route: 20 * 4 * 2 (DefaultMaxPerRoute * edge servers * request per second) + */ + connectionManager.setDefaultMaxPerRoute(20); + connectionManager.setMaxTotal(100); + + client = clientBuilder + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .setDefaultHeaders(headers) + .setRedirectStrategy(this.getRedirectStrategy()) + .build(); + } + + /** + * Create a new HTTP Client using custom configuration. + * @param clientBuilder an HttpClientBuilder. + */ + public BearerTokenNetworkHttpClient(HttpClientBuilder clientBuilder) { + Collection headers = Arrays.asList( + new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION), + //new BasicHeader(HttpHeaders.ACCEPT, "application/json"), Orgs API has scim or json support. + new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8") + ); + isCustomClient = true; + + client = clientBuilder + .setDefaultHeaders(headers) + .setRedirectStrategy(this.getRedirectStrategy()) + .build(); + } + + /** + * Make a request. + * + * @param request request to make + * @return Response of the HTTP request + */ + public Response makeRequest(final BearerTokenRequest request) { + + HttpMethod method = request.getMethod(); + RequestBuilder builder = RequestBuilder.create(method.toString()) + .setUri(request.constructURL().toString()) + .setVersion(HttpVersion.HTTP_1_1) + .setCharset(StandardCharsets.UTF_8); + + if (request.requiresAuthentication()) { + builder.addHeader(HttpHeaders.AUTHORIZATION, request.getAuthString()); + } + + for (Map.Entry> entry : request.getHeaderParams().entrySet()) { + for (String value : entry.getValue()) { + builder.addHeader(entry.getKey(), value); + } + } + + if (method != HttpMethod.GET) { + // TODO: It will be removed after one RC Release. + if (request.getContentType() == null) request.setContentType(EnumConstants.ContentType.FORM_URLENCODED); + if (EnumConstants.ContentType.JSON.getValue().equals(request.getContentType().getValue())) { + HttpEntity entity = new StringEntity(request.getBody(), ContentType.APPLICATION_JSON); + builder.setEntity(entity); + builder.addHeader( + HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.JSON.getValue()); + } else { + builder.addHeader( + HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.FORM_URLENCODED.getValue()); + for (Map.Entry> entry : request.getPostParams().entrySet()) { + for (String value : entry.getValue()) { + builder.addParameter(entry.getKey(), value); + } + } + } + + } + builder.addHeader(HttpHeaders.USER_AGENT, HttpUtility.getUserAgentString(request.getUserAgentExtensions(), isCustomClient)); + + HttpResponse response = null; + + try { + response = client.execute(builder.build()); + HttpEntity entity = response.getEntity(); + return new Response( + // Consume the entire HTTP response before returning the stream + entity == null ? null : new BufferedHttpEntity(entity).getContent(), + response.getStatusLine().getStatusCode(), + response.getAllHeaders() + ); + } catch (IOException e) { + throw new ApiException(e.getMessage(), e); + } finally { + + // Ensure this response is properly closed + HttpClientUtils.closeQuietly(response); + + } + } +} diff --git a/src/main/java/com/twilio/http/bearertoken/BearerTokenTwilioRestClient.java b/src/main/java/com/twilio/http/bearertoken/BearerTokenTwilioRestClient.java index 01b59096a6..0562c72534 100644 --- a/src/main/java/com/twilio/http/bearertoken/BearerTokenTwilioRestClient.java +++ b/src/main/java/com/twilio/http/bearertoken/BearerTokenTwilioRestClient.java @@ -1,13 +1,9 @@ package com.twilio.http.bearertoken; import com.auth0.jwt.JWT; -import com.twilio.http.bearertoken.TokenManager; import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import com.twilio.exception.AuthenticationException; -import com.twilio.http.HttpClient; -import com.twilio.http.NetworkHttpClient; import com.twilio.http.Response; import lombok.Getter; import lombok.Setter; @@ -37,7 +33,7 @@ public class BearerTokenTwilioRestClient { @Getter private final String edge; @Getter - private final HttpClient httpClient; + private final BearerTokenHttpClient httpClient; @Getter private final List userAgentExtensions; @Setter @@ -62,7 +58,7 @@ private BearerTokenTwilioRestClient(BearerTokenTwilioRestClient.Builder b) { public static class Builder { private String region; private String edge; - private HttpClient httpClient; + private BearerTokenHttpClient httpClient; private List userAgentExtensions; private TokenManager tokenManager; @@ -87,7 +83,7 @@ public BearerTokenTwilioRestClient.Builder tokenManager(final TokenManager token return this; } - public BearerTokenTwilioRestClient.Builder httpClient(final HttpClient httpClient) { + public BearerTokenTwilioRestClient.Builder httpClient(final BearerTokenHttpClient httpClient) { this.httpClient = httpClient; return this; } @@ -101,7 +97,7 @@ public BearerTokenTwilioRestClient.Builder userAgentExtensions(final List headers = Arrays.asList( - new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION), - new BasicHeader(HttpHeaders.ACCEPT, "application/json"), - new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8") - ); + public Response reliableRequest(final NoAuthRequest request, final int[] retryCodes, int retries, + final long delayMillis) { + lastRequest = request; + Response response = null; + while (retries > 0) { + response = makeRequest(request); - String googleAppEngineVersion = System.getProperty("com.google.appengine.runtime.version"); - boolean isGoogleAppEngine = googleAppEngineVersion != null && !googleAppEngineVersion.isEmpty(); + if (!shouldRetry(response, retryCodes)) { + break; + } - org.apache.http.impl.client.HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + try { + Thread.sleep(delayMillis); + } catch (final InterruptedException e) { + // Delay failed, continue + } - if (!isGoogleAppEngine) { - clientBuilder.useSystemProperties(); + // Decrement retries + retries--; } - PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); - connectionManager.setDefaultSocketConfig(socketConfig); - /* - * Example: Lets say client has one server. - * There are 4 servers on edge handling client request. - * Each request takes on an average 500ms (2 request per second) - * Total number request can be server in a second from a route: 20 * 4 * 2 (DefaultMaxPerRoute * edge servers * request per second) - */ - connectionManager.setDefaultMaxPerRoute(20); - connectionManager.setMaxTotal(100); + lastResponse = response; - client = clientBuilder - .setConnectionManager(connectionManager) - .setDefaultRequestConfig(requestConfig) - .setDefaultHeaders(headers) - .setRedirectStrategy(this.getRedirectStrategy()) - .build(); + return response; } - - @Override - public Response makeRequest(IRequest request) { - HttpMethod method = request.getMethod(); - RequestBuilder builder = RequestBuilder.create(method.toString()) - .setUri(request.constructURL().toString()) - .setVersion(HttpVersion.HTTP_1_1) - .setCharset(StandardCharsets.UTF_8); - for (Map.Entry> entry : request.getHeaderParams().entrySet()) { - for (String value : entry.getValue()) { - builder.addHeader(entry.getKey(), value); - } + protected boolean shouldRetry(final Response response, final int[] retryCodes) { + if (response == null) { + return true; } - if (method != HttpMethod.GET) { - // TODO: It will be removed after one RC Release. - if (request.getContentType() == null) request.setContentType(EnumConstants.ContentType.FORM_URLENCODED); - if (EnumConstants.ContentType.JSON.getValue().equals(request.getContentType().getValue())) { - HttpEntity entity = new StringEntity(request.getBody(), ContentType.APPLICATION_JSON); - builder.setEntity(entity); - builder.addHeader( - HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.JSON.getValue()); - } else { - builder.addHeader( - HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.FORM_URLENCODED.getValue()); - for (Map.Entry> entry : request.getPostParams().entrySet()) { - for (String value : entry.getValue()) { - builder.addParameter(entry.getKey(), value); + int statusCode = response.getStatusCode(); + int category = (int) Math.floor(statusCode / 100.0); + + for (final int retryCode : retryCodes) { + switch (retryCode) { + case HttpClient.ANY_100: + if (category == 1) { + return true; + } + break; + case HttpClient.ANY_200: + if (category == 2) { + return true; + } + break; + case HttpClient.ANY_300: + if (category == 3) { + return true; } - } + break; + case HttpClient.ANY_400: + if (category == 4) { + return true; + } + break; + case HttpClient.ANY_500: + if (category == 5) { + return true; + } + break; + default: + if (statusCode == retryCode) { + return true; + } + break; } - } - builder.addHeader(HttpHeaders.USER_AGENT, HttpUtility.getUserAgentString(request.getUserAgentExtensions())); - - HttpResponse response = null; - - try { - response = client.execute(builder.build()); - HttpEntity entity = response.getEntity(); - return new Response( - // Consume the entire HTTP response before returning the stream - entity == null ? null : new BufferedHttpEntity(entity).getContent(), - response.getStatusLine().getStatusCode(), - response.getAllHeaders() - ); - } catch (IOException e) { - throw new ApiException(e.getMessage(), e); - } finally { - - // Ensure this response is properly closed - HttpClientUtils.closeQuietly(response); - - } - + return false; } + + public abstract Response makeRequest(NoAuthRequest request); } diff --git a/src/main/java/com/twilio/http/noauth/NoAuthNetworkHttpClient.java b/src/main/java/com/twilio/http/noauth/NoAuthNetworkHttpClient.java new file mode 100644 index 0000000000..df87ef709b --- /dev/null +++ b/src/main/java/com/twilio/http/noauth/NoAuthNetworkHttpClient.java @@ -0,0 +1,134 @@ +package com.twilio.http.noauth; + +import com.twilio.Twilio; +import com.twilio.constant.EnumConstants; +import com.twilio.exception.ApiException; +import com.twilio.http.HttpMethod; +import com.twilio.http.HttpUtility; +import com.twilio.http.Request; +import com.twilio.http.Response; +import org.apache.http.HttpEntity; +import org.apache.http.HttpHeaders; +import org.apache.http.HttpResponse; +import org.apache.http.HttpVersion; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.RequestBuilder; +import org.apache.http.client.utils.HttpClientUtils; +import org.apache.http.config.SocketConfig; +import org.apache.http.entity.BufferedHttpEntity; +import org.apache.http.entity.ContentType; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.message.BasicHeader; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class NoAuthNetworkHttpClient extends NoAuthHttpClient { + + protected final org.apache.http.client.HttpClient client; + public NoAuthNetworkHttpClient() { + this(DEFAULT_REQUEST_CONFIG); + } + + public NoAuthNetworkHttpClient(final RequestConfig requestConfig) { + this(requestConfig, DEFAULT_SOCKET_CONFIG); + } + + public NoAuthNetworkHttpClient(final RequestConfig requestConfig, final SocketConfig socketConfig) { + Collection headers = Arrays.asList( + new BasicHeader("X-Twilio-Client", "java-" + Twilio.VERSION), + new BasicHeader(HttpHeaders.ACCEPT, "application/json"), + new BasicHeader(HttpHeaders.ACCEPT_ENCODING, "utf-8") + ); + + String googleAppEngineVersion = System.getProperty("com.google.appengine.runtime.version"); + boolean isGoogleAppEngine = googleAppEngineVersion != null && !googleAppEngineVersion.isEmpty(); + + org.apache.http.impl.client.HttpClientBuilder clientBuilder = HttpClientBuilder.create(); + + if (!isGoogleAppEngine) { + clientBuilder.useSystemProperties(); + } + + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(); + connectionManager.setDefaultSocketConfig(socketConfig); + /* + * Example: Lets say client has one server. + * There are 4 servers on edge handling client request. + * Each request takes on an average 500ms (2 request per second) + * Total number request can be server in a second from a route: 20 * 4 * 2 (DefaultMaxPerRoute * edge servers * request per second) + */ + connectionManager.setDefaultMaxPerRoute(20); + connectionManager.setMaxTotal(100); + + client = clientBuilder + .setConnectionManager(connectionManager) + .setDefaultRequestConfig(requestConfig) + .setDefaultHeaders(headers) + .setRedirectStrategy(this.getRedirectStrategy()) + .build(); + } + + @Override + public Response makeRequest(NoAuthRequest request) { + HttpMethod method = request.getMethod(); + RequestBuilder builder = RequestBuilder.create(method.toString()) + .setUri(request.constructURL().toString()) + .setVersion(HttpVersion.HTTP_1_1) + .setCharset(StandardCharsets.UTF_8); + + for (Map.Entry> entry : request.getHeaderParams().entrySet()) { + for (String value : entry.getValue()) { + builder.addHeader(entry.getKey(), value); + } + } + + if (method != HttpMethod.GET) { + // TODO: It will be removed after one RC Release. + if (request.getContentType() == null) request.setContentType(EnumConstants.ContentType.FORM_URLENCODED); + if (EnumConstants.ContentType.JSON.getValue().equals(request.getContentType().getValue())) { + HttpEntity entity = new StringEntity(request.getBody(), ContentType.APPLICATION_JSON); + builder.setEntity(entity); + builder.addHeader( + HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.JSON.getValue()); + } else { + builder.addHeader( + HttpHeaders.CONTENT_TYPE, EnumConstants.ContentType.FORM_URLENCODED.getValue()); + for (Map.Entry> entry : request.getPostParams().entrySet()) { + for (String value : entry.getValue()) { + builder.addParameter(entry.getKey(), value); + } + } + } + + } + builder.addHeader(HttpHeaders.USER_AGENT, HttpUtility.getUserAgentString(request.getUserAgentExtensions())); + + HttpResponse response = null; + + try { + response = client.execute(builder.build()); + HttpEntity entity = response.getEntity(); + return new Response( + // Consume the entire HTTP response before returning the stream + entity == null ? null : new BufferedHttpEntity(entity).getContent(), + response.getStatusLine().getStatusCode(), + response.getAllHeaders() + ); + } catch (IOException e) { + throw new ApiException(e.getMessage(), e); + } finally { + + // Ensure this response is properly closed + HttpClientUtils.closeQuietly(response); + + } + + } +} diff --git a/src/main/java/com/twilio/http/noauth/NoAuthTwilioRestClient.java b/src/main/java/com/twilio/http/noauth/NoAuthTwilioRestClient.java index 89d1e28805..84ef657419 100644 --- a/src/main/java/com/twilio/http/noauth/NoAuthTwilioRestClient.java +++ b/src/main/java/com/twilio/http/noauth/NoAuthTwilioRestClient.java @@ -27,7 +27,7 @@ public class NoAuthTwilioRestClient { @Getter private final String edge; @Getter - private final HttpClient httpClient; + private final NoAuthNetworkHttpClient httpClient; @Getter private final List userAgentExtensions; private static final Logger logger = LoggerFactory.getLogger(NoAuthTwilioRestClient.class); @@ -51,7 +51,7 @@ private NoAuthTwilioRestClient(NoAuthTwilioRestClient.Builder b) { public static class Builder { private String region; private String edge; - private HttpClient httpClient; + private NoAuthNetworkHttpClient httpClient; private List userAgentExtensions; public Builder() { @@ -70,7 +70,7 @@ public NoAuthTwilioRestClient.Builder edge(final String edge) { return this; } - public NoAuthTwilioRestClient.Builder httpClient(final HttpClient httpClient) { + public NoAuthTwilioRestClient.Builder httpClient(final NoAuthNetworkHttpClient httpClient) { this.httpClient = httpClient; return this; } @@ -84,7 +84,7 @@ public NoAuthTwilioRestClient.Builder userAgentExtensions(final List use public NoAuthTwilioRestClient build() { if (this.httpClient == null) { - this.httpClient = new NetworkHttpClient(); + this.httpClient = new NoAuthNetworkHttpClient(); } return new NoAuthTwilioRestClient(this); } diff --git a/src/test/java/com/twilio/http/CustomHttpClient.java b/src/test/java/com/twilio/http/CustomHttpClient.java index 9d087555ab..ecd6bfb14e 100644 --- a/src/test/java/com/twilio/http/CustomHttpClient.java +++ b/src/test/java/com/twilio/http/CustomHttpClient.java @@ -24,7 +24,7 @@ public CustomHttpClient() { } @Override - public Response makeRequest(T request) { + public Response makeRequest(Request request) { HttpMethod method = request.getMethod(); RequestBuilder builder = RequestBuilder.create(method.toString()) .setUri(request.constructURL().toString())