HTTPCLIENT-2135: TLS configuration on a per-host basis

This commit is contained in:
Oleg Kalnichevski 2021-09-12 14:54:32 +02:00
parent 4b7551a466
commit b10d43f2bb
37 changed files with 866 additions and 198 deletions

View File

@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -102,12 +103,14 @@ public class TestHttp1RequestReExecution extends AbstractIntegrationTestBase<Clo
@Override
protected void before() throws Throwable {
connManager.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2) ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.build());
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager)
.setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2) ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1);
.setConnectionManager(connManager);
}
};

View File

@ -34,9 +34,9 @@ import java.util.Random;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
@ -85,19 +85,19 @@ public class TestHttpAsyncMinimal extends AbstractHttpAsyncFundamentalsTest<Mini
@Override
protected MinimalHttpAsyncClient createClient() throws Exception {
final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.build();
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(TIMEOUT)
.build();
if (version.greaterEquals(HttpVersion.HTTP_2)) {
return HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, Http1Config.DEFAULT, ioReactorConfig, connectionManager);
} else {
return HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_1, H2Config.DEFAULT, Http1Config.DEFAULT, ioReactorConfig, connectionManager);
}
return HttpAsyncClients.createMinimal(
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.custom()
.setSoTimeout(TIMEOUT)
.build(),
PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2)
? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build());
}
@Override

View File

@ -34,9 +34,9 @@ import java.util.Random;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManager;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
@ -85,19 +85,19 @@ public class TestHttpMinimalReactive extends AbstractHttpReactiveFundamentalsTes
@Override
protected MinimalHttpAsyncClient createClient() throws Exception {
final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.build();
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(TIMEOUT)
.build();
if (version.greaterEquals(HttpVersion.HTTP_2)) {
return HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, Http1Config.DEFAULT, ioReactorConfig, connectionManager);
} else {
return HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_1, H2Config.DEFAULT, Http1Config.DEFAULT, ioReactorConfig, connectionManager);
}
return HttpAsyncClients.createMinimal(
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.custom()
.setSoTimeout(TIMEOUT)
.build(),
PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(version.greaterEquals(HttpVersion.HTTP_2)
? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build());
}
@Override

View File

@ -35,13 +35,12 @@ import java.util.concurrent.TimeoutException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.cache.CacheResponseStatus;
import org.apache.hc.client5.http.cache.HttpCacheContext;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.cache.CacheConfig;
import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients;
@ -88,17 +87,20 @@ public class CachingHttpAsyncClientCompatibilityTest {
CachingHttpAsyncClientCompatibilityTest(final HttpVersion protocolVersion, final HttpHost target) throws Exception {
this.protocolVersion = protocolVersion;
this.target = target;
final SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
.setTlsStrategy(new DefaultClientTlsStrategy(SSLContexts.custom()
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray())
.build()))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ?
HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build();
this.client = CachingHttpAsyncClients.custom()
.setCacheConfig(CacheConfig.custom()
.setMaxObjectSize(20480)
.build())
.setResourceFactory(HeapResourceFactory.INSTANCE)
.setVersionPolicy(this.protocolVersion == HttpVersion.HTTP_2 ? HttpVersionPolicy.FORCE_HTTP_2 : HttpVersionPolicy.FORCE_HTTP_1)
.setConnectionManager(this.connManager)
.build();
}

View File

@ -40,6 +40,7 @@ import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.Credentials;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
@ -139,9 +140,11 @@ public class HttpAsyncClientCompatibilityTest {
.loadTrustMaterial(getClass().getResource("/test-ca.keystore"), "nopassword".toCharArray()).build();
this.connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(sslContext))
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(versionPolicy)
.build())
.build();
this.client = HttpAsyncClients.custom()
.setVersionPolicy(this.versionPolicy)
.setConnectionManager(this.connManager)
.setProxy(this.proxy)
.setDefaultRequestConfig(requestConfig)

View File

