diff --git a/pom.xml b/pom.xml
index caee2b2..b06eb84 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,8 +43,8 @@
- 1.11
- 1.11
+ 1.8
+ 1.8
UTF-8
@@ -55,11 +55,22 @@
4.13.2
test
+
+ org.mockito
+ mockito-core
+ 4.6.1
+ test
+
com.fasterxml.jackson.core
jackson-databind
2.13.3
+
+ com.squareup.okhttp3
+ okhttp
+ 4.10.0
+
@@ -148,8 +159,8 @@
org.apache.maven.plugins
maven-compiler-plugin
-
- 11
+
+ 8
diff --git a/src/main/java/com/smartystreets/api/ClientBuilder.java b/src/main/java/com/smartystreets/api/ClientBuilder.java
index 01bf4e3..27b1b84 100644
--- a/src/main/java/com/smartystreets/api/ClientBuilder.java
+++ b/src/main/java/com/smartystreets/api/ClientBuilder.java
@@ -2,7 +2,6 @@
import java.net.InetSocketAddress;
import java.net.Proxy;
-import java.net.ProxySelector;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -27,7 +26,7 @@ public class ClientBuilder {
private int maxRetries;
private int maxTimeout;
private String urlPrefix;
- private ProxySelector proxy;
+ private Proxy proxy;
private Map customHeaders;
private List licenses = new ArrayList<>();
@@ -112,7 +111,7 @@ public ClientBuilder withCustomHeaders(Map customHeaders) {
* @return Returns this to accommodate method chaining.
*/
public ClientBuilder withProxy(Proxy.Type proxyType, String proxyHost, int proxyPort) {
- this.proxy = ProxySelector.of(new InetSocketAddress(proxyHost, proxyPort));
+ this.proxy = new Proxy(proxyType, new InetSocketAddress(proxyHost, proxyPort));
return this;
}
diff --git a/src/main/java/com/smartystreets/api/Request.java b/src/main/java/com/smartystreets/api/Request.java
index 1331244..5ee6a19 100644
--- a/src/main/java/com/smartystreets/api/Request.java
+++ b/src/main/java/com/smartystreets/api/Request.java
@@ -1,7 +1,6 @@
package com.smartystreets.api;
import java.net.URLEncoder;
-import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -36,7 +35,7 @@ public void putParameter(String name, String value) {
private static String urlEncode(String value) {
try {
- return URLEncoder.encode(value, StandardCharsets.UTF_8);
+ return URLEncoder.encode(value, CHARSET);
} catch (Exception ex) {
return "";
}
diff --git a/src/main/java/com/smartystreets/api/RetrySender.java b/src/main/java/com/smartystreets/api/RetrySender.java
index 5425b09..bcbee3d 100644
--- a/src/main/java/com/smartystreets/api/RetrySender.java
+++ b/src/main/java/com/smartystreets/api/RetrySender.java
@@ -22,7 +22,11 @@ public Response send(Request request) throws SmartyException, IOException, Inter
for (int i = 0; i <= this.maxRetries; i++) {
Response response = this.trySend(request, i);
if (response instanceof TooManyRequestsResponse) {
- long wait = ((TooManyRequestsResponse) response).getHeaders().firstValueAsLong("Retry-After").orElse(10L);
+ long wait = 10L;
+ String retryAfter = ((TooManyRequestsResponse) response).getHeaders().get("Retry-After");
+ if (retryAfter != null && retryAfter.length() > 0) {
+ wait = Long.parseLong(retryAfter);
+ }
if (wait < 1) {
wait = 1L;
}
diff --git a/src/main/java/com/smartystreets/api/SmartySender.java b/src/main/java/com/smartystreets/api/SmartySender.java
index 6750f55..ce39706 100644
--- a/src/main/java/com/smartystreets/api/SmartySender.java
+++ b/src/main/java/com/smartystreets/api/SmartySender.java
@@ -1,15 +1,14 @@
package com.smartystreets.api;
import com.smartystreets.api.exceptions.SmartyException;
+import okhttp3.Headers;
+import okhttp3.OkHttpClient;
+import okhttp3.RequestBody;
import java.io.IOException;
-import java.net.ProxySelector;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Duration;
+import java.net.Proxy;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -17,11 +16,11 @@
public class SmartySender implements Sender {
private int maxTimeOut;
- private HttpClient client;
+ private OkHttpClient client;
public SmartySender() {
this.maxTimeOut = 10000;
- this.client = HttpClient.newHttpClient();
+ this.client = new OkHttpClient();
}
public SmartySender(int maxTimeout) {
@@ -29,44 +28,58 @@ public SmartySender(int maxTimeout) {
this.maxTimeOut = maxTimeout;
}
- SmartySender(int maxTimeOut, ProxySelector proxy) {
+ SmartySender(int maxTimeOut, Proxy proxy) {
this.maxTimeOut = maxTimeOut;
- this.client = HttpClient.newBuilder().proxy(proxy).build();
+ this.client = new OkHttpClient.Builder()
+ .proxy(proxy)
+ .writeTimeout(this.maxTimeOut, TimeUnit.MILLISECONDS)
+ .readTimeout(this.maxTimeOut, TimeUnit.MILLISECONDS)
+ .connectTimeout(this.maxTimeOut, TimeUnit.MILLISECONDS)
+ .build();
}
- SmartySender(HttpClient client) {
+ SmartySender(OkHttpClient client) {
this();
this.client = client;
}
public Response send(Request smartyRequest) throws SmartyException, IOException {
- HttpRequest httpRequest = buildHttpRequest(smartyRequest);
+ okhttp3.Request httpRequest = buildHttpRequest(smartyRequest);
- try {
- return buildResponse(this.client.send(httpRequest, HttpResponse.BodyHandlers.ofString()));
- } catch(InterruptedException ex) {
+ try (okhttp3.Response httpResponse = client.newCall(httpRequest).execute()){
+ int statusCode = httpResponse.code();
+ if (statusCode == 429){
+ return new TooManyRequestsResponse(httpResponse.headers(), statusCode, httpResponse.body().bytes());
+ }
+ return new Response(statusCode, httpResponse.body().bytes());
+ } catch(IOException ex) {
return new Response(ex.hashCode(), new byte[0]);
}
}
- private HttpRequest buildHttpRequest(Request smartyRequest) throws IOException {
- java.net.http.HttpRequest.Builder builder = java.net.http.HttpRequest
- .newBuilder(URI.create(smartyRequest.getUrl()))
- .timeout(Duration.ofSeconds(maxTimeOut));
+ private okhttp3.Request buildHttpRequest(Request smartyRequest) throws IOException {
+
Map headers = smartyRequest.getHeaders();
- for (String headerName : headers.keySet())
- builder.setHeader(headerName, headers.get(headerName).toString());
+ Headers.Builder headersBuilder = new Headers.Builder();
+ for (String headerName : headers.keySet()) {
+ headersBuilder.add(headerName, headers.get(headerName).toString());
+ }
- if (smartyRequest.getMethod().equals("GET"))
- return builder.GET().build();
+ okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
+ .url(smartyRequest.getUrl())
+ .headers(headersBuilder.build());
- return builder
- .POST(HttpRequest.BodyPublishers.ofByteArray(smartyRequest.getPayload()))
- .build();
+ if (smartyRequest.getMethod().equals("GET")) {
+ return requestBuilder
+ .get()
+ .build();
+ }
+
+ return requestBuilder.post(RequestBody.create(smartyRequest.getPayload())).build();
}
- private Response buildResponse(HttpResponse httpResponse) {
- int statusCode = httpResponse.statusCode();
+ private Response buildResponse(okhttp3.Response httpResponse) {
+ int statusCode = httpResponse.code();
if (statusCode == 429){
return new TooManyRequestsResponse(httpResponse.headers(), statusCode, httpResponse.body().toString().getBytes());
}
@@ -74,7 +87,7 @@ private Response buildResponse(HttpResponse httpResponse) {
}
static void enableLogging() {
- Logger logger = Logger.getLogger(HttpClient.class.getName());
+ Logger logger = Logger.getLogger(OkHttpClient.class.getName());
logger.setLevel(Level.ALL);
logger.addHandler(new Handler() {
@Override
diff --git a/src/main/java/com/smartystreets/api/TooManyRequestsResponse.java b/src/main/java/com/smartystreets/api/TooManyRequestsResponse.java
index 0cc2d76..49960a9 100644
--- a/src/main/java/com/smartystreets/api/TooManyRequestsResponse.java
+++ b/src/main/java/com/smartystreets/api/TooManyRequestsResponse.java
@@ -1,17 +1,17 @@
package com.smartystreets.api;
-import java.net.http.HttpHeaders;
+import okhttp3.Headers;
public class TooManyRequestsResponse extends Response {
- private HttpHeaders headers;
+ private Headers headers;
- public TooManyRequestsResponse(HttpHeaders headers, int statusCode, byte[] payload) {
+ public TooManyRequestsResponse(Headers headers, int statusCode, byte[] payload) {
super(statusCode, payload);
this.headers = headers;
}
- public HttpHeaders getHeaders() {
+ public Headers getHeaders() {
return headers;
}
}
diff --git a/src/test/java/com/smartystreets/api/SmartySenderTest.java b/src/test/java/com/smartystreets/api/SmartySenderTest.java
index fbc4883..000f960 100644
--- a/src/test/java/com/smartystreets/api/SmartySenderTest.java
+++ b/src/test/java/com/smartystreets/api/SmartySenderTest.java
@@ -1,36 +1,22 @@
package com.smartystreets.api;
+import okhttp3.*;
import org.junit.Test;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLParameters;
-import javax.net.ssl.SSLSession;
import java.io.IOException;
-import java.net.Authenticator;
-import java.net.CookieHandler;
-import java.net.ProxySelector;
-import java.net.URI;
-import java.net.http.HttpClient;
-import java.net.http.HttpHeaders;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.time.Duration;
import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.Executor;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class SmartySenderTest {
- private HttpRequest httpRequest;
-
- //region [ Request Building ]
@Test
public void testHttpRequestContainsCorrectHeaders() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(200));
+ SmartySender sender = new SmartySender(mockHttpClient("{\"key\": \"value\"}", 200));
Request request = new Request();
request.setUrlPrefix("http://localhost");
request.putHeader("X-name1", "value1");
@@ -45,25 +31,27 @@ public void testHttpRequestContainsCorrectHeaders() throws Exception {
@Test
public void testHttpRequestContainsGetWhenAppropriate() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(200));
+ String responseString = "This is a GET response.";
+ SmartySender sender = new SmartySender(mockHttpClient(responseString, 200));
Request request = new Request();
request.setUrlPrefix("http://localhost");
Response response = sender.send(request);
- assertArrayEquals("This is a GET response.".getBytes(), response.getPayload());
+ assertArrayEquals(responseString.getBytes(), response.getPayload());
}
@Test
public void testHttpRequestContainsPostWhenAppropriate() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(200));
+ String responseString = "This is a POST response.";
+ SmartySender sender = new SmartySender(mockHttpClient(responseString, 200));
Request request = new Request();
request.setUrlPrefix("http://localhost");
request.setPayload(new byte[0]);
Response response = sender.send(request);
- assertArrayEquals("This is a POST response.".getBytes(), response.getPayload());
+ assertArrayEquals(responseString.getBytes(), response.getPayload());
}
// @Test
@@ -84,18 +72,20 @@ public void testHttpRequestContainsPostWhenAppropriate() throws Exception {
@Test
public void testResponseContainsCorrectPayload() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(200));
+ String responseBody = "{\"key\": \"value\"}";
+ SmartySender sender = new SmartySender(mockHttpClient(responseBody, 200));
Request request = new Request();
request.setUrlPrefix("http://localhost");
Response response = sender.send(request);
- assertArrayEquals("This is a GET response.".getBytes(), response.getPayload());
+ assertArrayEquals(responseBody.getBytes(), response.getPayload());
}
@Test
public void testResponseContainsStatusCode200OnSuccess() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(200));
+ String responseBody = "{\"key\": \"value\"}";
+ SmartySender sender = new SmartySender(mockHttpClient(responseBody, 200));
Request request = new Request();
request.setUrlPrefix("http://localhost");
@@ -106,7 +96,8 @@ public void testResponseContainsStatusCode200OnSuccess() throws Exception {
@Test
public void testResponseContainsStatusCode400WhenA400IsThrown() throws Exception {
- SmartySender sender = new SmartySender(this.getMockClient(400));
+ String responseBody = "{\"key\": \"value\"}";
+ SmartySender sender = new SmartySender(mockHttpClient(responseBody, 400));
Request request = new Request();
request.setUrlPrefix("http://localhost");
@@ -115,120 +106,24 @@ public void testResponseContainsStatusCode400WhenA400IsThrown() throws Exception
assertEquals(400, response.getStatusCode());
}
- //endregion
+ private static OkHttpClient mockHttpClient(final String serializedBody, final int code) throws IOException {
+ final OkHttpClient okHttpClient = mock(OkHttpClient.class);
- //region [ Transports ]
-
- private HttpClient getMockClient(int statusCode) {
- final int status = statusCode;
- return new HttpClient() {
- @Override
- public Optional cookieHandler() {
- return Optional.empty();
- }
-
- @Override
- public Optional connectTimeout() {
- return Optional.empty();
- }
-
- @Override
- public Redirect followRedirects() {
- return null;
- }
-
- @Override
- public Optional proxy() {
- return Optional.empty();
- }
-
- @Override
- public SSLContext sslContext() {
- return null;
- }
-
- @Override
- public SSLParameters sslParameters() {
- return null;
- }
-
- @Override
- public Optional authenticator() {
- return Optional.empty();
- }
-
- @Override
- public Version version() {
- return null;
- }
-
- @Override
- public Optional executor() {
- return Optional.empty();
- }
-
- @Override
- public HttpResponse send(HttpRequest request, HttpResponse.BodyHandler responseBodyHandler) throws IOException, InterruptedException {
- httpRequest = request;
- return new HttpResponse() {
- @Override
- public int statusCode() {
- return status;
- }
-
- @Override
- public HttpRequest request() {
- return httpRequest;
- }
-
- @Override
- public Optional previousResponse() {
- return Optional.empty();
- }
-
- @Override
- public HttpHeaders headers() {
- return null;
- }
-
- @Override
- public Object body() {
- if (httpRequest.method().equals("GET"))
- return "This is a GET response.";
- else {
- return "This is a POST response.";
-
- }
- }
-
- @Override
- public Optional sslSession() {
- return Optional.empty();
- }
-
- @Override
- public URI uri() {
- return null;
- }
-
- @Override
- public Version version() {
- return null;
- }
- };
- }
-
- @Override
- public CompletableFuture> sendAsync(HttpRequest request, HttpResponse.BodyHandler responseBodyHandler) {
- return null;
- }
-
- @Override
- public CompletableFuture> sendAsync(HttpRequest request, HttpResponse.BodyHandler responseBodyHandler, HttpResponse.PushPromiseHandler pushPromiseHandler) {
- return null;
- }
- };
- }
+ final Call remoteCall = mock(Call.class);
- //endregion
+ final okhttp3.Response response = new okhttp3.Response.Builder()
+ .request(new okhttp3.Request.Builder().url("http://url.com").build())
+ .protocol(Protocol.HTTP_2)
+ .code(code).message("").body(
+ ResponseBody.create(
+ serializedBody,
+ MediaType.parse("application/json")
+ ))
+ .build();
+
+ when(remoteCall.execute()).thenReturn(response);
+ when(okHttpClient.newCall(any())).thenReturn(remoteCall);
+
+ return okHttpClient;
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/smartystreets/api/mocks/MockCrashingSender.java b/src/test/java/com/smartystreets/api/mocks/MockCrashingSender.java
index 0bb859d..eb982be 100644
--- a/src/test/java/com/smartystreets/api/mocks/MockCrashingSender.java
+++ b/src/test/java/com/smartystreets/api/mocks/MockCrashingSender.java
@@ -5,11 +5,9 @@
import com.smartystreets.api.Sender;
import com.smartystreets.api.TooManyRequestsResponse;
import com.smartystreets.api.exceptions.SmartyException;
+import okhttp3.Headers;
import java.io.IOException;
-import java.net.http.HttpHeaders;
-import java.util.List;
-import java.util.Map;
public class MockCrashingSender implements Sender {
private int sendCount = 0;
@@ -23,7 +21,7 @@ public Response send(Request request) throws SmartyException, IOException {
if (request.getUrl().contains("TooManyRequests")) {
if (this.sendCount <= 2) {
- response = new TooManyRequestsResponse(HttpHeaders.of(Map.of("Retry-After", List.of("7")), (x, y) -> true), STATUS_CODE, new byte[]{});
+ response = new TooManyRequestsResponse(Headers.of("Retry-After", "7"), STATUS_CODE, new byte[]{});
}
}