diff --git a/CHANGELOG.md b/CHANGELOG.md index 625bd805..06156295 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ CHANGELOG ------------------ * Added `SECUREPAY` to the `Payment.Processor` enum. +* `WebServiceClient.Builder` now has an `httpClient()` method to allow + passing in a custom `HttpClient`. 3.8.0 (2025-06-09) ------------------ diff --git a/src/main/java/com/maxmind/minfraud/WebServiceClient.java b/src/main/java/com/maxmind/minfraud/WebServiceClient.java index 2df38a50..d5cc47f7 100644 --- a/src/main/java/com/maxmind/minfraud/WebServiceClient.java +++ b/src/main/java/com/maxmind/minfraud/WebServiceClient.java @@ -64,13 +64,16 @@ private WebServiceClient(WebServiceClient.Builder builder) { .getBytes(StandardCharsets.UTF_8)); requestTimeout = builder.requestTimeout; - HttpClient.Builder httpClientBuilder = HttpClient.newBuilder() - .proxy(builder.proxy); - if (builder.connectTimeout != null) { - httpClientBuilder.connectTimeout(builder.connectTimeout); + if (builder.httpClient != null) { + httpClient = builder.httpClient; + } else { + HttpClient.Builder httpClientBuilder = HttpClient.newBuilder() + .proxy(builder.proxy); + if (builder.connectTimeout != null) { + httpClientBuilder.connectTimeout(builder.connectTimeout); + } + httpClient = httpClientBuilder.build(); } - httpClient = httpClientBuilder.build(); - } /** @@ -102,7 +105,8 @@ public static final class Builder { Duration requestTimeout; List locales = Collections.singletonList("en"); - private ProxySelector proxy = ProxySelector.getDefault(); + private ProxySelector proxy; + private HttpClient httpClient; /** * @param accountId Your MaxMind account ID. @@ -188,12 +192,38 @@ public Builder proxy(ProxySelector val) { return this; } + /** + * @param val the HttpClient to use when making requests. When provided, + * connectTimeout and proxy settings will be ignored as the + * custom client should handle these configurations. + * @return Builder object + */ + public Builder httpClient(HttpClient val) { + httpClient = val; + return this; + } /** * @return an instance of {@code WebServiceClient} created from the fields set on this * builder. */ public WebServiceClient build() { + if (httpClient != null) { + if (connectTimeout != null) { + throw new IllegalArgumentException( + "Cannot set both httpClient and connectTimeout. " + + "Configure timeouts on the provided HttpClient instead."); + } + if (proxy != null) { + throw new IllegalArgumentException( + "Cannot set both httpClient and proxy. " + + "Configure proxy on the provided HttpClient instead."); + } + } else { + if (proxy == null) { + proxy = ProxySelector.getDefault(); + } + } return new WebServiceClient(this); } } diff --git a/src/test/java/com/maxmind/minfraud/WebServiceClientTest.java b/src/test/java/com/maxmind/minfraud/WebServiceClientTest.java index c05fb360..7de5b025 100644 --- a/src/test/java/com/maxmind/minfraud/WebServiceClientTest.java +++ b/src/test/java/com/maxmind/minfraud/WebServiceClientTest.java @@ -38,6 +38,9 @@ import com.maxmind.minfraud.response.IpRiskReason; import com.maxmind.minfraud.response.ScoreResponse; import java.net.InetAddress; +import java.net.ProxySelector; +import java.net.http.HttpClient; +import java.time.Duration; import java.util.List; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; @@ -357,4 +360,46 @@ private WebServiceClient createClient(String service, int status, String content .disableHttps() .build(); } + + @Test + public void testHttpClientWorks() { + HttpClient customClient = HttpClient.newBuilder().build(); + + WebServiceClient client = new WebServiceClient.Builder(6, "0123456789") + .httpClient(customClient) + .build(); + + // Verify the client was created successfully + assertEquals("WebServiceClient{host='minfraud.maxmind.com', port=443, useHttps=true, locales=[en], httpClient=" + customClient + "}", client.toString()); + } + + @Test + public void testHttpClientWithConnectTimeoutThrowsException() { + HttpClient customClient = HttpClient.newBuilder().build(); + + Exception ex = assertThrows(IllegalArgumentException.class, () -> + new WebServiceClient.Builder(6, "0123456789") + .httpClient(customClient) + .connectTimeout(Duration.ofSeconds(5)) + .build() + ); + + assertEquals("Cannot set both httpClient and connectTimeout. " + + "Configure timeouts on the provided HttpClient instead.", ex.getMessage()); + } + + @Test + public void testHttpClientWithProxyThrowsException() { + HttpClient customClient = HttpClient.newBuilder().build(); + + Exception ex = assertThrows(IllegalArgumentException.class, () -> + new WebServiceClient.Builder(6, "0123456789") + .httpClient(customClient) + .proxy(java.net.ProxySelector.of(null)) + .build() + ); + + assertEquals("Cannot set both httpClient and proxy. " + + "Configure proxy on the provided HttpClient instead.", ex.getMessage()); + } }