From 40ef327f5a96d792bf9ba35b00f80046aeb6c4c6 Mon Sep 17 00:00:00 2001 From: daniel-rohe_zse Date: Fri, 7 Aug 2026 15:02:10 +0200 Subject: [PATCH 1/3] possibility to inject connection manager --- .../com/adyen/httpclient/AdyenHttpClient.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/adyen/httpclient/AdyenHttpClient.java b/src/main/java/com/adyen/httpclient/AdyenHttpClient.java index 301feb2d8..56027f7fb 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,18 @@ public class AdyenHttpClient implements ClientInterface { private static final String CHARSET = "UTF-8"; private Proxy proxy; + private PoolingHttpClientConnectionManager sharedConnectionManager; private volatile CloseableHttpClient sharedHttpClient; private final Object lock = new Object(); + public AdyenHttpClient() { + super(); + } + + public AdyenHttpClient(PoolingHttpClientConnectionManager connectionManager) { + this.sharedConnectionManager = connectionManager; + } + /** * Returns the proxy configured for this HTTP client. * @@ -372,7 +383,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 +397,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(); From 7f1e3bdba4e7a7cabce9fc20aac26bf5a1ea705d Mon Sep 17 00:00:00 2001 From: daniel-rohe_zse Date: Fri, 7 Aug 2026 15:17:14 +0200 Subject: [PATCH 2/3] added review findings --- .../com/adyen/httpclient/AdyenHttpClient.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/adyen/httpclient/AdyenHttpClient.java b/src/main/java/com/adyen/httpclient/AdyenHttpClient.java index 56027f7fb..7572d61d1 100644 --- a/src/main/java/com/adyen/httpclient/AdyenHttpClient.java +++ b/src/main/java/com/adyen/httpclient/AdyenHttpClient.java @@ -93,14 +93,29 @@ public class AdyenHttpClient implements ClientInterface { private static final String CHARSET = "UTF-8"; private Proxy proxy; - private PoolingHttpClientConnectionManager sharedConnectionManager; + private final PoolingHttpClientConnectionManager sharedConnectionManager; private volatile CloseableHttpClient sharedHttpClient; private final Object lock = new Object(); public AdyenHttpClient() { - super(); + this(null); } + /** + * Creates an AdyenHttpClient with a shared connection manager. + *

+ * Note: When using a shared connection manager: + *

+ *

+ * + * @param connectionManager the shared connection manager to use + */ public AdyenHttpClient(PoolingHttpClientConnectionManager connectionManager) { this.sharedConnectionManager = connectionManager; } From 12e109242b61a81d4e53f68e99dce22061ec4c36 Mon Sep 17 00:00:00 2001 From: daniel-rohe_zse Date: Fri, 4 Sep 2026 16:33:10 +0200 Subject: [PATCH 3/3] enhanced test cases for shared connection manager --- .../java/com/adyen/httpclient/ClientTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) 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();