Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
CHANGELOG
=========

4.4.0
------------------

* `WebServiceClient.Builder` now has an `httpClient()` method to allow
passing in a custom `HttpClient`.

4.3.1 (2025-05-28)
------------------

Expand Down
50 changes: 44 additions & 6 deletions src/main/java/com/maxmind/geoip2/WebServiceClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,15 @@ private WebServiceClient(Builder builder) {
.build();

requestTimeout = builder.requestTimeout;
httpClient = HttpClient.newBuilder()
.connectTimeout(builder.connectTimeout)
.proxy(builder.proxy)
.build();

if (builder.httpClient != null) {
httpClient = builder.httpClient;
} else {
httpClient = HttpClient.newBuilder()
.connectTimeout(builder.connectTimeout)
.proxy(builder.proxy)
.build();
}
}

/**
Expand Down Expand Up @@ -171,11 +176,12 @@ public static final class Builder {
int port = 443;
boolean useHttps = true;

Duration connectTimeout = Duration.ofSeconds(3);
Duration connectTimeout = null;
Duration requestTimeout = Duration.ofSeconds(20);

List<String> locales = Collections.singletonList("en");
private ProxySelector proxy = ProxySelector.getDefault();
private ProxySelector proxy = null;
private HttpClient httpClient = null;

/**
* @param accountId Your MaxMind account ID.
Expand Down Expand Up @@ -296,11 +302,43 @@ public Builder proxy(ProxySelector val) {
return this;
}

/**
* @param val the custom HttpClient to use for requests. When providing a
* custom HttpClient, you cannot also set connectTimeout or proxy
* parameters as these should be configured on the provided client.
* @return Builder object
*/
public Builder httpClient(HttpClient val) {
this.httpClient = val;
return this;
}

/**
* @return an instance of {@code WebServiceClient} created from the
* fields set on this builder.
* @throws IllegalArgumentException if httpClient is provided along with
* connectTimeout or proxy settings
*/
public WebServiceClient build() {
if (httpClient != null) {
if (connectTimeout != null) {
throw new IllegalArgumentException(
"Cannot set both httpClient and connectTimeout. "
+ "Configure timeout 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 (connectTimeout == null) {
connectTimeout = Duration.ofSeconds(3);
}
if (proxy == null) {
proxy = ProxySelector.getDefault();
}
}
return new WebServiceClient(this);
}
}
Expand Down
53 changes: 53 additions & 0 deletions src/test/java/com/maxmind/geoip2/WebServiceClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
import com.maxmind.geoip2.record.Traits;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.http.HttpClient;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.List;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -407,4 +411,53 @@ private WebServiceClient createClient(String service, String ip, int status, Str
.disableHttps()
.build();
}

@Test
public void testHttpClientWithConnectTimeoutThrowsException() {
HttpClient customClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

WebServiceClient.Builder builder = new WebServiceClient.Builder(6, "0123456789")
.httpClient(customClient)
.connectTimeout(Duration.ofSeconds(5));

IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("Cannot set both httpClient and connectTimeout. Configure timeout on the provided HttpClient instead.",
ex.getMessage());
}

@Test
public void testHttpClientWithProxyThrowsException() {
HttpClient customClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

ProxySelector proxySelector = ProxySelector.of(new InetSocketAddress("proxy.example.com", 8080));
WebServiceClient.Builder builder = new WebServiceClient.Builder(6, "0123456789")
.httpClient(customClient)
.proxy(proxySelector);

IllegalArgumentException ex = assertThrows(IllegalArgumentException.class, builder::build);
assertEquals("Cannot set both httpClient and proxy. Configure proxy on the provided HttpClient instead.",
ex.getMessage());
}

@Test
public void testHttpClientWithDefaultSettingsDoesNotThrow() throws Exception {
HttpClient customClient = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();

// Should not throw because we're not setting connectTimeout or proxy
WebServiceClient client = new WebServiceClient.Builder(6, "0123456789")
.host("localhost")
.port(8080)
.disableHttps()
.httpClient(customClient)
.build();

assertNotNull(client);
}

}
Loading