diff --git a/src/main/java/com/adyen/httpclient/AdyenHttpClient.java b/src/main/java/com/adyen/httpclient/AdyenHttpClient.java
index 301feb2d8..7572d61d1 100644
--- a/src/main/java/com/adyen/httpclient/AdyenHttpClient.java
+++ b/src/main/java/com/adyen/httpclient/AdyenHttpClient.java
@@ -56,7 +56,9 @@
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.http.HttpHost;
@@ -91,9 +93,33 @@ public class AdyenHttpClient implements ClientInterface {
private static final String CHARSET = "UTF-8";
private Proxy proxy;
+ private final PoolingHttpClientConnectionManager sharedConnectionManager;
private volatile CloseableHttpClient sharedHttpClient;
private final Object lock = new Object();
+ public AdyenHttpClient() {
+ this(null);
+ }
+
+ /**
+ * Creates an AdyenHttpClient with a shared connection manager.
+ *
+ * Note: When using a shared connection manager:
+ *
+ * - Any custom {@link SSLContext} or {@link HostnameVerifier} configured in {@link Config}
+ * will be ignored, as the connection manager's own socket factory registry is used.
+ * - Connection-level configurations (such as {@code connectTimeout} and {@code socketTimeout})
+ * must be pre-configured on the shared connection manager, as the timeouts from {@link Config}
+ * will only be applied at the request level (via {@link RequestConfig}).
+ *
+ *
+ *
+ * @param connectionManager the shared connection manager to use
+ */
+ public AdyenHttpClient(PoolingHttpClientConnectionManager connectionManager) {
+ this.sharedConnectionManager = connectionManager;
+ }
+
/**
* Returns the proxy configured for this HTTP client.
*
@@ -372,7 +398,12 @@ private CloseableHttpClient createHttpClientWithSocketFactory(
config.getConnectionRequestTimeoutMillis(), TimeUnit.MILLISECONDS)
.setDefaultKeepAlive(config.getDefaultKeepAliveMillis(), TimeUnit.MILLISECONDS)
.build();
- ConnectionConfig connectionConfig =
+
+ HttpClientBuilder clientBuilder = HttpClients.custom();
+ if (sharedConnectionManager != null) {
+ clientBuilder = clientBuilder.setConnectionManager(sharedConnectionManager).setConnectionManagerShared(true);
+ } else {
+ ConnectionConfig connectionConfig =
ConnectionConfig.custom()
.setConnectTimeout(config.getConnectionTimeoutMillis(), TimeUnit.MILLISECONDS)
// socketTimeout acts as an OS-level safety net for stalled reads;
@@ -381,12 +412,14 @@ private CloseableHttpClient createHttpClientWithSocketFactory(
// fires first.
.setSocketTimeout(config.getReadTimeoutMillis(), TimeUnit.MILLISECONDS)
.build();
- return HttpClients.custom()
- .setConnectionManager(
+ clientBuilder = clientBuilder.setConnectionManager(
PoolingHttpClientConnectionManagerBuilder.create()
.setSSLSocketFactory(socketFactory)
.setDefaultConnectionConfig(connectionConfig)
- .build())
+ .build());
+ }
+
+ return clientBuilder
.setDefaultRequestConfig(defaultRequestConfig)
.setRedirectStrategy(new AdyenCustomRedirectStrategy())
.build();
diff --git a/src/test/java/com/adyen/httpclient/ClientTest.java b/src/test/java/com/adyen/httpclient/ClientTest.java
index 919ff9aad..a2c164842 100644
--- a/src/test/java/com/adyen/httpclient/ClientTest.java
+++ b/src/test/java/com/adyen/httpclient/ClientTest.java
@@ -1,6 +1,9 @@
package com.adyen.httpclient;
import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import com.adyen.BaseTest;
import com.adyen.Client;
@@ -17,6 +20,7 @@
import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.core5.http.Header;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
@@ -177,6 +181,39 @@ public void testDefaultRequestConfigTimeoutsWithConfigDefaults() throws Exceptio
}
}
+ @Test
+ public void testInjectedConnectionManagerAppliesDefaultRequestConfig() throws Exception {
+ PoolingHttpClientConnectionManager connectionManager =
+ mock(PoolingHttpClientConnectionManager.class);
+ Config config = new Config();
+ config.setReadTimeoutMillis(15000);
+ config.setConnectionRequestTimeoutMillis(30000);
+ config.setDefaultKeepAliveMillis(40000);
+
+ try (AdyenHttpClient adyenHttpClient = new AdyenHttpClient(connectionManager);
+ CloseableHttpClient httpClient = adyenHttpClient.createCloseableHttpClient(config)) {
+ assertInstanceOf(Configurable.class, httpClient);
+ RequestConfig defaultConfig = ((Configurable) httpClient).getConfig();
+ assertNotNull(defaultConfig);
+ assertEquals(15000, defaultConfig.getResponseTimeout().toMilliseconds());
+ assertEquals(30000, defaultConfig.getConnectionRequestTimeout().toMilliseconds());
+ }
+ }
+
+ @Test
+ public void testClosingHttpClientDoesNotCloseInjectedConnectionManager() throws Exception {
+ PoolingHttpClientConnectionManager connectionManager =
+ mock(PoolingHttpClientConnectionManager.class);
+
+ try (AdyenHttpClient adyenHttpClient = new AdyenHttpClient(connectionManager);
+ CloseableHttpClient httpClient = adyenHttpClient.createCloseableHttpClient(new Config())) {
+ assertNotNull(httpClient);
+ adyenHttpClient.close();
+ }
+
+ verify(connectionManager, never()).close();
+ }
+
@Test
public void testPerRequestConfigIncludesAllTimeouts() throws Exception {
AdyenHttpClient adyenHttpClient = new AdyenHttpClient();