@ -82,7 +82,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final LeaseRequest leaseRequest1 = this.connManager.lease("id1", route,null);
final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS);
this.connManager.connect(endpoint1, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint1, null, context);
final HttpProcessor httpProcessor = new DefaultHttpProcessor(
new RequestTargetHost(), new RequestContent(), new RequestConnControl());
@ -104,7 +104,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final ConnectionEndpoint endpoint2 = leaseRequest3.get(Timeout.ZERO_MILLISECONDS);
Assert.assertFalse(endpoint2.isConnected());
this.connManager.connect(endpoint2, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint2, null, context);
try (final ClassicHttpResponse response2 = endpoint2.execute("id2", request, exec, context)) {
Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
@ -145,7 +145,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final LeaseRequest leaseRequest1 = this.connManager.lease("id1", route,null);
final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS);
this.connManager.connect(endpoint1, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint1, null, context);
final HttpProcessor httpProcessor = new DefaultHttpProcessor(
new RequestTargetHost(), new RequestContent(), new RequestConnControl());
@ -168,7 +168,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final ConnectionEndpoint endpoint2 = leaseRequest3.get(Timeout.ZERO_MILLISECONDS);
Assert.assertFalse(endpoint2.isConnected());
this.connManager.connect(endpoint2, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint2, null, context);
try (final ClassicHttpResponse response2 = endpoint2.execute("id2", request, exec, context)) {
Assert.assertEquals(HttpStatus.SC_OK, response2.getCode());
@ -193,7 +193,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
Assert.assertFalse(endpoint4.isConnected());
// repeat the communication, no need to prepare the request again
this.connManager.connect(endpoint4, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint4, null, context);
try (final ClassicHttpResponse response4 = endpoint4.execute("id4", request, exec, context)) {
Assert.assertEquals(HttpStatus.SC_OK, response4.getCode());
@ -213,7 +213,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final LeaseRequest leaseRequest1 = this.connManager.lease("id1", route,null);
final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS);
this.connManager.connect(endpoint1, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint1, null, context);
Assert.assertEquals(1, this.connManager.getTotalStats().getLeased());
Assert.assertEquals(1, this.connManager.getStats(route).getLeased());
@ -262,7 +262,7 @@ public class TestConnectionManagement extends LocalServerTestBase {
final LeaseRequest leaseRequest1 = this.connManager.lease("id1", route,null);
final ConnectionEndpoint endpoint1 = leaseRequest1.get(Timeout.ZERO_MILLISECONDS);
this.connManager.connect(endpoint1, TimeValue.NEG_ONE_MILLISECOND, context);
this.connManager.connect(endpoint1, null, context);
Assert.assertEquals(1, this.connManager.getTotalStats().getLeased());
Assert.assertEquals(1, this.connManager.getStats(route).getLeased());

View File

@ -126,7 +126,6 @@ public class ConnectionConfig implements Cloneable {
this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
/**
* @see #setSocketTimeout(Timeout)
*/
@ -138,7 +137,7 @@ public class ConnectionConfig implements Cloneable {
/**
* Determines the default socket timeout value for I/O operations.
* <p>
* Default: {@code null}
* Default: {@code null} (undefined)
* </p>
*
* @return the default socket timeout value for I/O operations.
@ -150,8 +149,6 @@ public class ConnectionConfig implements Cloneable {
/**
* Determines the timeout until a new connection is fully established.
* This may also include transport security negotiation exchanges
* such as {@code SSL} or {@code TLS} protocol negotiation).
* <p>
* A timeout value of zero is interpreted as an infinite timeout.
* </p>
@ -177,7 +174,7 @@ public class ConnectionConfig implements Cloneable {
* be re-validated prior to being leased to the consumer. Negative values passed
* to this method disable connection validation.
* <p>
* Default: {@code null}
* Default: {@code null} (undefined)
* </p>
*/
public Builder setValidateAfterInactivity(final TimeValue validateAfterInactivity) {

View File

@ -0,0 +1,203 @@
/*
* ====================================================================
* 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
* <http://www.apache.org/>.
*
*/
package org.apache.hc.client5.http.config;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.util.Timeout;
/**
* Immutable class encapsulating TLS protocol settings.
*
* @since 5.2
*/
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class TlsConfig implements Cloneable {
public static final TlsConfig DEFAULT = new Builder().build();
private final Timeout handshakeTimeout;
private final String[] supportedProtocols;
private final String[] supportedCipherSuites;
private final HttpVersionPolicy httpVersionPolicy;
/**
* Intended for CDI compatibility
*/
protected TlsConfig() {
this(null, null, null, null);
}
TlsConfig(
final Timeout handshakeTimeout,
final String[] supportedProtocols,
final String[] supportedCipherSuites,
final HttpVersionPolicy httpVersionPolicy) {
super();
this.handshakeTimeout = handshakeTimeout;
this.supportedProtocols = supportedProtocols;
this.supportedCipherSuites = supportedCipherSuites;
this.httpVersionPolicy = httpVersionPolicy;
}
/**
* @see Builder#setHandshakeTimeout(Timeout)
*/
public Timeout getHandshakeTimeout() {
return handshakeTimeout;
}
/**
* @see Builder#setSupportedProtocols(String...)
*/
public String[] getSupportedProtocols() {
return supportedProtocols != null ? supportedProtocols.clone() : null;
}
/**
* @see Builder#setSupportedCipherSuites(String...)
*/
public String[] getSupportedCipherSuites() {
return supportedCipherSuites != null ? supportedCipherSuites.clone() : null;
}
/**
* @see Builder#setVersionPolicy(HttpVersionPolicy)
*/
public HttpVersionPolicy getHttpVersionPolicy() {
return httpVersionPolicy;
}
@Override
protected TlsConfig clone() throws CloneNotSupportedException {
return (TlsConfig) super.clone();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append("handshakeTimeout=").append(handshakeTimeout);
builder.append(", supportedProtocols=").append(Arrays.asList(supportedProtocols));
builder.append(", supportedCipherSuites=").append(Arrays.asList(supportedCipherSuites));
builder.append(", httpVersionPolicy=").append(httpVersionPolicy);
builder.append("]");
return builder.toString();
}
public static TlsConfig.Builder custom() {
return new Builder();
}
public static TlsConfig.Builder copy(final TlsConfig config) {
return new Builder()
.setHandshakeTimeout(config.getHandshakeTimeout())
.setSupportedProtocols(config.getSupportedProtocols())
.setSupportedCipherSuites(config.getSupportedCipherSuites())
.setVersionPolicy(config.getHttpVersionPolicy());
}
public static class Builder {
private Timeout handshakeTimeout;
private String[] supportedProtocols;
private String[] supportedCipherSuites;
private HttpVersionPolicy versionPolicy;
/**
* Determines the timeout used by TLS session negotiation exchanges (session handshake).
* <p>
* A timeout value of zero is interpreted as an infinite timeout.
* </p>
* <p>
* Default: {@code null} (undefined)
* </p>
*/
public Builder setHandshakeTimeout(final Timeout handshakeTimeout) {
this.handshakeTimeout = handshakeTimeout;
return this;
}
/**
* @see #setHandshakeTimeout(Timeout)
*/
public Builder setHandshakeTimeout(final long handshakeTimeout, final TimeUnit timeUnit) {
this.handshakeTimeout = Timeout.of(handshakeTimeout, timeUnit);
return this;
}
/**
* Determines supported TLS protocols.
* <p>
* Default: {@code null} (undefined)
* </p>
*/
public Builder setSupportedProtocols(final String... supportedProtocols) {
this.supportedProtocols = supportedProtocols;
return this;
}
/**
* Determines supported cipher suites.
* <p>
* Default: {@code null} (undefined)
* </p>
*/
public Builder setSupportedCipherSuites(final String... supportedCipherSuites) {
this.supportedCipherSuites = supportedCipherSuites;
return this;
}
/**
* Determines the HTTP protocol policy. By default, connections are expected to use TLS ALPN
* extension to negotiate the application protocol to be used by both endpoints.
* </p>
* <p>
* Default: {@link HttpVersionPolicy#NEGOTIATE}
* </p>
*/
public Builder setVersionPolicy(final HttpVersionPolicy versionPolicy) {
this.versionPolicy = versionPolicy;
return this;
}
public TlsConfig build() {
return new TlsConfig(
handshakeTimeout,
supportedProtocols,
supportedCipherSuites,
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE);
}
}
}

View File

@ -47,6 +47,7 @@ import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.CookieSpecFactory;
import org.apache.hc.client5.http.cookie.CookieStore;
@ -196,7 +197,11 @@ public class HttpAsyncClientBuilder {
}
private HttpVersionPolicy versionPolicy;
/**
* @deprecated TLS should be configured by the connection manager
*/
@Deprecated
private TlsConfig tlsConfig;
private AsyncClientConnectionManager connManager;
private boolean connManagerShared;
private IOReactorConfig ioReactorConfig;
@ -254,9 +259,12 @@ public class HttpAsyncClientBuilder {
/**
* Sets HTTP protocol version policy.
*
* @deprecated Use {@link TlsConfig} and connection nanager methods
*/
@Deprecated
public final HttpAsyncClientBuilder setVersionPolicy(final HttpVersionPolicy versionPolicy) {
this.versionPolicy = versionPolicy;
this.tlsConfig = versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null;
return this;
}
@ -903,7 +911,6 @@ public class HttpAsyncClientBuilder {
final IOEventHandlerFactory ioEventHandlerFactory = new HttpAsyncClientEventHandlerFactory(
new DefaultHttpProcessor(new H2RequestContent(), new H2RequestTargetHost(), new H2RequestConnControl()),
(request, context) -> pushConsumerRegistry.get(request),
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
h2Config != null ? h2Config : H2Config.DEFAULT,
h1Config != null ? h1Config : Http1Config.DEFAULT,
charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
@ -986,7 +993,7 @@ public class HttpAsyncClientBuilder {
threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true),
connManagerCopy,
routePlannerCopy,
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
tlsConfig,
cookieSpecRegistryCopy,
authSchemeRegistryCopy,
cookieStoreCopy,

View File

@ -74,7 +74,6 @@ class HttpAsyncClientEventHandlerFactory implements IOEventHandlerFactory {
private final HttpProcessor httpProcessor;
private final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory;
private final HttpVersionPolicy versionPolicy;
private final H2Config h2Config;
private final Http1Config h1Config;
private final CharCodingConfig charCodingConfig;
@ -85,14 +84,12 @@ class HttpAsyncClientEventHandlerFactory implements IOEventHandlerFactory {
HttpAsyncClientEventHandlerFactory(
final HttpProcessor httpProcessor,
final HandlerFactory<AsyncPushConsumer> exchangeHandlerFactory,
final HttpVersionPolicy versionPolicy,
final H2Config h2Config,
final Http1Config h1Config,
final CharCodingConfig charCodingConfig,
final ConnectionReuseStrategy connectionReuseStrategy) {
this.httpProcessor = Args.notNull(httpProcessor, "HTTP processor");
this.exchangeHandlerFactory = exchangeHandlerFactory;
this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
this.h2Config = h2Config != null ? h2Config : H2Config.DEFAULT;
this.h1Config = h1Config != null ? h1Config : Http1Config.DEFAULT;
this.charCodingConfig = charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT;
@ -238,7 +235,7 @@ class HttpAsyncClientEventHandlerFactory implements IOEventHandlerFactory {
ioSession,
http1StreamHandlerFactory,
http2StreamHandlerFactory,
attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : versionPolicy);
attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : null);
}
final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
httpProcessor,
@ -258,7 +255,7 @@ class HttpAsyncClientEventHandlerFactory implements IOEventHandlerFactory {
ioSession,
http1StreamHandlerFactory,
http2StreamHandlerFactory,
attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : versionPolicy);
attachment instanceof HttpVersionPolicy ? (HttpVersionPolicy) attachment : null);
}
}

View File

@ -30,6 +30,7 @@ package org.apache.hc.client5.http.impl.async;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
@ -122,26 +123,29 @@ public final class HttpAsyncClients {
private static MinimalHttpAsyncClient createMinimalHttpAsyncClientImpl(
final IOEventHandlerFactory eventHandlerFactory,
final AsyncPushConsumerRegistry pushConsumerRegistry,
final HttpVersionPolicy versionPolicy,
final IOReactorConfig ioReactorConfig,
final AsyncClientConnectionManager connmgr,
final SchemePortResolver schemePortResolver) {
final SchemePortResolver schemePortResolver,
final TlsConfig tlsConfig) {
return new MinimalHttpAsyncClient(
eventHandlerFactory,
pushConsumerRegistry,
versionPolicy,
ioReactorConfig,
new DefaultThreadFactory("httpclient-main", true),
new DefaultThreadFactory("httpclient-dispatch", true),
connmgr,
schemePortResolver);
schemePortResolver,
tlsConfig);
}
/**
* Creates {@link MinimalHttpAsyncClient} instance optimized for
* HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol
* functionality.
*
* @deprecated Use {@link #createMinimal(H2Config, Http1Config, IOReactorConfig, AsyncClientConnectionManager)}
*/
@Deprecated
public static MinimalHttpAsyncClient createMinimal(
final HttpVersionPolicy versionPolicy,
final H2Config h2Config,
@ -153,16 +157,60 @@ public final class HttpAsyncClients {
new HttpAsyncClientEventHandlerFactory(
createMinimalProtocolProcessor(),
(request, context) -> pushConsumerRegistry.get(request),
versionPolicy,
h2Config,
h1Config,
CharCodingConfig.DEFAULT,
DefaultClientConnectionReuseStrategy.INSTANCE),
pushConsumerRegistry,
versionPolicy,
ioReactorConfig,
connmgr,
DefaultSchemePortResolver.INSTANCE);
DefaultSchemePortResolver.INSTANCE,
versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null);
}
/**
* Creates {@link MinimalHttpAsyncClient} instance optimized for
* HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol
* functionality.
*
* @since 5.2
*/
public static MinimalHttpAsyncClient createMinimal(
final H2Config h2Config,
final Http1Config h1Config,
final IOReactorConfig ioReactorConfig,
final AsyncClientConnectionManager connmgr) {
final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
return createMinimalHttpAsyncClientImpl(
new HttpAsyncClientEventHandlerFactory(
createMinimalProtocolProcessor(),
(request, context) -> pushConsumerRegistry.get(request),
h2Config,
h1Config,
CharCodingConfig.DEFAULT,
DefaultClientConnectionReuseStrategy.INSTANCE),
pushConsumerRegistry,
ioReactorConfig,
connmgr,
DefaultSchemePortResolver.INSTANCE,
null);
}
/**
* Creates {@link MinimalHttpAsyncClient} instance optimized for
* HTTP/1.1 and HTTP/2 message transport without advanced HTTP protocol
* functionality.
*
* @deprecated Use {@link #createMinimal(H2Config, Http1Config, IOReactorConfig)}
*/
@Deprecated
public static MinimalHttpAsyncClient createMinimal(
final HttpVersionPolicy versionPolicy,
final H2Config h2Config,
final Http1Config h1Config,
final IOReactorConfig ioReactorConfig) {
return createMinimal(versionPolicy, h2Config, h1Config, ioReactorConfig,
PoolingAsyncClientConnectionManagerBuilder.create().build());
}
/**
@ -171,11 +219,10 @@ public final class HttpAsyncClients {
* functionality.
*/
public static MinimalHttpAsyncClient createMinimal(
final HttpVersionPolicy versionPolicy,
final H2Config h2Config,
final Http1Config h1Config,
final IOReactorConfig ioReactorConfig) {
return createMinimal(versionPolicy, h2Config, h1Config, ioReactorConfig,
return createMinimal(h2Config, h1Config, ioReactorConfig,
PoolingAsyncClientConnectionManagerBuilder.create().build());
}

View File

@ -35,6 +35,7 @@ import org.apache.hc.client5.http.async.AsyncExecRuntime;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.cookie.CookieSpecFactory;
import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@ -48,7 +49,6 @@ import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
import org.apache.hc.core5.http.nio.HandlerFactory;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -71,7 +71,7 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie
private static final Logger LOG = LoggerFactory.getLogger(InternalHttpAsyncClient.class);
private final AsyncClientConnectionManager manager;
private final HttpRoutePlanner routePlanner;
private final HttpVersionPolicy versionPolicy;
private final TlsConfig tlsConfig;
InternalHttpAsyncClient(
final DefaultConnectingIOReactor ioReactor,
@ -80,7 +80,7 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie
final ThreadFactory threadFactory,
final AsyncClientConnectionManager manager,
final HttpRoutePlanner routePlanner,
final HttpVersionPolicy versionPolicy,
final TlsConfig tlsConfig,
final Lookup<CookieSpecFactory> cookieSpecRegistry,
final Lookup<AuthSchemeFactory> authSchemeRegistry,
final CookieStore cookieStore,
@ -91,12 +91,12 @@ public final class InternalHttpAsyncClient extends InternalAbstractHttpAsyncClie
cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, closeables);
this.manager = manager;
this.routePlanner = routePlanner;
this.versionPolicy = versionPolicy;
this.tlsConfig = tlsConfig;
}
@Override
AsyncExecRuntime createAsyncExecRuntime(final HandlerFactory<AsyncPushConsumer> pushHandlerFactory) {
return new InternalHttpAsyncExecRuntime(LOG, manager, getConnectionInitiator(), pushHandlerFactory, versionPolicy);
return new InternalHttpAsyncExecRuntime(LOG, manager, getConnectionInitiator(), pushHandlerFactory, tlsConfig);
}
@Override

View File

@ -33,6 +33,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.async.AsyncExecRuntime;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.Operations;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@ -44,7 +45,6 @@ import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
import org.apache.hc.core5.http.nio.HandlerFactory;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.ConnectionInitiator;
import org.apache.hc.core5.util.TimeValue;
@ -57,7 +57,11 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
private final AsyncClientConnectionManager manager;
private final ConnectionInitiator connectionInitiator;
private final HandlerFactory<AsyncPushConsumer> pushHandlerFactory;
private final HttpVersionPolicy versionPolicy;
/**
* @deprecated TLS should be configured by the connection manager
*/
@Deprecated
private final TlsConfig tlsConfig;
private final AtomicReference<AsyncConnectionEndpoint> endpointRef;
private volatile boolean reusable;
private volatile Object state;
@ -68,14 +72,14 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
final AsyncClientConnectionManager manager,
final ConnectionInitiator connectionInitiator,
final HandlerFactory<AsyncPushConsumer> pushHandlerFactory,
final HttpVersionPolicy versionPolicy) {
final TlsConfig tlsConfig) {
super();
this.log = log;
this.manager = manager;
this.connectionInitiator = connectionInitiator;
this.pushHandlerFactory = pushHandlerFactory;
this.versionPolicy = versionPolicy;
this.endpointRef = new AtomicReference<>();
this.tlsConfig = tlsConfig;
this.endpointRef = new AtomicReference<>(null);
this.validDuration = TimeValue.NEG_ONE_MILLISECOND;
}
@ -213,7 +217,7 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
endpoint,
connectionInitiator,
connectTimeout,
versionPolicy,
tlsConfig,
context,
new CallbackContribution<AsyncConnectionEndpoint>(callback) {
@ -242,7 +246,7 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
if (log.isDebugEnabled()) {
log.debug("{} upgrading endpoint", ConnPoolSupport.getId(endpoint));
}
manager.upgrade(endpoint, versionPolicy, context, new CallbackContribution<AsyncConnectionEndpoint>(callback) {
manager.upgrade(endpoint, tlsConfig, context, new CallbackContribution<AsyncConnectionEndpoint>(callback) {
@Override
public void completed(final AsyncConnectionEndpoint endpoint) {
@ -320,7 +324,7 @@ class InternalHttpAsyncExecRuntime implements AsyncExecRuntime {
@Override
public AsyncExecRuntime fork() {
return new InternalHttpAsyncExecRuntime(log, manager, connectionInitiator, pushHandlerFactory, versionPolicy);
return new InternalHttpAsyncExecRuntime(log, manager, connectionInitiator, pushHandlerFactory, tlsConfig);
}
}

View File

@ -39,6 +39,7 @@ import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.impl.ExecSupport;
@ -69,7 +70,6 @@ import org.apache.hc.core5.http.nio.HandlerFactory;
import org.apache.hc.core5.http.nio.RequestChannel;
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.io.Closer;
import org.apache.hc.core5.reactor.Command;
@ -101,17 +101,17 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
private static final Logger LOG = LoggerFactory.getLogger(MinimalHttpAsyncClient.class);
private final AsyncClientConnectionManager manager;
private final SchemePortResolver schemePortResolver;
private final HttpVersionPolicy versionPolicy;
private final TlsConfig tlsConfig;
MinimalHttpAsyncClient(
final IOEventHandlerFactory eventHandlerFactory,
final AsyncPushConsumerRegistry pushConsumerRegistry,
final HttpVersionPolicy versionPolicy,
final IOReactorConfig reactorConfig,
final ThreadFactory threadFactory,
final ThreadFactory workerThreadFactory,
final AsyncClientConnectionManager manager,
final SchemePortResolver schemePortResolver) {
final SchemePortResolver schemePortResolver,
final TlsConfig tlsConfig) {
super(new DefaultConnectingIOReactor(
eventHandlerFactory,
reactorConfig,
@ -124,7 +124,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
threadFactory);
this.manager = manager;
this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE;
this.versionPolicy = versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE;
this.tlsConfig = tlsConfig;
}
private Future<AsyncConnectionEndpoint> leaseEndpoint(
@ -153,7 +153,7 @@ public final class MinimalHttpAsyncClient extends AbstractMinimalHttpAsyncClient
connectionEndpoint,
getConnectionInitiator(),
connectTimeout,
versionPolicy,
tlsConfig,
clientContext,
new FutureCallback<AsyncConnectionEndpoint>() {

View File

@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
@ -109,6 +110,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
private boolean leased;
private SocketConfig socketConfig;
private ConnectionConfig connectionConfig;
private TlsConfig tlsConfig;
private final AtomicBoolean closed;
@ -142,6 +144,8 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
this.id = String.format("ep-%010d", COUNT.getAndIncrement());
this.expiry = Long.MAX_VALUE;
this.socketConfig = SocketConfig.DEFAULT;
this.connectionConfig = ConnectionConfig.DEFAULT;
this.tlsConfig = TlsConfig.DEFAULT;
this.closed = new AtomicBoolean(false);
this.validateAfterInactivity = TimeValue.ofSeconds(2L);
}
@ -203,6 +207,13 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
/**
* @since 5.2
*/
public synchronized void setTlsConfig(final TlsConfig tlsConfig) {
this.tlsConfig = tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
}
public LeaseRequest lease(final String id, final HttpRoute route, final Object state) {
return lease(id, route, Timeout.DISABLED, state);
}
@ -358,8 +369,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
} else {
host = route.getTargetHost();
}
final ConnectionConfig config = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
final TimeValue connectTimeout = timeout != null ? timeout : config.getConnectTimeout();
final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout();
final ManagedHttpClientConnection connection = internalEndpoint.getConnection();
if (LOG.isDebugEnabled()) {
LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
@ -369,12 +379,13 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
host,
route.getLocalSocketAddress(),
connectTimeout,
this.socketConfig,
socketConfig,
tlsConfig,
context);
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
}
final Timeout socketTimeout = config.getSocketTimeout();
final Timeout socketTimeout = connectionConfig.getSocketTimeout();
if (socketTimeout != null) {
connection.setSocketTimeout(socketTimeout);
}
@ -387,9 +398,11 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
Args.notNull(endpoint, "Endpoint");
Args.notNull(route, "HTTP route");
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final ConnectionConfig config = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
this.connectionOperator.upgrade(
internalEndpoint.getConnection(),
internalEndpoint.getRoute().getTargetHost(),
tlsConfig,
context);
}

View File

@ -54,6 +54,7 @@ import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -107,6 +108,19 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
final TimeValue connectTimeout,
final SocketConfig socketConfig,
final HttpContext context) throws IOException {
final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
connect(conn, host, localAddress, timeout, socketConfig, null, context);
}
@Override
public void connect(
final ManagedHttpClientConnection conn,
final HttpHost host,
final InetSocketAddress localAddress,
final Timeout connectTimeout,
final SocketConfig socketConfig,
final Object attachment,
final HttpContext context) throws IOException {
Args.notNull(conn, "Connection");
Args.notNull(host, "Host");
Args.notNull(socketConfig, "Socket config");
@ -131,13 +145,17 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
}
}
final Timeout soTimeout = socketConfig.getSoTimeout();
final int port = this.schemePortResolver.resolve(host);
for (int i = 0; i < remoteAddresses.length; i++) {
final InetAddress address = remoteAddresses[i];
final boolean last = i == remoteAddresses.length - 1;
Socket sock = sf.createSocket(context);
sock.setSoTimeout(socketConfig.getSoTimeout().toMillisecondsIntBound());
if (soTimeout != null) {
sock.setSoTimeout(soTimeout.toMillisecondsIntBound());
}
sock.setReuseAddress(socketConfig.isSoReuseAddress());
sock.setTcpNoDelay(socketConfig.isTcpNoDelay());
sock.setKeepAlive(socketConfig.isSoKeepAlive());
@ -160,8 +178,9 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
host.getHostName(), host.getPort(), localAddress, remoteAddress, connectTimeout);
}
try {
sock = sf.connectSocket(connectTimeout, sock, host, remoteAddress, localAddress, context);
sock = sf.connectSocket(sock, host, remoteAddress, localAddress, connectTimeout, attachment, context);
conn.bind(sock);
conn.setSocketTimeout(soTimeout);
if (LOG.isDebugEnabled()) {
LOG.debug("{}:{} connected {}->{} as {}",
host.getHostName(), host.getPort(), localAddress, remoteAddress, ConnPoolSupport.getId(conn));
@ -189,6 +208,15 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
final ManagedHttpClientConnection conn,
final HttpHost host,
final HttpContext context) throws IOException {
upgrade(conn, host, null, context);
}
@Override
public void upgrade(
final ManagedHttpClientConnection conn,
final HttpHost host,
final Object attachment,
final HttpContext context) throws IOException {
final HttpClientContext clientContext = HttpClientContext.adapt(context);
final Lookup<ConnectionSocketFactory> registry = getSocketFactoryRegistry(clientContext);
final ConnectionSocketFactory sf = registry.lookup(host.getSchemeName());
@ -206,7 +234,7 @@ public class DefaultHttpClientConnectionOperator implements HttpClientConnection
throw new ConnectionClosedException("Connection is closed");
}
final int port = this.schemePortResolver.resolve(host);
sock = lsf.createLayeredSocket(sock, host.getHostName(), port, context);
sock = lsf.createLayeredSocket(sock, host.getHostName(), port, attachment, context);
conn.bind(sock);
}

View File

@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
@ -115,6 +116,7 @@ public class PoolingHttpClientConnectionManager
private volatile Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private volatile Resolver<HttpHost, TlsConfig> tlsConfigResolver;
public PoolingHttpClientConnectionManager() {
this(RegistryBuilder.<ConnectionSocketFactory>create()
@ -241,16 +243,22 @@ public class PoolingHttpClientConnectionManager
throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass());
}
private SocketConfig resolveSocketConfig(final HttpRoute route) {
final Resolver<HttpRoute, SocketConfig> resolver = this.socketConfigResolver;
final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null;
return socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
}
private ConnectionConfig resolveConnectionConfig(final HttpRoute route) {
final Resolver<HttpRoute, ConnectionConfig> resolver = this.connectionConfigResolver;
final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(route) : null;
return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
private SocketConfig resolveSocketConfig(final HttpRoute route) {
final Resolver<HttpRoute, SocketConfig> resolver = this.socketConfigResolver;
final SocketConfig socketConfig = resolver != null ? resolver.resolve(route) : null;
return socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
private TlsConfig resolveTlsConfig(final HttpHost host) {
final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
}
private TimeValue resolveValidateAfterInactivity(final ConnectionConfig connectionConfig) {
@ -401,15 +409,11 @@ public class PoolingHttpClientConnectionManager
poolEntry.assignConnection(connFactory.createConnection(null));
}
final HttpRoute route = poolEntry.getRoute();
final HttpHost host;
if (route.getProxyHost() != null) {
host = route.getProxyHost();
} else {
host = route.getTargetHost();
}
final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
final SocketConfig socketConfig = resolveSocketConfig(route);
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final TimeValue connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
final TlsConfig tlsConfig = resolveTlsConfig(host);
final Timeout connectTimeout = timeout != null ? Timeout.of(timeout.getDuration(), timeout.getTimeUnit()) : connectionConfig.getConnectTimeout();
if (LOG.isDebugEnabled()) {
LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
}
@ -418,8 +422,9 @@ public class PoolingHttpClientConnectionManager
conn,
host,
route.getLocalSocketAddress(),
timeout,
connectTimeout,
socketConfig,
tlsConfig,
context);
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
@ -436,7 +441,9 @@ public class PoolingHttpClientConnectionManager
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final PoolEntry<HttpRoute, ManagedHttpClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
final HttpRoute route = poolEntry.getRoute();
this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), context);
final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
final TlsConfig tlsConfig = resolveTlsConfig(host);
this.connectionOperator.upgrade(poolEntry.getConnection(), route.getTargetHost(), tlsConfig, context);
}
@Override
@ -533,6 +540,24 @@ public class PoolingHttpClientConnectionManager
this.connectionConfigResolver = connectionConfigResolver;
}
/**
* Sets the same {@link ConnectionConfig} for all hosts
*
* @since 5.2
*/
public void setDefaultTlsConfig(final TlsConfig config) {
this.tlsConfigResolver = (host) -> config;
}
/**
* Sets {@link Resolver} of {@link TlsConfig} on a per host basis.
*
* @since 5.2
*/
public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
this.tlsConfigResolver = tlsConfigResolver;
}
/**
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
*/

View File

@ -31,12 +31,14 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.URIScheme;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.HttpConnectionFactory;
@ -81,6 +83,7 @@ public class PoolingHttpClientConnectionManagerBuilder {
private PoolReusePolicy poolReusePolicy;
private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private Resolver<HttpHost, TlsConfig> tlsConfigResolver;
private boolean systemProperties;
@ -203,6 +206,27 @@ public class PoolingHttpClientConnectionManagerBuilder {
return this;
}
/**
* Assigns the same {@link TlsConfig} for all hosts.
*
* @since 5.2
*/
public final PoolingHttpClientConnectionManagerBuilder setDefaultTlsConfig(final TlsConfig config) {
this.tlsConfigResolver = (host) -> config;
return this;
}
/**
* Assigns {@link Resolver} of {@link TlsConfig} on a per host basis.
*
* @since 5.2
*/
public final PoolingHttpClientConnectionManagerBuilder setTlsConfigResolver(
final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
this.tlsConfigResolver = tlsConfigResolver;
return this;
}
/**
* Sets maximum time to live for persistent connections
*/
@ -251,6 +275,7 @@ public class PoolingHttpClientConnectionManagerBuilder {
connectionFactory);
poolingmgr.setSocketConfigResolver(socketConfigResolver);
poolingmgr.setConnectionConfigResolver(connectionConfigResolver);
poolingmgr.setTlsConfigResolver(tlsConfigResolver);
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}

View File

@ -34,6 +34,7 @@ import java.util.concurrent.Future;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.nio.AsyncClientConnectionOperator;
import org.apache.hc.client5.http.nio.ManagedAsyncClientConnection;
@ -81,13 +82,14 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
final HttpHost remoteEndpoint = RoutingSupport.normalize(host, schemePortResolver);
final InetAddress remoteAddress = host.getAddress();
final TlsStrategy tlsStrategy = tlsStrategyLookup != null ? tlsStrategyLookup.lookup(host.getSchemeName()) : null;
final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
final Future<IOSession> sessionFuture = sessionRequester.connect(
connectionInitiator,
remoteEndpoint,
remoteAddress != null ? new InetSocketAddress(remoteAddress, remoteEndpoint.getPort()) : null,
localAddress,
connectTimeout,
attachment,
tlsConfig.getHttpVersionPolicy(),
new FutureCallback<IOSession>() {
@Override
@ -95,15 +97,18 @@ final class DefaultAsyncClientConnectionOperator implements AsyncClientConnectio
final DefaultManagedAsyncClientConnection connection = new DefaultManagedAsyncClientConnection(session);
if (tlsStrategy != null && URIScheme.HTTPS.same(host.getSchemeName())) {
try {
final Timeout socketTimeout = connection.getSocketTimeout();
final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
tlsStrategy.upgrade(
connection,
host,
attachment,
null,
handshakeTimeout != null ? handshakeTimeout : connectTimeout,
new FutureContribution<TransportSecurityLayer>(future) {
@Override
public void completed(final TransportSecurityLayer transportSecurityLayer) {
connection.setSocketTimeout(socketTimeout);
future.completed(connection);
}

View File

@ -41,6 +41,7 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@ -123,6 +124,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
private final AtomicBoolean closed;
private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private volatile Resolver<HttpHost, TlsConfig> tlsConfigResolver;
public PoolingAsyncClientConnectionManager() {
this(RegistryBuilder.<TlsStrategy>create()
@ -227,6 +229,15 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
private TlsConfig resolveTlsConfig(final HttpHost host, final Object attachment) {
if (attachment instanceof TlsConfig) {
return (TlsConfig) attachment;
}
final Resolver<HttpHost, TlsConfig> resolver = this.tlsConfigResolver;
final TlsConfig tlsConfig = resolver != null ? resolver.resolve(host) : null;
return tlsConfig != null ? tlsConfig : TlsConfig.DEFAULT;
}
@Override
public Future<AsyncConnectionEndpoint> lease(
final String id,
@ -400,6 +411,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
}
final InetSocketAddress localAddress = route.getLocalSocketAddress();
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
final Timeout connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
if (LOG.isDebugEnabled()) {
@ -410,7 +422,9 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
host,
localAddress,
connectTimeout,
route.isTunnelled() ? HttpVersionPolicy.FORCE_HTTP_1 : attachment,
route.isTunnelled() ? TlsConfig.copy(tlsConfig)
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
.build() : tlsConfig,
new FutureCallback<ManagedAsyncClientConnection>() {
@Override
@ -455,10 +469,12 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry = internalEndpoint.getValidatedPoolEntry();
final HttpRoute route = poolEntry.getRoute();
final HttpHost host = route.getProxyHost() != null ? route.getProxyHost() : route.getTargetHost();
final TlsConfig tlsConfig = resolveTlsConfig(host, attachment);
connectionOperator.upgrade(
poolEntry.getConnection(),
route.getTargetHost(),
attachment,
attachment != null ? attachment : tlsConfig,
new CallbackContribution<ManagedAsyncClientConnection>(callback) {
@Override
@ -566,6 +582,24 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
this.connectionConfigResolver = connectionConfigResolver;
}
/**
* Sets the same {@link ConnectionConfig} for all hosts
*
* @since 5.2
*/
public void setDefaultTlsConfig(final TlsConfig config) {
this.tlsConfigResolver = (host) -> config;
}
/**
* Sets {@link Resolver} of {@link TlsConfig} on a per host basis.
*
* @since 5.2
*/
public void setTlsConfigResolver(final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
this.tlsConfigResolver = tlsConfigResolver;
}
/**
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
*/

View File

@ -31,9 +31,11 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.ssl.ConscryptClientTlsStrategy;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
@ -83,6 +85,7 @@ public class PoolingAsyncClientConnectionManagerBuilder {
private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private Resolver<HttpHost, TlsConfig> tlsConfigResolver;
private TimeValue timeToLive;
public static PoolingAsyncClientConnectionManagerBuilder create() {
@ -171,6 +174,26 @@ public class PoolingAsyncClientConnectionManagerBuilder {
return this;
}
/**
* Assigns the same {@link TlsConfig} for all hosts.
*
* @since 5.2
*/
public final PoolingAsyncClientConnectionManagerBuilder setDefaultTlsConfig(final TlsConfig config) {
this.tlsConfigResolver = (host) -> config;
return this;
}
/**
* Assigns {@link Resolver} of {@link TlsConfig} on a per host basis.
*
* @since 5.2
*/
public final PoolingAsyncClientConnectionManagerBuilder setTlsConfigResolver(
final Resolver<HttpHost, TlsConfig> tlsConfigResolver) {
this.tlsConfigResolver = tlsConfigResolver;
return this;
}
/**
* Sets maximum time to live for persistent connections
@ -232,6 +255,7 @@ public class PoolingAsyncClientConnectionManagerBuilder {
schemePortResolver,
dnsResolver);
poolingmgr.setConnectionConfigResolver(connectionConfigResolver);
poolingmgr.setTlsConfigResolver(tlsConfigResolver);
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}

View File

@ -37,6 +37,7 @@ import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
/**
* Connection operator that performs connection connect and upgrade operations.
@ -65,6 +66,30 @@ public interface HttpClientConnectionOperator {
SocketConfig socketConfig,
HttpContext context) throws IOException;
/**
* Connect the given managed connection to the remote endpoint.
*
* @param conn the managed connection.
* @param host the address of the opposite endpoint.
* @param localAddress the address of the local endpoint.
* @param connectTimeout the timeout of the connect operation.
* @param socketConfig the socket configuration.
* @param attachment connect request attachment.
* @param context the execution context.
*
* @since 5.2
*/
default void connect(
ManagedHttpClientConnection conn,
HttpHost host,
InetSocketAddress localAddress,
Timeout connectTimeout,
SocketConfig socketConfig,
Object attachment,
HttpContext context) throws IOException {
connect(conn, host, localAddress, connectTimeout, socketConfig, context);
}
/**
* Upgrades transport security of the given managed connection
* by using the TLS security protocol.
@ -78,4 +103,23 @@ public interface HttpClientConnectionOperator {
HttpHost host,
HttpContext context) throws IOException;
/**
* Upgrades transport security of the given managed connection
* by using the TLS security protocol.
*
* @param conn the managed connection.
* @param host the address of the opposite endpoint with TLS security.
* @param attachment connect request attachment.
* @param context the execution context.
*
* @since 5.2
*/
default void upgrade(
ManagedHttpClientConnection conn,
HttpHost host,
Object attachment,
HttpContext context) throws IOException {
upgrade(conn, host, context);
}
}

View File

@ -67,7 +67,6 @@ public interface AsyncClientConnectionOperator {
Object attachment,
FutureCallback<ManagedAsyncClientConnection> callback);
/**
* Upgrades transport security of the given managed connection
* by using the TLS security protocol.

View File

@ -36,6 +36,7 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
/**
* A factory for creating and connecting connection sockets.
@ -81,4 +82,35 @@ public interface ConnectionSocketFactory {
InetSocketAddress localAddress,
HttpContext context) throws IOException;
/**
* Connects the socket to the target host with the given resolved remote address.
*
* @param socket the socket to connect, as obtained from {@link #createSocket(HttpContext)}.
* {@code null} indicates that a new socket should be created and connected.
* @param host target host as specified by the caller (end user).
* @param remoteAddress the resolved remote address to connect to.
* @param localAddress the local address to bind the socket to, or {@code null} for any.
* @param connectTimeout connect timeout.
* @param attachment connect request attachment.
* @param context the actual HTTP context.
*
* @return the connected socket. The returned object may be different
* from the {@code sock} argument if this factory supports
* a layered protocol.
*
* @throws IOException if an I/O error occurs
*
* @since 5.2
*/
default Socket connectSocket(
Socket socket,
HttpHost host,
InetSocketAddress remoteAddress,
InetSocketAddress localAddress,
Timeout connectTimeout,
Object attachment,
HttpContext context) throws IOException {
return connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context);
}
}

View File

@ -62,4 +62,30 @@ public interface LayeredConnectionSocketFactory extends ConnectionSocketFactory
int port,
HttpContext context) throws IOException;
/**
* Returns a socket connected to the given host that is layered over an
* existing socket. Used primarily for creating secure sockets through
* proxies.
*
* @param socket the existing socket
* @param target the name of the target host.
* @param port the port to connect to on the target host.
* @param context the actual HTTP context.
* @param attachment connect request attachment.
*
* @return Socket a new socket
*
* @throws IOException if an I/O error occurs while creating the socket
*
* @since 5.2
*/
default Socket createLayeredSocket(
Socket socket,
String target,
int port,
Object attachment,
HttpContext context) throws IOException {
return createLayeredSocket(socket, target, port, context);
}
}

View File

@ -38,6 +38,7 @@ import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSession;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.FutureCallback;
@ -109,17 +110,23 @@ abstract class AbstractClientTlsStrategy implements TlsStrategy {
final FutureCallback<TransportSecurityLayer> callback) {
tlsSession.startTls(sslContext, endpoint, sslBufferManagement, (e, sslEngine) -> {
final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
(HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
final HttpVersionPolicy versionPolicy = tlsConfig.getHttpVersionPolicy();
final SSLParameters sslParameters = sslEngine.getSSLParameters();
final String[] supportedProtocols = tlsConfig.getSupportedProtocols();
if (supportedProtocols != null) {
sslParameters.setProtocols(supportedProtocols);
} else if (this.supportedProtocols != null) {
sslParameters.setProtocols(this.supportedProtocols);
} else if (versionPolicy != HttpVersionPolicy.FORCE_HTTP_1) {
sslParameters.setProtocols(TLS.excludeWeak(sslParameters.getProtocols()));
}
final String[] supportedCipherSuites = tlsConfig.getSupportedCipherSuites();
if (supportedCipherSuites != null) {
sslParameters.setCipherSuites(supportedCipherSuites);
} else if (this.supportedCipherSuites != null) {
sslParameters.setCipherSuites(this.supportedCipherSuites);
} else if (versionPolicy == HttpVersionPolicy.FORCE_HTTP_2) {
sslParameters.setCipherSuites(TlsCiphers.excludeH2Blacklisted(sslParameters.getCipherSuites()));
}
@ -135,6 +142,7 @@ abstract class AbstractClientTlsStrategy implements TlsStrategy {
if (LOG.isDebugEnabled()) {
LOG.debug("Enabled protocols: {}", Arrays.asList(sslEngine.getEnabledProtocols()));
LOG.debug("Enabled cipher suites:{}", Arrays.asList(sslEngine.getEnabledCipherSuites()));
LOG.debug("Starting handshake ({})", handshakeTimeout);
}
}, (e, sslEngine) -> {
verifySession(endpoint.getHostName(), sslEngine.getSession());

View File

@ -47,6 +47,7 @@ import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
@ -60,6 +61,7 @@ import org.apache.hc.core5.ssl.SSLInitializationException;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.Asserts;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -200,6 +202,19 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
final InetSocketAddress remoteAddress,
final InetSocketAddress localAddress,
final HttpContext context) throws IOException {
final Timeout timeout = connectTimeout != null ? Timeout.of(connectTimeout.getDuration(), connectTimeout.getTimeUnit()) : null;
return connectSocket(socket, host, remoteAddress, localAddress, timeout, timeout, context);
}
@Override
public Socket connectSocket(
final Socket socket,
final HttpHost host,
final InetSocketAddress remoteAddress,
final InetSocketAddress localAddress,
final Timeout connectTimeout,
final Object attachment,
final HttpContext context) throws IOException {
Args.notNull(host, "HTTP host");
Args.notNull(remoteAddress, "Remote address");
final Socket sock = socket != null ? socket : createSocket(context);
@ -214,7 +229,7 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
// only to this library
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
sock.connect(remoteAddress, connectTimeout != null ? connectTimeout.toMillisecondsIntBound() : 0);
sock.connect(remoteAddress, Timeout.defaultsToDisabled(connectTimeout).toMillisecondsIntBound());
return null;
});
} catch (final PrivilegedActionException e) {
@ -230,12 +245,10 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
// Setup SSL layering if necessary
if (sock instanceof SSLSocket) {
final SSLSocket sslsock = (SSLSocket) sock;
LOG.debug("Starting handshake");
sslsock.startHandshake();
verifyHostname(sslsock, host.getHostName());
executeHandshake(sslsock, host.getHostName(), attachment);
return sock;
}
return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), context);
return createLayeredSocket(sock, host.getHostName(), remoteAddress.getPort(), attachment, context);
}
@Override
@ -244,11 +257,27 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
final String target,
final int port,
final HttpContext context) throws IOException {
return createLayeredSocket(socket, target, port, context);
}
@Override
public Socket createLayeredSocket(
final Socket socket,
final String target,
final int port,
final Object attachment,
final HttpContext context) throws IOException {
final SSLSocket sslsock = (SSLSocket) this.socketFactory.createSocket(
socket,
target,
port,
true);
executeHandshake(sslsock, target, attachment);
return sslsock;
}
private void executeHandshake(final SSLSocket sslsock, final String target, final Object attachment) throws IOException {
final TlsConfig tlsConfig = attachment instanceof TlsConfig ? (TlsConfig) attachment : TlsConfig.DEFAULT;
if (supportedProtocols != null) {
sslsock.setEnabledProtocols(supportedProtocols);
} else {
@ -259,17 +288,20 @@ public class SSLConnectionSocketFactory implements LayeredConnectionSocketFactor
} else {
sslsock.setEnabledCipherSuites(TlsCiphers.excludeWeak(sslsock.getEnabledCipherSuites()));
}
final Timeout handshakeTimeout = tlsConfig.getHandshakeTimeout();
if (handshakeTimeout != null) {
sslsock.setSoTimeout(handshakeTimeout.toMillisecondsIntBound());
}
prepareSocket(sslsock);
if (LOG.isDebugEnabled()) {
LOG.debug("Enabled protocols: {}", (Object) sslsock.getEnabledProtocols());
LOG.debug("Enabled cipher suites: {}", (Object) sslsock.getEnabledCipherSuites());
LOG.debug("Starting handshake ({})", handshakeTimeout);
}
prepareSocket(sslsock);
LOG.debug("Starting handshake");
sslsock.startHandshake();
verifyHostname(sslsock, target);
return sslsock;
}
private void verifyHostname(final SSLSocket sslsock, final String hostname) throws IOException {

View File

@ -52,11 +52,9 @@ import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
import org.apache.hc.core5.http.nio.support.BasicResponseConsumer;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
/**
* This example demonstrates a full-duplex, streaming HTTP/1.1 message exchange.
@ -65,15 +63,10 @@ public class AsyncClientFullDuplexExchange {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
HttpVersionPolicy.NEGOTIATE,
H2Config.DEFAULT,
Http1Config.DEFAULT,
ioReactorConfig);
IOReactorConfig.DEFAULT);
client.start();

View File

@ -32,13 +32,16 @@ import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.message.BasicHttpRequest;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.http.nio.AsyncClientExchangeHandler;
@ -55,7 +58,6 @@ import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
/**
* This example demonstrates a full-duplex, streaming HTTP/2 message exchange.
@ -64,12 +66,15 @@ public class AsyncClientH2FullDuplexExchange {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, null, ioReactorConfig);
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.DEFAULT,
PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
.build())
.build());
client.start();

View File

@ -35,17 +35,19 @@ import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
/**
* This example demonstrates concurrent (multiplexed) execution of multiple
@ -55,12 +57,15 @@ public class AsyncClientH2Multiplexing {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, null, ioReactorConfig);
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.DEFAULT,
PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
.build())
.build());
client.start();

View File

@ -33,8 +33,10 @@ import java.util.concurrent.Future;
import org.apache.hc.client5.http.async.methods.AbstractBinPushConsumer;
import org.apache.hc.client5.http.async.methods.AbstractCharResponseConsumer;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
@ -46,8 +48,6 @@ import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
/**
* This example demonstrates handling of HTTP/2 message exchanges pushed by the server.
@ -56,18 +56,15 @@ public class AsyncClientH2ServerPush {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final H2Config h2Config = H2Config.custom()
.setPushEnabled(true)
.build();
final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setIOReactorConfig(ioReactorConfig)
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
.setH2Config(h2Config)
.setH2Config(H2Config.custom()
.setPushEnabled(true)
.build())
.setConnectionManager(PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_2)
.build())
.build())
.build();
client.start();

View File

@ -35,17 +35,19 @@ import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.async.methods.SimpleRequestProducer;
import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.impl.async.HttpAsyncClients;
import org.apache.hc.client5.http.impl.async.MinimalHttpAsyncClient;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
/**
* This example demonstrates pipelined execution of multiple HTTP/1.1 message exchanges.
@ -54,12 +56,15 @@ public class AsyncClientHttp1Pipelining {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
HttpVersionPolicy.FORCE_HTTP_1, null, Http1Config.DEFAULT, ioReactorConfig);
H2Config.DEFAULT,
Http1Config.DEFAULT,
IOReactorConfig.DEFAULT,
PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultTlsConfig(TlsConfig.custom()
.setVersionPolicy(HttpVersionPolicy.FORCE_HTTP_1)
.build())
.build());
client.start();

View File

@ -45,7 +45,6 @@ import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.message.StatusLine;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
/**
@ -74,7 +73,6 @@ public class AsyncClientTlsAlpn {
.setTlsStrategy(tlsStrategy)
.build();
try (final CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
.setConnectionManager(cm)
.build()) {

View File

@ -40,13 +40,11 @@ import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.Message;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.nio.support.BasicRequestProducer;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactive.ReactiveEntityProducer;
import org.apache.hc.core5.reactive.ReactiveResponseConsumer;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.util.Timeout;
import org.reactivestreams.Publisher;
import io.reactivex.Flowable;
@ -59,15 +57,10 @@ public class ReactiveClientFullDuplexExchange {
public static void main(final String[] args) throws Exception {
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom()
.setSoTimeout(Timeout.ofSeconds(5))
.build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(
HttpVersionPolicy.NEGOTIATE,
H2Config.DEFAULT,
Http1Config.DEFAULT,
ioReactorConfig);
IOReactorConfig.DEFAULT);
client.start();

View File

@ -30,10 +30,13 @@ package org.apache.hc.client5.http.impl.io;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
import org.apache.hc.client5.http.io.LeaseRequest;
import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
@ -361,26 +364,55 @@ public class TestBasicHttpClientConnectionManager {
mgr.setSocketConfig(sconfig);
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setConnectTimeout(234, TimeUnit.MILLISECONDS)
.build();
mgr.setConnectionConfig(connectionConfig);
final TlsConfig tlsConfig = TlsConfig.custom()
.setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
.build();
mgr.setTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[] {remote});
Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory);
Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket);
Mockito.when(plainSocketFactory.connectSocket(
Mockito.any(),
Mockito.eq(socket),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context);
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), socket, target,
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
socket,
target,
new InetSocketAddress(remote, 8443),
new InetSocketAddress(local, 0), context);
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(234),
tlsConfig,
context);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(context);
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
socket,
target,
new InetSocketAddress(remote, 8443),
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(123),
tlsConfig,
context);
}
@Test
@ -402,6 +434,15 @@ public class TestBasicHttpClientConnectionManager {
mgr.setSocketConfig(sconfig);
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setConnectTimeout(234, TimeUnit.MILLISECONDS)
.build();
mgr.setConnectionConfig(connectionConfig);
final TlsConfig tlsConfig = TlsConfig.custom()
.setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
.build();
mgr.setTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
@ -409,21 +450,27 @@ public class TestBasicHttpClientConnectionManager {
Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslSocketFactory);
Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket);
Mockito.when(plainSocketFactory.connectSocket(
Mockito.any(),
Mockito.eq(socket),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context);
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), socket, proxy,
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
socket,
proxy,
new InetSocketAddress(remote, 8080),
new InetSocketAddress(local, 0), context);
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(234),
tlsConfig,
context);
Mockito.when(conn.getSocket()).thenReturn(socket);
@ -431,7 +478,7 @@ public class TestBasicHttpClientConnectionManager {
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
Mockito.verify(sslSocketFactory, Mockito.times(1)).createLayeredSocket(
socket, "somehost", 8443, context);
socket, "somehost", 8443, tlsConfig, context);
}
}

View File

@ -39,6 +39,7 @@ import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpHostConnectException;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.UnsupportedSchemeException;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
import org.apache.hc.client5.http.socket.ConnectionSocketFactory;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
@ -48,6 +49,7 @@ import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.protocol.BasicHttpContext;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@ -96,6 +98,7 @@ public class TestHttpClientConnectionOperator {
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
final SocketConfig socketConfig = SocketConfig.custom()
@ -105,8 +108,11 @@ public class TestHttpClientConnectionOperator {
.setTcpNoDelay(true)
.setSoLinger(50, TimeUnit.MILLISECONDS)
.build();
final TlsConfig tlsConfig = TlsConfig.custom()
.build();
final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), socketConfig, context);
connectionOperator.connect(conn, host, localAddress,
Timeout.ofMilliseconds(123), socketConfig, tlsConfig, context);
Mockito.verify(socket).setKeepAlive(true);
Mockito.verify(socket).setReuseAddress(true);
@ -115,11 +121,12 @@ public class TestHttpClientConnectionOperator {
Mockito.verify(socket).setTcpNoDelay(true);
Mockito.verify(plainSocketFactory).connectSocket(
TimeValue.ofMilliseconds(1000),
socket,
host,
new InetSocketAddress(ip1, 80),
localAddress,
Timeout.ofMilliseconds(123),
tlsConfig,
context);
Mockito.verify(conn, Mockito.times(2)).bind(socket);
}
@ -141,6 +148,7 @@ public class TestHttpClientConnectionOperator {
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenThrow(new SocketTimeoutException());
Assert.assertThrows(ConnectTimeoutException.class, () ->
@ -165,6 +173,7 @@ public class TestHttpClientConnectionOperator {
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenThrow(new ConnectException());
Assert.assertThrows(HttpHostConnectException.class, () ->
@ -185,29 +194,35 @@ public class TestHttpClientConnectionOperator {
Mockito.when(schemePortResolver.resolve(host)).thenReturn(80);
Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket);
Mockito.when(plainSocketFactory.connectSocket(
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.eq(new InetSocketAddress(ip1, 80)),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenThrow(new ConnectException());
Mockito.when(plainSocketFactory.connectSocket(
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.eq(new InetSocketAddress(ip2, 80)),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
final TlsConfig tlsConfig = TlsConfig.custom()
.build();
connectionOperator.connect(conn, host, localAddress,
Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
Mockito.verify(plainSocketFactory).connectSocket(
TimeValue.ofMilliseconds(1000),
socket,
host,
new InetSocketAddress(ip2, 80),
localAddress,
Timeout.ofMilliseconds(123),
tlsConfig,
context);
Mockito.verify(conn, Mockito.times(3)).bind(socket);
}
@ -228,17 +243,22 @@ public class TestHttpClientConnectionOperator {
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
final InetSocketAddress localAddress = new InetSocketAddress(local, 0);
connectionOperator.connect(conn, host, localAddress, TimeValue.ofMilliseconds(1000), SocketConfig.DEFAULT, context);
final TlsConfig tlsConfig = TlsConfig.custom()
.build();
connectionOperator.connect(conn, host, localAddress,
Timeout.ofMilliseconds(123), SocketConfig.DEFAULT, tlsConfig, context);
Mockito.verify(plainSocketFactory).connectSocket(
TimeValue.ofMilliseconds(1000),
socket,
host,
new InetSocketAddress(ip, 80),
localAddress,
Timeout.ofMilliseconds(123),
tlsConfig,
context);
Mockito.verify(dnsResolver, Mockito.never()).resolve(Mockito.anyString());
Mockito.verify(conn, Mockito.times(2)).bind(socket);
@ -258,9 +278,10 @@ public class TestHttpClientConnectionOperator {
Mockito.any(),
Mockito.eq("somehost"),
Mockito.eq(443),
Mockito.eq(Timeout.ofMilliseconds(345)),
Mockito.any())).thenReturn(socket);
connectionOperator.upgrade(conn, host, context);
connectionOperator.upgrade(conn, host, Timeout.ofMilliseconds(345), context);
Mockito.verify(conn).bind(socket);
}

View File

@ -37,6 +37,8 @@ import java.util.concurrent.TimeoutException;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.TlsConfig;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
import org.apache.hc.client5.http.io.LeaseRequest;
import org.apache.hc.client5.http.io.ManagedHttpClientConnection;
@ -247,26 +249,55 @@ public class TestPoolingHttpClientConnectionManager {
mgr.setDefaultSocketConfig(sconfig);
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setConnectTimeout(234, TimeUnit.MILLISECONDS)
.build();
mgr.setDefaultConnectionConfig(connectionConfig);
final TlsConfig tlsConfig = TlsConfig.custom()
.setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
.build();
mgr.setDefaultTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("somehost")).thenReturn(new InetAddress[]{remote});
Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(plainSocketFactory);
Mockito.when(plainSocketFactory.createSocket(Mockito.any())).thenReturn(socket);
Mockito.when(plainSocketFactory.connectSocket(
Mockito.any(),
Mockito.eq(socket),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(socket);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("somehost");
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
Mockito.verify(plainSocketFactory, Mockito.times(1)).createSocket(context);
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), socket, target,
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
socket,
target,
new InetSocketAddress(remote, 8443),
new InetSocketAddress(local, 0), context);
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(234),
tlsConfig,
context);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
Mockito.verify(dnsResolver, Mockito.times(2)).resolve("somehost");
Mockito.verify(schemePortResolver, Mockito.times(2)).resolve(target);
Mockito.verify(plainSocketFactory, Mockito.times(2)).createSocket(context);
Mockito.verify(plainSocketFactory, Mockito.times(1)).connectSocket(
socket,
target,
new InetSocketAddress(remote, 8443),
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(123),
tlsConfig,
context);
}
@Test
@ -301,6 +332,15 @@ public class TestPoolingHttpClientConnectionManager {
mgr.setDefaultSocketConfig(sconfig);
final ConnectionConfig connectionConfig = ConnectionConfig.custom()
.setConnectTimeout(234, TimeUnit.MILLISECONDS)
.build();
mgr.setDefaultConnectionConfig(connectionConfig);
final TlsConfig tlsConfig = TlsConfig.custom()
.setHandshakeTimeout(345, TimeUnit.MILLISECONDS)
.build();
mgr.setDefaultTlsConfig(tlsConfig);
Mockito.when(dnsResolver.resolve("someproxy")).thenReturn(new InetAddress[] {remote});
Mockito.when(schemePortResolver.resolve(proxy)).thenReturn(8080);
Mockito.when(schemePortResolver.resolve(target)).thenReturn(8443);
@ -308,21 +348,27 @@ public class TestPoolingHttpClientConnectionManager {
Mockito.when(socketFactoryRegistry.lookup("https")).thenReturn(sslsf);
Mockito.when(plainsf.createSocket(Mockito.any())).thenReturn(mockSock);
Mockito.when(plainsf.connectSocket(
Mockito.any(),
Mockito.eq(mockSock),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any(),
Mockito.any())).thenReturn(mockSock);
mgr.connect(endpoint1, TimeValue.ofMilliseconds(123), context);
mgr.connect(endpoint1, null, context);
Mockito.verify(dnsResolver, Mockito.times(1)).resolve("someproxy");
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(proxy);
Mockito.verify(plainsf, Mockito.times(1)).createSocket(context);
Mockito.verify(plainsf, Mockito.times(1)).connectSocket(TimeValue.ofMilliseconds(123), mockSock, proxy,
Mockito.verify(plainsf, Mockito.times(1)).connectSocket(
mockSock,
proxy,
new InetSocketAddress(remote, 8080),
new InetSocketAddress(local, 0), context);
new InetSocketAddress(local, 0),
Timeout.ofMilliseconds(234),
tlsConfig,
context);
Mockito.when(conn.isOpen()).thenReturn(true);
Mockito.when(conn.getSocket()).thenReturn(mockSock);
@ -331,7 +377,7 @@ public class TestPoolingHttpClientConnectionManager {
Mockito.verify(schemePortResolver, Mockito.times(1)).resolve(target);
Mockito.verify(sslsf, Mockito.times(1)).createLayeredSocket(
mockSock, "somehost", 8443, context);
mockSock, "somehost", 8443, tlsConfig, context);
}
}