From 47358dbbbd763b71c32cce3aaca733c55236abe1 Mon Sep 17 00:00:00 2001 From: yhzdys Date: Sat, 17 Jan 2026 20:09:23 +0800 Subject: [PATCH] Add custom ExchangeId generator support --- .../http/impl/DefaultExchangeIdGenerator.java | 47 +++++++++++++++++++ .../http/impl/async/H2AsyncClientBuilder.java | 20 ++++++++ .../impl/async/HttpAsyncClientBuilder.java | 19 ++++++++ .../InternalAbstractHttpAsyncClient.java | 7 ++- .../impl/async/InternalH2AsyncClient.java | 4 +- .../impl/async/InternalHttpAsyncClient.java | 4 +- .../http/impl/classic/HttpClientBuilder.java | 19 ++++++++ .../http/impl/classic/InternalHttpClient.java | 7 ++- .../impl/classic/TestInternalHttpClient.java | 32 ++++++++++++- 9 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExchangeIdGenerator.java diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExchangeIdGenerator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExchangeIdGenerator.java new file mode 100644 index 0000000000..eb2e166972 --- /dev/null +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultExchangeIdGenerator.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.core5.annotation.Contract; +import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.function.Supplier; + +/** + * Default implementation of {@link Supplier} for generating exchange IDs. + * + * @since 5.7 + */ +@Contract(threading = ThreadingBehavior.STATELESS) +public class DefaultExchangeIdGenerator implements Supplier { + + public static final Supplier INSTANCE = new DefaultExchangeIdGenerator(); + + @Override + public String get() { + 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..d280997901 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 @@ -51,6 +51,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.DefaultExchangeIdGenerator; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; @@ -76,6 +77,7 @@ import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; import org.apache.hc.core5.function.Resolver; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequestInterceptor; @@ -182,6 +184,7 @@ private ExecInterceptorEntry( private LinkedList responseInterceptors; private LinkedList execInterceptors; + private Supplier exchangeIdGenerator; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; @@ -563,6 +566,17 @@ public final H2AsyncClientBuilder setDefaultHeaders(final Collection exchangeIdGenerator) { + this.exchangeIdGenerator = exchangeIdGenerator; + return this; + } + /** * Sets {@link HttpRoutePlanner} instance. * @@ -855,6 +869,11 @@ public CloseableHttpAsyncClient build() { ChainElement.RETRY.name()); } + Supplier exchangeIdGeneratorCopy = this.exchangeIdGenerator; + if (exchangeIdGeneratorCopy == null) { + exchangeIdGeneratorCopy = DefaultExchangeIdGenerator.INSTANCE; + } + HttpRoutePlanner routePlannerCopy = this.routePlanner; if (routePlannerCopy == null) { SchemePortResolver schemePortResolverCopy = this.schemePortResolver; @@ -983,6 +1002,7 @@ public CloseableHttpAsyncClient build() { pushConsumerRegistry, threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true), connPool, + exchangeIdGeneratorCopy, routePlannerCopy, cookieSpecRegistryCopy, authSchemeRegistryCopy, diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java index 1c39b2c314..64271ccd15 100644 --- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java +++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java @@ -59,6 +59,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.DefaultExchangeIdGenerator; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; @@ -91,6 +92,7 @@ import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.function.Callback; import org.apache.hc.core5.function.Decorator; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpHost; @@ -233,6 +235,7 @@ private ExecInterceptorEntry( private LinkedList responseInterceptors; private LinkedList execInterceptors; + private Supplier exchangeIdGenerator; private HttpRoutePlanner routePlanner; private RedirectStrategy redirectStrategy; private HttpRequestRetryStrategy retryStrategy; @@ -695,6 +698,17 @@ public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) { return this; } + /** + * Sets exchange ID generator instance. + * + * @return this instance. + * @since 5.7 + */ + public final HttpAsyncClientBuilder setExchangeIdGenerator(final Supplier 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()); } + Supplier exchangeIdGeneratorCopy = this.exchangeIdGenerator; + if (exchangeIdGeneratorCopy == null) { + exchangeIdGeneratorCopy = DefaultExchangeIdGenerator.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..9a81c7d944 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 @@ -50,13 +50,13 @@ 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; import org.apache.hc.core5.concurrent.ComplexFuture; import org.apache.hc.core5.concurrent.DefaultThreadFactory; import org.apache.hc.core5.concurrent.FutureCallback; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.EntityDetails; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; @@ -88,6 +88,7 @@ abstract class InternalAbstractHttpAsyncClient extends AbstractHttpAsyncClientBa private static final Logger LOG = LoggerFactory.getLogger(InternalAbstractHttpAsyncClient.class); private final AsyncExecChainElement execChain; + private final Supplier 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 Supplier 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.get(); 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..4d4c056124 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 @@ -43,6 +43,7 @@ import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; @@ -79,6 +80,7 @@ public final class InternalH2AsyncClient extends InternalAbstractHttpAsyncClient final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final InternalH2ConnPool connPool, + final Supplier 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..09d20f0810 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 @@ -46,6 +46,7 @@ import org.apache.hc.core5.annotation.Contract; import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; import org.apache.hc.core5.http.HttpRequest; @@ -85,6 +86,7 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie final AsyncPushConsumerRegistry pushConsumerRegistry, final ThreadFactory threadFactory, final AsyncClientConnectionManager manager, + final Supplier 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..8a15b3774a 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 @@ -61,6 +61,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.DefaultExchangeIdGenerator; import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy; import org.apache.hc.client5.http.impl.DefaultRedirectStrategy; import org.apache.hc.client5.http.impl.DefaultSchemePortResolver; @@ -89,6 +90,7 @@ import org.apache.hc.client5.http.protocol.ResponseProcessCookies; import org.apache.hc.client5.http.routing.HttpRoutePlanner; import org.apache.hc.core5.annotation.Internal; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ConnectionReuseStrategy; import org.apache.hc.core5.http.Header; import org.apache.hc.core5.http.HttpEntity; @@ -194,6 +196,7 @@ private ExecInterceptorEntry( } private HttpRequestExecutor requestExec; + private Supplier exchangeIdGenerator; private HttpClientConnectionManager connManager; private boolean connManagerShared; private SchemePortResolver schemePortResolver; @@ -257,6 +260,17 @@ public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requ return this; } + /** + * Sets exchange ID generator instance. + * + * @return this instance. + * @since 5.7 + */ + public final HttpClientBuilder setExchangeIdGenerator(final Supplier exchangeIdGenerator) { + this.exchangeIdGenerator = exchangeIdGenerator; + return this; + } + /** * Sets {@link HttpClientConnectionManager} instance. * @@ -865,6 +879,10 @@ public CloseableHttpClient build() { if (requestExecCopy == null) { requestExecCopy = new HttpRequestExecutor(); } + Supplier exchangeIdGeneratorCopy = this.exchangeIdGenerator; + if (exchangeIdGeneratorCopy == null) { + exchangeIdGeneratorCopy = DefaultExchangeIdGenerator.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..31c8dedf87 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 @@ -43,7 +43,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; @@ -52,6 +51,7 @@ import org.apache.hc.core5.annotation.Internal; import org.apache.hc.core5.annotation.ThreadingBehavior; import org.apache.hc.core5.concurrent.CancellableDependency; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.HttpException; @@ -85,6 +85,7 @@ class InternalHttpClient extends CloseableHttpClient implements Configurable { private final HttpClientConnectionManager connManager; private final HttpRequestExecutor requestExecutor; + private final Supplier 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 Supplier 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.get(); 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..a6fdbd8b4b 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 @@ -39,9 +39,11 @@ 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; +import org.apache.hc.core5.function.Supplier; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.HttpException; import org.apache.hc.core5.http.HttpHost; @@ -69,6 +71,8 @@ class TestInternalHttpClient { @Mock private ExecChainHandler execChain; @Mock + private Supplier 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.get()).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.get()).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.get()).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.get()).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.get()).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.get()).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.get()).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).get(); + } }