Skip to content

Commit da1a8e0

Browse files
committed
Imporved TestAsyncClient wiring; added internal methods to get the underlying connection from the connection endpoint; coverage for async connection management operations
1 parent ab40ec9 commit da1a8e0

File tree

12 files changed

+447
-31
lines changed

12 files changed

+447
-31
lines changed

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/async/TestAsyncConnectionManagement.java

Lines changed: 315 additions & 0 deletions
Large diffs are not rendered by default.

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/MinimalTestClientBuilder.java

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@
3131
import org.apache.hc.client5.http.config.TlsConfig;
3232
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
3333
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
34-
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
3534
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
35+
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
3636
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
3737
import org.apache.hc.client5.testing.SSLTestContexts;
3838
import org.apache.hc.core5.http.config.Http1Config;
@@ -45,6 +45,7 @@ final class MinimalTestClientBuilder implements TestAsyncClientBuilder {
4545

4646
private final PoolingAsyncClientConnectionManagerBuilder connectionManagerBuilder;
4747

48+
private AsyncClientConnectionManager connectionManager;
4849
private Timeout timeout;
4950
private TlsStrategy tlsStrategy;
5051
private Http1Config http1Config;
@@ -65,6 +66,12 @@ public TestAsyncClientBuilder setTimeout(final Timeout timeout) {
6566
return this;
6667
}
6768

69+
@Override
70+
public TestAsyncClientBuilder setConnectionManager(final AsyncClientConnectionManager connectionManager) {
71+
this.connectionManager = connectionManager;
72+
return this;
73+
}
74+
6875
@Override
6976
public TestAsyncClientBuilder setTlsStrategy(final TlsStrategy tlsStrategy) {
7077
this.tlsStrategy = tlsStrategy;
@@ -97,21 +104,21 @@ public TestAsyncClientBuilder setH2Config(final H2Config h2Config) {
97104

98105
@Override
99106
public TestAsyncClient build() throws Exception {
100-
final PoolingAsyncClientConnectionManager connectionManager = connectionManagerBuilder
107+
final AsyncClientConnectionManager connectionManagerCopy = connectionManager == null ? connectionManagerBuilder
101108
.setTlsStrategy(tlsStrategy != null ? tlsStrategy : new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
102109
.setDefaultConnectionConfig(ConnectionConfig.custom()
103110
.setSocketTimeout(timeout)
104111
.setConnectTimeout(timeout)
105112
.build())
106-
.build();
113+
.build() : connectionManager;
107114
final CloseableHttpAsyncClient client = HttpAsyncClients.createMinimal(
108-
h2Config,
109-
http1Config,
110-
IOReactorConfig.custom()
111-
.setSoTimeout(timeout)
112-
.build(),
113-
connectionManager);
114-
return new TestAsyncClient(client, connectionManager);
115+
h2Config,
116+
http1Config,
117+
IOReactorConfig.custom()
118+
.setSoTimeout(timeout)
119+
.build(),
120+
connectionManagerCopy);
121+
return new TestAsyncClient(client, connectionManagerCopy);
115122
}
116123

117124
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/StandardTestClientBuilder.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@
3838
import org.apache.hc.client5.http.config.TlsConfig;
3939
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
4040
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
41-
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
4241
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
42+
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
4343
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
4444
import org.apache.hc.client5.testing.SSLTestContexts;
4545
import org.apache.hc.core5.http.Header;
@@ -57,6 +57,7 @@ final class StandardTestClientBuilder implements TestAsyncClientBuilder {
5757
private final PoolingAsyncClientConnectionManagerBuilder connectionManagerBuilder;
5858
private final HttpAsyncClientBuilder clientBuilder;
5959

60+
private AsyncClientConnectionManager connectionManager;
6061
private Timeout timeout;
6162
private TlsStrategy tlsStrategy;
6263

@@ -76,6 +77,12 @@ public TestAsyncClientBuilder setTimeout(final Timeout timeout) {
7677
return this;
7778
}
7879

80+
@Override
81+
public TestAsyncClientBuilder setConnectionManager(final AsyncClientConnectionManager connectionManager) {
82+
this.connectionManager = connectionManager;
83+
return this;
84+
}
85+
7986
@Override
8087
public TestAsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
8188
this.clientBuilder.addResponseInterceptorFirst(interceptor);
@@ -168,20 +175,20 @@ public TestAsyncClientBuilder setDefaultCredentialsProvider(final CredentialsPro
168175

169176
@Override
170177
public TestAsyncClient build() throws Exception {
171-
final PoolingAsyncClientConnectionManager connectionManager = connectionManagerBuilder
178+
final AsyncClientConnectionManager connectionManagerCopy = connectionManager == null ? connectionManagerBuilder
172179
.setTlsStrategy(tlsStrategy != null ? tlsStrategy : new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
173180
.setDefaultConnectionConfig(ConnectionConfig.custom()
174181
.setSocketTimeout(timeout)
175182
.setConnectTimeout(timeout)
176183
.build())
177-
.build();
184+
.build() : connectionManager;
178185
final CloseableHttpAsyncClient client = clientBuilder
179186
.setIOReactorConfig(IOReactorConfig.custom()
180187
.setSoTimeout(timeout)
181188
.build())
182-
.setConnectionManager(connectionManager)
189+
.setConnectionManager(connectionManagerCopy)
183190
.build();
184-
return new TestAsyncClient(client, connectionManager);
191+
return new TestAsyncClient(client, connectionManagerCopy);
185192
}
186193

187194
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/TestAsyncClient.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
import java.util.concurrent.Future;
3232

3333
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
34-
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
34+
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
3535
import org.apache.hc.core5.concurrent.FutureCallback;
3636
import org.apache.hc.core5.function.Supplier;
3737
import org.apache.hc.core5.http.HttpHost;
@@ -43,16 +43,15 @@
4343
import org.apache.hc.core5.io.CloseMode;
4444
import org.apache.hc.core5.reactor.IOReactorStatus;
4545
import org.apache.hc.core5.util.Args;
46-
import org.apache.hc.core5.util.Asserts;
4746
import org.apache.hc.core5.util.TimeValue;
4847

4948
public class TestAsyncClient extends CloseableHttpAsyncClient {
5049

5150
private final CloseableHttpAsyncClient client;
52-
private final PoolingAsyncClientConnectionManager connectionManager;
51+
private final AsyncClientConnectionManager connectionManager;
5352

5453
public TestAsyncClient(final CloseableHttpAsyncClient client,
55-
final PoolingAsyncClientConnectionManager connectionManager) {
54+
final AsyncClientConnectionManager connectionManager) {
5655
this.client = Args.notNull(client, "Client");
5756
this.connectionManager = connectionManager;
5857
}
@@ -113,9 +112,9 @@ public <T extends CloseableHttpAsyncClient> T getImplementation() {
113112
return (T) client;
114113
}
115114

116-
public PoolingAsyncClientConnectionManager getConnectionManager() {
117-
Asserts.check(connectionManager != null, "Connection manager is not available");
118-
return connectionManager;
115+
@SuppressWarnings("unchecked")
116+
public <T extends AsyncClientConnectionManager> T getConnectionManager() {
117+
return (T) connectionManager;
119118
}
120119

121120
}

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/async/TestAsyncClientBuilder.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
3636
import org.apache.hc.client5.http.auth.CredentialsProvider;
3737
import org.apache.hc.client5.http.config.TlsConfig;
38+
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
3839
import org.apache.hc.core5.http.Header;
3940
import org.apache.hc.core5.http.HttpRequestInterceptor;
4041
import org.apache.hc.core5.http.HttpResponseInterceptor;
@@ -50,8 +51,12 @@ public interface TestAsyncClientBuilder {
5051

5152
TestAsyncClientBuilder setTimeout(Timeout soTimeout);
5253

53-
default TestAsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
54-
return this;
54+
default TestAsyncClientBuilder setConnectionManager(AsyncClientConnectionManager connManager) {
55+
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel());
56+
}
57+
58+
default TestAsyncClientBuilder addResponseInterceptorFirst(HttpResponseInterceptor interceptor) {
59+
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel());
5560
}
5661

5762
default TestAsyncClientBuilder addResponseInterceptorLast(HttpResponseInterceptor interceptor) {

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/extension/sync/TestClientBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public interface TestClientBuilder {
5151

5252
TestClientBuilder setConnectionManager(HttpClientConnectionManager connManager);
5353

54-
default TestClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
55-
return this;
54+
default TestClientBuilder addResponseInterceptorFirst(HttpResponseInterceptor interceptor) {
55+
throw new UnsupportedOperationException("Operation not supported by " + getProtocolLevel());
5656
}
5757

5858
default TestClientBuilder addResponseInterceptorLast(HttpResponseInterceptor interceptor) {

httpclient5-testing/src/test/java/org/apache/hc/client5/testing/sync/TestConnectionManagement.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@ void testReleaseConnection() throws Exception {
129129
final LeaseRequest leaseRequest2 = connManager.lease("id2", route, null);
130130
Assertions.assertThrows(TimeoutException.class, () -> leaseRequest2.get(Timeout.ofMilliseconds(10)));
131131

132+
// close and release the connection
133+
// expect the next connection obtained to be closed
132134
endpoint1.close();
133135
connManager.release(endpoint1, null, TimeValue.NEG_ONE_MILLISECOND);
134136
final LeaseRequest leaseRequest3 = connManager.lease("id2", route, null);
@@ -141,7 +143,7 @@ void testReleaseConnection() throws Exception {
141143
Assertions.assertEquals(HttpStatus.SC_OK, response2.getCode());
142144
}
143145

144-
// release connection after marking it for re-use
146+
// release connection keeping it open
145147
// expect the next connection obtained to be open
146148
connManager.release(endpoint2, null, TimeValue.NEG_ONE_MILLISECOND);
147149

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* ====================================================================
3+
* Licensed to the Apache Software Foundation (ASF) under one
4+
* or more contributor license agreements. See the NOTICE file
5+
* distributed with this work for additional information
6+
* regarding copyright ownership. The ASF licenses this file
7+
* to you under the Apache License, Version 2.0 (the
8+
* "License"); you may not use this file except in compliance
9+
* with the License. You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing,
14+
* software distributed under the License is distributed on an
15+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
* KIND, either express or implied. See the License for the
17+
* specific language governing permissions and limitations
18+
* under the License.
19+
* ====================================================================
20+
*
21+
* This software consists of voluntary contributions made by many
22+
* individuals on behalf of the Apache Software Foundation. For more
23+
* information on the Apache Software Foundation, please see
24+
* <http://www.apache.org/>.
25+
*
26+
*/
27+
package org.apache.hc.client5.http.impl;
28+
29+
import org.apache.hc.core5.annotation.Internal;
30+
import org.apache.hc.core5.http.HttpConnection;
31+
32+
/**
33+
* Internal interface to expose the underlying connection
34+
* @since 5.5
35+
*/
36+
@Internal
37+
public interface ConnectionHolder {
38+
39+
HttpConnection get();
40+
41+
}

httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AbstractHttpAsyncClientBase.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,28 @@
2626
*/
2727
package org.apache.hc.client5.http.impl.async;
2828

29+
import java.net.SocketAddress;
2930
import java.util.concurrent.ExecutorService;
3031
import java.util.concurrent.Executors;
32+
import java.util.concurrent.Future;
3133
import java.util.concurrent.ThreadFactory;
3234
import java.util.concurrent.atomic.AtomicReference;
3335

36+
import org.apache.hc.core5.concurrent.FutureCallback;
3437
import org.apache.hc.core5.function.Supplier;
3538
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
3639
import org.apache.hc.core5.io.CloseMode;
40+
import org.apache.hc.core5.net.NamedEndpoint;
3741
import org.apache.hc.core5.reactor.ConnectionInitiator;
3842
import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
3943
import org.apache.hc.core5.reactor.IOReactorStatus;
44+
import org.apache.hc.core5.reactor.IOSession;
4045
import org.apache.hc.core5.util.TimeValue;
46+
import org.apache.hc.core5.util.Timeout;
4147
import org.slf4j.Logger;
4248
import org.slf4j.LoggerFactory;
4349

44-
abstract class AbstractHttpAsyncClientBase extends CloseableHttpAsyncClient {
50+
abstract class AbstractHttpAsyncClientBase extends CloseableHttpAsyncClient implements ConnectionInitiator {
4551

4652
enum Status { READY, RUNNING, TERMINATED }
4753

@@ -84,6 +90,17 @@ boolean isRunning() {
8490
return status.get() == Status.RUNNING;
8591
}
8692

93+
@Override
94+
public Future<IOSession> connect(
95+
final NamedEndpoint remoteEndpoint,
96+
final SocketAddress remoteAddress,
97+
final SocketAddress localAddress,
98+
final Timeout timeout,
99+
final Object attachment,
100+
final FutureCallback<IOSession> callback) {
101+
return ioReactor.connect(remoteEndpoint, remoteAddress, localAddress, timeout, attachment, callback);
102+
}
103+
87104
ConnectionInitiator getConnectionInitiator() {
88105
return ioReactor;
89106
}

httpclient5/src/main/java/org/apache/hc/client5/http/impl/io/BasicHttpClientConnectionManager.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import org.apache.hc.client5.http.config.ConnectionConfig;
4545
import org.apache.hc.client5.http.config.TlsConfig;
4646
import org.apache.hc.client5.http.impl.ConnPoolSupport;
47+
import org.apache.hc.client5.http.impl.ConnectionHolder;
4748
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
4849
import org.apache.hc.client5.http.io.ConnectionEndpoint;
4950
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
@@ -56,6 +57,7 @@
5657
import org.apache.hc.core5.annotation.ThreadingBehavior;
5758
import org.apache.hc.core5.http.ClassicHttpRequest;
5859
import org.apache.hc.core5.http.ClassicHttpResponse;
60+
import org.apache.hc.core5.http.HttpConnection;
5961
import org.apache.hc.core5.http.HttpException;
6062
import org.apache.hc.core5.http.HttpHost;
6163
import org.apache.hc.core5.http.URIScheme;
@@ -590,7 +592,7 @@ public void setValidateAfterInactivity(final TimeValue validateAfterInactivity)
590592
.build();
591593
}
592594

593-
class InternalConnectionEndpoint extends ConnectionEndpoint implements Identifiable {
595+
class InternalConnectionEndpoint extends ConnectionEndpoint implements ConnectionHolder, Identifiable {
594596

595597
private final HttpRoute route;
596598
private final AtomicReference<ManagedHttpClientConnection> connRef;
@@ -703,6 +705,11 @@ public EndpointInfo getInfo() {
703705
return null;
704706
}
705707

708+
@Override
709+
public HttpConnection get() {
710+
return this.connRef.get();
711+
}
712+
706713
}
707714

708715
/**

0 commit comments

Comments
 (0)