diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ExchangeIdGenerator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ExchangeIdGenerator.java new file mode 100644 index 0000000000..fcc48ec666 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ExchangeIdGenerator.java @@ -0,0 +1,49 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.client5.http; + +import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.ThreadingBehavior; + +/** + * Generates exchange identifiers for message exchanges. + * + * @since 5.7 + */ +@Contract(threading = ThreadingBehavior.STATELESS) +public interface ExchangeIdGenerator { + + /** + * Generates the next exchange identifier. + *

+ * The returned identifier must be unique, non-null, and non-blank. + *

+ * + * @return the next exchange identifier + */ + String getNextExchangeId(); +} \ No newline at end of file diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExechangeIdGenerator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExechangeIdGenerator.java new file mode 100644 index 0000000000..35b60ea413 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExechangeIdGenerator.java @@ -0,0 +1,47 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.client5.http.impl; + +import org.apache.hc.client5.http.ExchangeIdGenerator; +import org.apache.hc.core5.annotation.Contract; +import org.apache.hc.core5.annotation.ThreadingBehavior; + +/** + * The default implementation of {@link ExchangeIdGenerator}. + * + * @since 5.7 + */ +@Contract(threading = ThreadingBehavior.STATELESS) +public class DefaultExechangeIdGenerator implements ExchangeIdGenerator { + + public static final ExchangeIdGenerator INSTANCE = new DefaultExechangeIdGenerator(); + + @Override + public String getNextExchangeId() { + return ExecSupport.getNextExchangeId(); + } +} diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java index f5cfc4de08..e9c213e977 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java @@ -37,6 +37,7 @@ import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.DnsResolver; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.async.AsyncExecChainHandler; @@ -51,6 +52,7 @@ import org.apache.hc.client5.http.impl.ChainElement; import org.apache.hc.client5.http.impl.CookieSpecSupport; import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; +import org.apache.hc.client5.http.impl.DefaultExechangeIdGenerator; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; @@ -182,6 +184,7 @@ private ExecInterceptorEntry( private LinkedList responseInterceptors; private LinkedList execInterceptors; + private ExchangeIdGenerator exchangeIdGenerator; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; @@ -563,6 +566,17 @@ public final H2AsyncClientBuilder setDefaultHeaders(final Collection responseInterceptors; private LinkedList execInterceptors; + private ExchangeIdGenerator exchangeIdGenerator; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; @@ -695,6 +698,17 @@ public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) { return this; } + /** + * Sets {@link ExchangeIdGenerator} instance. + * + * @return this instance. + * @since 5.7 + */ + public final HttpAsyncClientBuilder setExchangeIdGenerator(final ExchangeIdGenerator exchangeIdGenerator) { + this.exchangeIdGenerator = exchangeIdGenerator; + return this; + } + /** * Sets {@link HttpRoutePlanner} instance. * @@ -1140,6 +1154,10 @@ public CloseableHttpAsyncClient build() { execChainDefinition.addFirst(new TlsRequiredAsyncExec(), ChainElement.TLS_REQUIRED.name()); } + ExchangeIdGenerator exchangeIdGeneratorCopy = this.exchangeIdGenerator; + if (exchangeIdGeneratorCopy == null) { + exchangeIdGeneratorCopy = DefaultExechangeIdGenerator.INSTANCE; + } HttpRoutePlanner routePlannerCopy = this.routePlanner; if (routePlannerCopy == null) { @@ -1277,6 +1295,7 @@ public CloseableHttpAsyncClient build() { pushConsumerRegistry, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true), connManagerCopy, + exchangeIdGeneratorCopy, routePlannerCopy, tlsConfig, cookieSpecRegistryCopy, diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java index 6c1679604f..5527a298e0 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalAbstractHttpAsyncClient.java @@ -40,6 +40,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecCallback; import org.apache.hc.client5.http.async.AsyncExecChain; @@ -50,7 +51,6 @@ import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.RoutingSupport; import org.apache.hc.core5.concurrent.Cancellable; @@ -88,6 +88,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa private static final Logger LOG = LoggerFactory.getLogger(InternalAbstractHttpAsyncClient.class); private final AsyncExecChainElement execChain; + private final ExchangeIdGenerator exchangeIdGenerator; private final Lookup cookieSpecRegistry; private final Lookup authSchemeRegistry; private final CookieStore cookieStore; @@ -103,6 +104,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final AsyncExecChainElement execChain, + final ExchangeIdGenerator exchangeIdGenerator, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, final CookieStore cookieStore, @@ -112,6 +114,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa final List closeables) { super(ioReactor, pushConsumerRegistry, threadFactory); this.execChain = execChain; + this.exchangeIdGenerator = exchangeIdGenerator; this.cookieSpecRegistry = cookieSpecRegistry; this.authSchemeRegistry = authSchemeRegistry; this.cookieStore = cookieStore; @@ -232,7 +235,7 @@ protected Future doExecute( resolvedTarget, request, clientContext); - final String exchangeId = ExecSupport.getNextExchangeId(); + final String exchangeId = exchangeIdGenerator.getNextExchangeId(); clientContext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} preparing request execution", exchangeId); diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncClient.java index 3c27051000..1093dc22c9 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalH2AsyncClient.java @@ -31,6 +31,7 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthSchemeFactory; @@ -79,6 +80,7 @@ public final class InternalH2AsyncClient extends InternalAbstractHttpAsyncClient final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final InternalH2ConnPool connPool, + final ExchangeIdGenerator exchangeIdGenerator, final HttpRoutePlanner routePlanner, final Lookup cookieSpecRegistry, final Lookup authSchemeRegistry, @@ -87,7 +89,7 @@ public final class InternalH2AsyncClient extends InternalAbstractHttpAsyncClient final RequestConfig defaultConfig, final List closeables, final int maxQueuedRequests) { - super(ioReactor, pushConsumerRegistry, threadFactory, execChain, + super(ioReactor, pushConsumerRegistry, threadFactory, execChain, exchangeIdGenerator, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, HttpClientContext::castOrCreate, defaultConfig, closeables); this.connPool = connPool; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java index 1604039f30..2962bc1f28 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/InternalHttpAsyncClient.java @@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.async.AsyncExecRuntime; import org.apache.hc.client5.http.auth.AuthSchemeFactory; @@ -85,6 +86,7 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final AsyncClientConnectionManager manager, + final ExchangeIdGenerator exchangeIdGenerator, final HttpRoutePlanner routePlanner, final TlsConfig tlsConfig, final Lookup cookieSpecRegistry, @@ -95,7 +97,7 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie final RequestConfig defaultConfig, final List closeables, final int maxQueuedRequests) { - super(ioReactor, pushConsumerRegistry, threadFactory, execChain, + super(ioReactor, pushConsumerRegistry, threadFactory, execChain, exchangeIdGenerator, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, contextAdaptor, defaultConfig, closeables); this.manager = manager; diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java index 671b7e5de7..ef130620a8 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java @@ -41,6 +41,7 @@ import org.apache.hc.client5.http.AuthenticationStrategy; import org.apache.hc.client5.http.ConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRequestRetryStrategy; import org.apache.hc.client5.http.SchemePortResolver; import org.apache.hc.client5.http.UserTokenHandler; @@ -61,6 +62,7 @@ import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy; import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy; import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy; +import org.apache.hc.client5.http.impl.DefaultExechangeIdGenerator; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; @@ -194,6 +196,7 @@ private ExecInterceptorEntry( } private HttpRequestExecutor requestExec; + private ExchangeIdGenerator exchangeIdGenerator; private HttpClientConnectionManager connManager; private boolean connManagerShared; private SchemePortResolver schemePortResolver; @@ -257,6 +260,17 @@ public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requ return this; } + /** + * Sets {@link ExchangeIdGenerator} instance. + * + * @return this instance. + * @since 5.7 + */ + public final HttpClientBuilder setExchangeIdGenerator(final ExchangeIdGenerator exchangeIdGenerator) { + this.exchangeIdGenerator = exchangeIdGenerator; + return this; + } + /** * Sets {@link HttpClientConnectionManager} instance. * @@ -865,6 +879,10 @@ public CloseableHttpClient build() { if (requestExecCopy == null) { requestExecCopy = new HttpRequestExecutor(); } + ExchangeIdGenerator exchangeIdGeneratorCopy = this.exchangeIdGenerator; + if (exchangeIdGeneratorCopy == null) { + exchangeIdGeneratorCopy = DefaultExechangeIdGenerator.INSTANCE; + } HttpClientConnectionManager connManagerCopy = this.connManager; if (connManagerCopy == null) { final PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder.create(); @@ -1139,6 +1157,7 @@ public CloseableHttpClient build() { return new InternalHttpClient( connManagerCopy, requestExecCopy, + exchangeIdGeneratorCopy, execChain, routePlannerCopy, cookieSpecRegistryCopy, diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalHttpClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalHttpClient.java index 07265fcb4b..c4674d362f 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalHttpClient.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/InternalHttpClient.java @@ -34,6 +34,7 @@ import java.util.function.Function; import org.apache.hc.client5.http.ClientProtocolException; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; @@ -43,7 +44,6 @@ import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; -import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; @@ -85,6 +85,7 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { private final HttpClientConnectionManager connManager; private final HttpRequestExecutor requestExecutor; + private final ExchangeIdGenerator exchangeIdGenerator; private final ExecChainElement execChain; private final HttpRoutePlanner routePlanner; private final Lookup cookieSpecRegistry; @@ -98,6 +99,7 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { public InternalHttpClient( final HttpClientConnectionManager connManager, final HttpRequestExecutor requestExecutor, + final ExchangeIdGenerator exchangeIdGenerator, final ExecChainElement execChain, final HttpRoutePlanner routePlanner, final Lookup cookieSpecRegistry, @@ -110,6 +112,7 @@ public InternalHttpClient( super(); this.connManager = Args.notNull(connManager, "Connection manager"); this.requestExecutor = Args.notNull(requestExecutor, "Request executor"); + this.exchangeIdGenerator = Args.notNull(exchangeIdGenerator, "Exchange id generator"); this.execChain = Args.notNull(execChain, "Execution chain"); this.routePlanner = Args.notNull(routePlanner, "Route planner"); this.cookieSpecRegistry = cookieSpecRegistry; @@ -173,7 +176,7 @@ protected CloseableHttpResponse doExecute( resolvedTarget, request, localcontext); - final String exchangeId = ExecSupport.getNextExchangeId(); + final String exchangeId = exchangeIdGenerator.getNextExchangeId(); localcontext.setExchangeId(exchangeId); if (LOG.isDebugEnabled()) { LOG.debug("{} preparing request execution", exchangeId); diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java index bbe7292dd2..2518d39efb 100644 --- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java +++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestInternalHttpClient.java @@ -31,6 +31,7 @@ import java.util.Arrays; import org.apache.hc.client5.http.ClientProtocolException; +import org.apache.hc.client5.http.ExchangeIdGenerator; import org.apache.hc.client5.http.HttpRoute; import org.apache.hc.client5.http.auth.AuthSchemeFactory; import org.apache.hc.client5.http.auth.CredentialsProvider; @@ -39,6 +40,7 @@ import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.cookie.CookieSpecFactory; import org.apache.hc.client5.http.cookie.CookieStore; +import org.apache.hc.client5.http.impl.ExecSupport; import org.apache.hc.client5.http.io.HttpClientConnectionManager; import org.apache.hc.client5.http.protocol.HttpClientContext; import org.apache.hc.client5.http.routing.HttpRoutePlanner; @@ -69,6 +71,8 @@ class TestInternalHttpClient { @Mock private ExecChainHandler execChain; @Mock + private ExchangeIdGenerator exchangeIdGenerator; + @Mock private HttpRoutePlanner routePlanner; @Mock private Lookup cookieSpecRegistry; @@ -90,10 +94,10 @@ class TestInternalHttpClient { @BeforeEach void setup() { MockitoAnnotations.openMocks(this); - client = new InternalHttpClient(connManager, requestExecutor, new ExecChainElement(execChain, null), routePlanner, + client = new InternalHttpClient(connManager, requestExecutor, exchangeIdGenerator, + new ExecChainElement(execChain, null), routePlanner, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, HttpClientContext::castOrCreate, defaultConfig, Arrays.asList(closeable1, closeable2)); - } @Test @@ -101,6 +105,7 @@ void testExecute() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any(), @@ -122,6 +127,7 @@ void testExecuteHttpException() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any(), @@ -143,6 +149,7 @@ void testExecuteDefaultContext() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any(), @@ -166,6 +173,7 @@ void testExecuteRequestConfig() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any(), @@ -187,6 +195,7 @@ void testExecuteLocalContext() throws Exception { final HttpGet httpget = new HttpGet("http://somehost/stuff"); final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(new HttpHost("somehost")), Mockito.any(), @@ -240,6 +249,7 @@ void testClientCloseIOException() throws Exception { void testDoExecuteThrowsWhenNoTargetOrHost() throws Exception { final ClassicHttpRequest request = ClassicRequestBuilder.get("/foo").build(); final HttpClientContext context = HttpClientContext.create(); + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); Mockito.when(routePlanner.determineRoute( Mockito.eq(null), Mockito.any(), @@ -247,4 +257,22 @@ void testDoExecuteThrowsWhenNoTargetOrHost() throws Exception { Assertions.assertThrows(ClientProtocolException.class, () -> client.executeOpen(null, request, context)); } + + @Test + void testExchangeIdGenerator() throws Exception { + final HttpGet httpget = new HttpGet("http://somehost/stuff"); + final HttpRoute route = new HttpRoute(new HttpHost("somehost", 80)); + + Mockito.when(exchangeIdGenerator.getNextExchangeId()).thenReturn(ExecSupport.getNextExchangeId()); + Mockito.when(routePlanner.determineRoute( + Mockito.eq(new HttpHost("somehost")), + Mockito.any(), + Mockito.any())).thenReturn(route); + Mockito.when(execChain.execute( + Mockito.any(), Mockito.any(), Mockito.any())).thenReturn( + CloseableHttpResponse.adapt(new BasicClassicHttpResponse(200))); + + client.execute(httpget, response -> null); + Mockito.verify(exchangeIdGenerator).getNextExchangeId(); + } }