Moved connection management related settings from RequestConfig to new class ConnectionMgmtConfig

This commit is contained in:
Oleg Kalnichevski 2021-05-11 21:26:11 +02:00
parent f5d3c14afd
commit f36637dc2f
25 changed files with 685 additions and 109 deletions

View File

@ -30,6 +30,7 @@ package org.apache.hc.client5.testing.async;
import java.net.InetSocketAddress;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -69,6 +70,10 @@ public abstract class AbstractHttp1IntegrationTestBase extends AbstractServerTes
@Override
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.build();
}
@ -91,7 +96,6 @@ public abstract class AbstractHttp1IntegrationTestBase extends AbstractServerTes
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -29,6 +29,7 @@ package org.apache.hc.client5.testing.async;
import java.util.Arrays;
import java.util.Collection;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
@ -63,7 +64,10 @@ public class TestH2Async extends AbstractHttpAsyncFundamentalsTest<CloseableHttp
clientBuilder = H2AsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.setTlsStrategy(new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext()));
}

View File

@ -29,6 +29,7 @@ package org.apache.hc.client5.testing.async;
import java.util.Arrays;
import java.util.Collection;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
@ -66,7 +67,10 @@ public class TestH2AsyncRedirect extends AbstractHttpAsyncRedirectsTest<Closeabl
clientBuilder = H2AsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()));
}

View File

@ -31,6 +31,7 @@ import java.util.Collection;
import org.apache.hc.client5.http.AuthenticationStrategy;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
@ -67,7 +68,10 @@ public class TestH2ClientAuthentication extends AbstractHttpAsyncClientAuthentic
clientBuilder = H2AsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()));
}

View File

@ -29,6 +29,7 @@ package org.apache.hc.client5.testing.async;
import java.util.Arrays;
import java.util.Collection;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.H2AsyncClientBuilder;
@ -63,7 +64,10 @@ public class TestH2Reactive extends AbstractHttpReactiveFundamentalsTest<Closeab
clientBuilder = H2AsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.setTlsStrategy(new BasicClientTlsStrategy(SSLTestContexts.createClientSSLContext()));
}

View File

@ -32,6 +32,7 @@ import java.util.concurrent.Future;
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.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -74,6 +75,10 @@ public class TestHttp1Async extends AbstractHttpAsyncFundamentalsTest<CloseableH
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.build();
}
@ -95,7 +100,6 @@ public class TestHttp1Async extends AbstractHttpAsyncFundamentalsTest<CloseableH
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -34,6 +34,7 @@ import java.util.concurrent.Future;
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.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -88,6 +89,10 @@ public class TestHttp1AsyncRedirects extends AbstractHttpAsyncRedirectsTest<Clos
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.build();
}
@ -109,7 +114,6 @@ public class TestHttp1AsyncRedirects extends AbstractHttpAsyncRedirectsTest<Clos
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -32,6 +32,7 @@ import org.apache.hc.client5.http.UserTokenHandler;
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.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -68,6 +69,10 @@ public class TestHttp1AsyncStatefulConnManagement extends AbstractIntegrationTes
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.build();
}
@ -88,7 +93,6 @@ public class TestHttp1AsyncStatefulConnManagement extends AbstractIntegrationTes
protected void before() throws Throwable {
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(TIMEOUT)
.setConnectionRequestTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);

View File

@ -37,6 +37,7 @@ import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.auth.AuthSchemeFactory;
import org.apache.hc.client5.http.auth.AuthScope;
import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -84,6 +85,10 @@ public class TestHttp1ClientAuthentication extends AbstractHttpAsyncClientAuthen
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.build();
}
@ -105,7 +110,6 @@ public class TestHttp1ClientAuthentication extends AbstractHttpAsyncClientAuthen
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -32,6 +32,7 @@ import java.util.Collection;
import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
import org.apache.hc.client5.http.impl.async.HttpAsyncClientBuilder;
@ -80,6 +81,10 @@ public class TestHttp1Reactive extends AbstractHttpReactiveFundamentalsTest<Clos
protected void before() throws Throwable {
connManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new DefaultClientTlsStrategy(SSLTestContexts.createClientSSLContext()))
.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.setSocketTimeout(TIMEOUT)
.build())
.build();
}
@ -101,7 +106,6 @@ public class TestHttp1Reactive extends AbstractHttpReactiveFundamentalsTest<Clos
clientBuilder = HttpAsyncClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -29,6 +29,7 @@ package org.apache.hc.client5.testing.sync;
import java.io.IOException;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
@ -110,10 +111,13 @@ public abstract class LocalServerTestBase {
connManager.setDefaultSocketConfig(SocketConfig.custom()
.setSoTimeout(TIMEOUT)
.build());
connManager.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(TIMEOUT)
.build());
clientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectionRequestTimeout(TIMEOUT)
.setConnectTimeout(TIMEOUT)
.build())
.setConnectionManager(connManager);
}

View File

@ -0,0 +1,205 @@
/*
* ====================================================================
* 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.concurrent.TimeUnit;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
/**
* Immutable class encapsulating connection initialization and management settings.
*
* @since 5.2
*/
@Contract(threading = ThreadingBehavior.IMMUTABLE)
public class ConnectionConfig implements Cloneable {
private static final Timeout DEFAULT_CONNECT_TIMEOUT = Timeout.ofMinutes(3);
public static final ConnectionConfig DEFAULT = new Builder().build();
private final Timeout connectTimeout;
private final Timeout socketTimeout;
private final TimeValue validateAfterInactivity;
/**
* Intended for CDI compatibility
*/
protected ConnectionConfig() {
this(DEFAULT_CONNECT_TIMEOUT, null, null);
}
ConnectionConfig(
final Timeout connectTimeout,
final Timeout socketTimeout,
final TimeValue validateAfterInactivity) {
super();
this.connectTimeout = connectTimeout;
this.socketTimeout = socketTimeout;
this.validateAfterInactivity = validateAfterInactivity;
}
/**
* @see Builder#setSocketTimeout(Timeout)
*/
public Timeout getSocketTimeout() {
return socketTimeout;
}
/**
* @see Builder#setConnectTimeout(Timeout)
*/
public Timeout getConnectTimeout() {
return connectTimeout;
}
/**
* @see Builder#setValidateAfterInactivity(TimeValue)
*/
public TimeValue getValidateAfterInactivity() {
return validateAfterInactivity;
}
@Override
protected ConnectionConfig clone() throws CloneNotSupportedException {
return (ConnectionConfig) super.clone();
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("[");
builder.append(", connectTimeout=").append(connectTimeout);
builder.append(", socketTimeout=").append(socketTimeout);
builder.append(", validateAfterInactivity=").append(validateAfterInactivity);
builder.append("]");
return builder.toString();
}
public static ConnectionConfig.Builder custom() {
return new Builder();
}
public static ConnectionConfig.Builder copy(final ConnectionConfig config) {
return new Builder()
.setConnectTimeout(config.getConnectTimeout())
.setSocketTimeout(config.getSocketTimeout())
.setValidateAfterInactivity(config.getValidateAfterInactivity());
}
public static class Builder {
private Timeout socketTimeout;
private Timeout connectTimeout;
private TimeValue validateAfterInactivity;
Builder() {
super();
this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
}
/**
* @see #setSocketTimeout(Timeout)
*/
public Builder setSocketTimeout(final int soTimeout, final TimeUnit timeUnit) {
this.socketTimeout = Timeout.of(soTimeout, timeUnit);
return this;
}
/**
* Determines the default socket timeout value for I/O operations.
* <p>
* Default: {@code null}
* </p>
*
* @return the default socket timeout value for I/O operations.
*/
public Builder setSocketTimeout(final Timeout soTimeout) {
this.socketTimeout = soTimeout;
return this;
}
/**
* 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>
* <p>
* Default: 3 minutes
* </p>
*/
public Builder setConnectTimeout(final Timeout connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
}
/**
* @see #setConnectTimeout(Timeout)
*/
public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) {
this.connectTimeout = Timeout.of(connectTimeout, timeUnit);
return this;
}
/**
* Defines period of inactivity after which persistent connections must
* be re-validated prior to being leased to the consumer. Negative values passed
* to this method disable connection validation.
* <p>
* Default: {@code null}
* </p>
*/
public Builder setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
this.validateAfterInactivity = validateAfterInactivity;
return this;
}
/**
* @see #setValidateAfterInactivity(TimeValue)
*/
public Builder setValidateAfterInactivity(final long validateAfterInactivity, final TimeUnit timeUnit) {
this.validateAfterInactivity = TimeValue.of(validateAfterInactivity, timeUnit);
return this;
}
public ConnectionConfig build() {
return new ConnectionConfig(
connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
socketTimeout,
validateAfterInactivity);
}
}
}

View File

@ -43,7 +43,6 @@ import org.apache.hc.core5.util.Timeout;
public class RequestConfig implements Cloneable {
private static final Timeout DEFAULT_CONNECTION_REQUEST_TIMEOUT = Timeout.ofMinutes(3);
private static final Timeout DEFAULT_CONNECT_TIMEOUT = Timeout.ofMinutes(3);
private static final TimeValue DEFAULT_CONN_KEEP_ALIVE = TimeValue.ofMinutes(3);
public static final RequestConfig DEFAULT = new Builder().build();
@ -69,7 +68,7 @@ public class RequestConfig implements Cloneable {
*/
protected RequestConfig() {
this(false, null, null, false, false, 0, false, null, null,
DEFAULT_CONNECTION_REQUEST_TIMEOUT, DEFAULT_CONNECT_TIMEOUT, null, DEFAULT_CONN_KEEP_ALIVE, false, false);
DEFAULT_CONNECTION_REQUEST_TIMEOUT, null, null, DEFAULT_CONN_KEEP_ALIVE, false, false);
}
RequestConfig(
@ -115,7 +114,11 @@ public class RequestConfig implements Cloneable {
/**
* @see Builder#setProxy(HttpHost)
*
* @deprecated Use {@link org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner}
* or a custom {@link org.apache.hc.client5.http.routing.HttpRoutePlanner}.
*/
@Deprecated
public HttpHost getProxy() {
return proxy;
}
@ -178,7 +181,10 @@ public class RequestConfig implements Cloneable {
/**
* @see Builder#setConnectTimeout(Timeout)
*
* @deprecated Use {@link ConnectionConfig#getConnectTimeout()}.
*/
@Deprecated
public Timeout getConnectTimeout() {
return connectTimeout;
}
@ -286,7 +292,6 @@ public class RequestConfig implements Cloneable {
this.maxRedirects = 50;
this.authenticationEnabled = true;
this.connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
this.connectTimeout = DEFAULT_CONNECT_TIMEOUT;
this.contentCompressionEnabled = true;
this.hardCancellationEnabled = true;
}
@ -323,7 +328,11 @@ public class RequestConfig implements Cloneable {
* <p>
* Default: {@code null}
* </p>
*
* @deprecated Use {@link org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner}
* or a custom {@link org.apache.hc.client5.http.routing.HttpRoutePlanner}.
*/
@Deprecated
public Builder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
@ -444,7 +453,10 @@ public class RequestConfig implements Cloneable {
* <p>
* Default: 3 minutes
* </p>
*
* @deprecated Use {@link ConnectionConfig.Builder#setConnectTimeout(Timeout)}.
*/
@Deprecated
public Builder setConnectTimeout(final Timeout connectTimeout) {
this.connectTimeout = connectTimeout;
return this;
@ -452,7 +464,10 @@ public class RequestConfig implements Cloneable {
/**
* @see #setConnectTimeout(Timeout)
*
* @deprecated Use {@link ConnectionConfig.Builder#setConnectTimeout(long, TimeUnit)}.
*/
@Deprecated
public Builder setConnectTimeout(final long connectTimeout, final TimeUnit timeUnit) {
this.connectTimeout = Timeout.of(connectTimeout, timeUnit);
return this;
@ -570,7 +585,7 @@ public class RequestConfig implements Cloneable {
targetPreferredAuthSchemes,
proxyPreferredAuthSchemes,
connectionRequestTimeout != null ? connectionRequestTimeout : DEFAULT_CONNECTION_REQUEST_TIMEOUT,
connectTimeout != null ? connectTimeout : DEFAULT_CONNECT_TIMEOUT,
connectTimeout,
responseTimeout,
connectionKeepAlive != null ? connectionKeepAlive : DEFAULT_CONN_KEEP_ALIVE,
contentCompressionEnabled,

View File

@ -45,6 +45,7 @@ import org.apache.hc.client5.http.async.AsyncExecChainHandler;
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.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.CookieSpecFactory;
@ -74,7 +75,9 @@ import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.concurrent.DefaultThreadFactory;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.HttpResponseInterceptor;
import org.apache.hc.core5.http.config.CharCodingConfig;
@ -89,7 +92,6 @@ import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
import org.apache.hc.core5.http.protocol.RequestTargetHost;
import org.apache.hc.core5.http.protocol.RequestUserAgent;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.http2.nio.pool.H2ConnPool;
import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
import org.apache.hc.core5.http2.protocol.H2RequestContent;
import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
@ -189,6 +191,7 @@ public class H2AsyncClientBuilder {
private String userAgent;
private Collection<? extends Header> defaultHeaders;
private RequestConfig defaultRequestConfig;
private Resolver<HttpHost, ConnectionConfig> connectionConfigResolver;
private boolean evictIdleConnections;
private TimeValue maxIdleTime;
@ -500,6 +503,26 @@ public class H2AsyncClientBuilder {
return this;
}
/**
* Assigns {@link Resolver} for {@link ConnectionConfig} on a per host basis.
*
* @since 5.2
*/
public final H2AsyncClientBuilder setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
return this;
}
/**
* Assigns the same {@link ConnectionConfig} for all hosts.
*
* @since 5.2
*/
public final H2AsyncClientBuilder setDefaultConnectionConfig(final ConnectionConfig connectionConfig) {
this.connectionConfigResolver = (host) -> connectionConfig;
return this;
}
/**
* Use system properties when creating and configuring default
* implementations.
@ -784,7 +807,8 @@ public class H2AsyncClientBuilder {
}
final MultihomeConnectionInitiator connectionInitiator = new MultihomeConnectionInitiator(ioReactor, dnsResolver);
final H2ConnPool connPool = new H2ConnPool(connectionInitiator, host -> null, tlsStrategyCopy);
final InternalH2ConnPool connPool = new InternalH2ConnPool(connectionInitiator, host -> null, tlsStrategyCopy);
connPool.setConnectionConfigResolver(connectionConfigResolver);
List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
if (closeablesCopy == null) {
@ -821,7 +845,7 @@ public class H2AsyncClientBuilder {
private final Thread thread;
public IdleConnectionEvictor(final H2ConnPool connPool, final TimeValue maxIdleTime) {
public IdleConnectionEvictor(final InternalH2ConnPool connPool, final TimeValue maxIdleTime) {
this.thread = new DefaultThreadFactory("idle-connection-evictor", true).newThread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {

View File

@ -47,7 +47,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.nio.pool.H2ConnPool;
import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -68,14 +67,14 @@ public final class InternalH2AsyncClient extends InternalAbstractHttpAsyncClient
private static final Logger LOG = LoggerFactory.getLogger(InternalH2AsyncClient.class);
private final HttpRoutePlanner routePlanner;
private final H2ConnPool connPool;
private final InternalH2ConnPool connPool;
InternalH2AsyncClient(
final DefaultConnectingIOReactor ioReactor,
final AsyncExecChainElement execChain,
final AsyncPushConsumerRegistry pushConsumerRegistry,
final ThreadFactory threadFactory,
final H2ConnPool connPool,
final InternalH2ConnPool connPool,
final HttpRoutePlanner routePlanner,
final Lookup<CookieSpecFactory> cookieSpecRegistry,
final Lookup<AuthSchemeFactory> authSchemeRegistry,

View File

@ -44,7 +44,6 @@ 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.http.nio.command.RequestExecutionCommand;
import org.apache.hc.core5.http2.nio.pool.H2ConnPool;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.Command;
import org.apache.hc.core5.reactor.IOSession;
@ -56,14 +55,14 @@ import org.slf4j.Logger;
class InternalH2AsyncExecRuntime implements AsyncExecRuntime {
private final Logger log;
private final H2ConnPool connPool;
private final InternalH2ConnPool connPool;
private final HandlerFactory<AsyncPushConsumer> pushHandlerFactory;
private final AtomicReference<Endpoint> sessionRef;
private volatile boolean reusable;
InternalH2AsyncExecRuntime(
final Logger log,
final H2ConnPool connPool,
final InternalH2ConnPool connPool,
final HandlerFactory<AsyncPushConsumer> pushHandlerFactory) {
super();
this.log = log;
@ -91,9 +90,7 @@ class InternalH2AsyncExecRuntime implements AsyncExecRuntime {
if (log.isDebugEnabled()) {
log.debug("{} acquiring endpoint ({})", id, connectTimeout);
}
return Operations.cancellable(connPool.getSession(
target,
connectTimeout,
return Operations.cancellable(connPool.getSession(target, connectTimeout,
new FutureCallback<IOSession>() {
@Override

View File

@ -0,0 +1,102 @@
/*
* ====================================================================
* 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.impl.async;
import java.net.InetSocketAddress;
import java.util.concurrent.Future;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.core5.concurrent.CallbackContribution;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http2.nio.pool.H2ConnPool;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.io.ModalCloseable;
import org.apache.hc.core5.reactor.ConnectionInitiator;
import org.apache.hc.core5.reactor.IOSession;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
class InternalH2ConnPool implements ModalCloseable {
private final H2ConnPool connPool;
private volatile Resolver<HttpHost, ConnectionConfig> connectionConfigResolver;
InternalH2ConnPool(final ConnectionInitiator connectionInitiator,
final Resolver<HttpHost, InetSocketAddress> addressResolver,
final TlsStrategy tlsStrategy) {
this.connPool = new H2ConnPool(connectionInitiator, addressResolver, tlsStrategy);
}
public void close(final CloseMode closeMode) {
connPool.close(closeMode);
}
public void close() {
connPool.close();
}
private ConnectionConfig resolveConnectionConfig(final HttpHost httpHost) {
final Resolver<HttpHost, ConnectionConfig> resolver = this.connectionConfigResolver;
final ConnectionConfig connectionConfig = resolver != null ? resolver.resolve(httpHost) : null;
return connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
public Future<IOSession> getSession(
final HttpHost endpoint,
final Timeout connectTimeout,
final FutureCallback<IOSession> callback) {
final ConnectionConfig connectionConfig = resolveConnectionConfig(endpoint);
return connPool.getSession(
endpoint,
connectTimeout != null ? connectTimeout : connectionConfig.getConnectTimeout(),
new CallbackContribution<IOSession>(callback) {
@Override
public void completed(final IOSession ioSession) {
final Timeout socketTimeout = connectionConfig.getSocketTimeout();
if (socketTimeout != null) {
ioSession.setSocketTimeout(socketTimeout);
}
callback.completed(ioSession);
}
});
}
public void closeIdle(final TimeValue idleTime) {
connPool.closeIdle(idleTime);
}
public void setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
}
}

View File

@ -35,6 +35,7 @@ import java.util.concurrent.ThreadFactory;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.config.Configurable;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ExecSupport;
@ -46,6 +47,7 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.Cancellable;
import org.apache.hc.core5.concurrent.ComplexCancellable;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.EntityDetails;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
@ -61,7 +63,6 @@ import org.apache.hc.core5.http.nio.command.RequestExecutionCommand;
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http2.nio.pool.H2ConnPool;
import org.apache.hc.core5.io.CloseMode;
import org.apache.hc.core5.reactor.Command;
import org.apache.hc.core5.reactor.ConnectionInitiator;
@ -90,7 +91,7 @@ import org.slf4j.LoggerFactory;
public final class MinimalH2AsyncClient extends AbstractMinimalHttpAsyncClientBase {
private static final Logger LOG = LoggerFactory.getLogger(MinimalH2AsyncClient.class);
private final H2ConnPool connPool;
private final InternalH2ConnPool connPool;
private final ConnectionInitiator connectionInitiator;
MinimalH2AsyncClient(
@ -112,7 +113,7 @@ public final class MinimalH2AsyncClient extends AbstractMinimalHttpAsyncClientBa
pushConsumerRegistry,
threadFactory);
this.connectionInitiator = new MultihomeConnectionInitiator(getConnectionInitiator(), dnsResolver);
this.connPool = new H2ConnPool(this.connectionInitiator, object -> null, tlsStrategy);
this.connPool = new InternalH2ConnPool(this.connectionInitiator, object -> null, tlsStrategy);
}
@Override
@ -249,4 +250,13 @@ public final class MinimalH2AsyncClient extends AbstractMinimalHttpAsyncClientBa
return cancellable;
}
/**
* Sets {@link Resolver} for {@link ConnectionConfig} on a per host basis.
*
* @since 5.2
*/
public void setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
connPool.setConnectionConfigResolver(connectionConfigResolver);
}
}

View File

@ -38,6 +38,7 @@ import java.util.concurrent.atomic.AtomicReference;
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.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
@ -107,6 +108,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
private long expiry;
private boolean leased;
private SocketConfig socketConfig;
private ConnectionConfig connectionConfig;
private final AtomicBoolean closed;
@ -187,6 +189,20 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
}
/**
* @since 5.2
*/
public synchronized ConnectionConfig getConnectionConfig() {
return connectionConfig;
}
/**
* @since 5.2
*/
public synchronized void setConnectionConfig(final ConnectionConfig connectionConfig) {
this.connectionConfig = connectionConfig != null ? connectionConfig : ConnectionConfig.DEFAULT;
}
public LeaseRequest lease(final String id, final HttpRoute route, final Object state) {
return lease(id, route, Timeout.DISABLED, state);
}
@ -328,7 +344,7 @@ public class BasicHttpClientConnectionManager implements HttpClientConnectionMan
}
@Override
public void connect(final ConnectionEndpoint endpoint, final TimeValue connectTimeout, final HttpContext context) throws IOException {
public synchronized void connect(final ConnectionEndpoint endpoint, final TimeValue timeout, final HttpContext context) throws IOException {
Args.notNull(endpoint, "Endpoint");
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
@ -342,17 +358,24 @@ 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 ManagedHttpClientConnection connection = internalEndpoint.getConnection();
this.connectionOperator.connect(
internalEndpoint.getConnection(),
connection,
host,
route.getLocalSocketAddress(),
connectTimeout,
this.socketConfig,
context);
final Timeout socketTimeout = config.getSocketTimeout();
if (socketTimeout != null) {
connection.setSocketTimeout(socketTimeout);
}
}
@Override
public void upgrade(
public synchronized void upgrade(
final ConnectionEndpoint endpoint,
final HttpContext context) throws IOException {
Args.notNull(endpoint, "Endpoint");

View File

@ -39,6 +39,7 @@ import java.util.concurrent.atomic.AtomicReference;
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.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.io.ConnectionEndpoint;
@ -52,6 +53,7 @@ import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory;
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.HttpException;
@ -112,8 +114,8 @@ public class PoolingHttpClientConnectionManager
private final HttpConnectionFactory<ManagedHttpClientConnection> connFactory;
private final AtomicBoolean closed;
private volatile SocketConfig defaultSocketConfig;
private volatile TimeValue validateAfterInactivity;
private volatile Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
public PoolingHttpClientConnectionManager() {
this(RegistryBuilder.<ConnectionSocketFactory>create()
@ -203,7 +205,6 @@ public class PoolingHttpClientConnectionManager
}
this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
this.closed = new AtomicBoolean(false);
this.validateAfterInactivity = TimeValue.ofSeconds(2L);
}
@Internal
@ -241,6 +242,23 @@ public class PoolingHttpClientConnectionManager
throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass());
}
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 TimeValue resolveValidateAfterInactivity(final ConnectionConfig connectionConfig) {
final TimeValue timeValue = connectionConfig.getValidateAfterInactivity();
return timeValue != null ? timeValue : TimeValue.ofSeconds(2);
}
public LeaseRequest lease(final String id, final HttpRoute route, final Object state) {
return lease(id, route, Timeout.DISABLED, state);
}
@ -280,12 +298,13 @@ public class PoolingHttpClientConnectionManager
if (LOG.isDebugEnabled()) {
LOG.debug("{} endpoint leased {}", id, ConnPoolSupport.formatStats(route, state, pool));
}
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final TimeValue timeValue = resolveValidateAfterInactivity(connectionConfig);
try {
final TimeValue validateAfterInactivitySnapshot = validateAfterInactivity;
if (TimeValue.isNonNegative(validateAfterInactivitySnapshot)) {
if (TimeValue.isNonNegative(timeValue)) {
final ManagedHttpClientConnection conn = poolEntry.getConnection();
if (conn != null
&& poolEntry.getUpdated() + validateAfterInactivitySnapshot.toMilliseconds() <= System.currentTimeMillis()) {
&& poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
boolean stale;
try {
stale = conn.isStale();
@ -382,7 +401,7 @@ public class PoolingHttpClientConnectionManager
}
@Override
public void connect(final ConnectionEndpoint endpoint, final TimeValue connectTimeout, final HttpContext context) throws IOException {
public void connect(final ConnectionEndpoint endpoint, final TimeValue timeout, final HttpContext context) throws IOException {
Args.notNull(endpoint, "Managed endpoint");
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
if (internalEndpoint.isConnected()) {
@ -399,21 +418,27 @@ public class PoolingHttpClientConnectionManager
} else {
host = route.getTargetHost();
}
final SocketConfig socketConfig = resolveSocketConfig(route);
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final TimeValue connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
if (LOG.isDebugEnabled()) {
LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
}
final ManagedHttpClientConnection conn = poolEntry.getConnection();
final SocketConfig defaultSocketConfigSnapshot = defaultSocketConfig;
this.connectionOperator.connect(
conn,
host,
route.getLocalSocketAddress(),
connectTimeout,
defaultSocketConfigSnapshot != null ? defaultSocketConfigSnapshot : SocketConfig.DEFAULT,
timeout,
socketConfig,
context);
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(conn));
}
final Timeout socketTimeout = connectionConfig.getSocketTimeout();
if (socketTimeout != null) {
conn.setSocketTimeout(socketTimeout);
}
}
@Override
@ -485,21 +510,56 @@ public class PoolingHttpClientConnectionManager
return this.pool.getStats(route);
}
public SocketConfig getDefaultSocketConfig() {
return this.defaultSocketConfig;
}
public void setDefaultSocketConfig(final SocketConfig defaultSocketConfig) {
this.defaultSocketConfig = defaultSocketConfig;
/**
* Sets the same {@link SocketConfig} for all routes
*/
public void setDefaultSocketConfig(final SocketConfig config) {
this.socketConfigResolver = (route) -> config;
}
/**
* @see #setValidateAfterInactivity(TimeValue)
* Sets {@link Resolver} of {@link SocketConfig} on a per route basis.
*
* @since 4.4
* @since 5.2
*/
public void setSocketConfigResolver(final Resolver<HttpRoute, SocketConfig> socketConfigResolver) {
this.socketConfigResolver = socketConfigResolver;
}
/**
* Sets the same {@link ConnectionConfig} for all routes
*
* @since 5.2
*/
public void setDefaultConnectionConfig(final ConnectionConfig config) {
this.connectionConfigResolver = (route) -> config;
}
/**
* Sets {@link Resolver} of {@link ConnectionConfig} on a per route basis.
*
* @since 5.2
*/
public void setConnectionConfigResolver(final Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
}
/**
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
*/
@Deprecated
public SocketConfig getDefaultSocketConfig() {
return SocketConfig.DEFAULT;
}
/**
* @since 4.4
*
* @deprecated Use {@link #setConnectionConfigResolver(Resolver)}.
*/
@Deprecated
public TimeValue getValidateAfterInactivity() {
return validateAfterInactivity;
return ConnectionConfig.DEFAULT.getValidateAfterInactivity();
}
/**
@ -509,9 +569,14 @@ public class PoolingHttpClientConnectionManager
* detect connections that have become stale (half-closed) while kept inactive in the pool.
*
* @since 4.4
*
* @deprecated Use {@link #setConnectionConfigResolver(Resolver)}.
*/
@Deprecated
public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
this.validateAfterInactivity = validateAfterInactivity;
setDefaultConnectionConfig(ConnectionConfig.custom()
.setValidateAfterInactivity(validateAfterInactivity)
.build());
}
private static final AtomicLong COUNT = new AtomicLong(0);

View File

@ -28,12 +28,15 @@
package org.apache.hc.client5.http.impl.io;
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.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.URIScheme;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.io.HttpConnectionFactory;
@ -76,7 +79,8 @@ public class PoolingHttpClientConnectionManagerBuilder {
private DnsResolver dnsResolver;
private PoolConcurrencyPolicy poolConcurrencyPolicy;
private PoolReusePolicy poolReusePolicy;
private SocketConfig defaultSocketConfig;
private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private boolean systemProperties;
@ -84,7 +88,6 @@ public class PoolingHttpClientConnectionManagerBuilder {
private int maxConnPerRoute;
private TimeValue timeToLive;
private TimeValue validateAfterInactivity;
public static PoolingHttpClientConnectionManagerBuilder create() {
return new PoolingHttpClientConnectionManagerBuilder();
@ -161,10 +164,42 @@ public class PoolingHttpClientConnectionManagerBuilder {
}
/**
* Assigns default {@link SocketConfig}.
* Assigns the same {@link SocketConfig} for all routes.
*/
public final PoolingHttpClientConnectionManagerBuilder setDefaultSocketConfig(final SocketConfig config) {
this.defaultSocketConfig = config;
this.socketConfigResolver = (route) -> config;
return this;
}
/**
* Assigns {@link Resolver} of {@link SocketConfig} on a per route basis.
*
* @since 5.2
*/
public final PoolingHttpClientConnectionManagerBuilder setSocketConfigResolver(
final Resolver<HttpRoute, SocketConfig> socketConfigResolver) {
this.socketConfigResolver = socketConfigResolver;
return this;
}
/**
* Assigns the same {@link ConnectionConfig} for all routes.
*
* @since 5.2
*/
public final PoolingHttpClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) {
this.connectionConfigResolver = (route) -> config;
return this;
}
/**
* Assigns {@link Resolver} of {@link ConnectionConfig} on a per route basis.
*
* @since 5.2
*/
public final PoolingHttpClientConnectionManagerBuilder setConnectionConfigResolver(
final Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
return this;
}
@ -180,10 +215,13 @@ public class PoolingHttpClientConnectionManagerBuilder {
* Sets period after inactivity after which persistent
* connections must be checked to ensure they are still valid.
*
* @see org.apache.hc.core5.http.io.HttpClientConnection#isStale()
* @deprecated Use {@link #setDefaultConnectionConfig(ConnectionConfig)}.
*/
@Deprecated
public final PoolingHttpClientConnectionManagerBuilder setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
this.validateAfterInactivity = validateAfterInactivity;
setDefaultConnectionConfig(ConnectionConfig.custom()
.setValidateAfterInactivity(validateAfterInactivity)
.build());
return this;
}
@ -197,8 +235,7 @@ public class PoolingHttpClientConnectionManagerBuilder {
}
public PoolingHttpClientConnectionManager build() {
@SuppressWarnings("resource")
final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
@SuppressWarnings("resource") final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
RegistryBuilder.<ConnectionSocketFactory>create()
.register(URIScheme.HTTP.id, PlainConnectionSocketFactory.getSocketFactory())
.register(URIScheme.HTTPS.id, sslSocketFactory != null ? sslSocketFactory :
@ -212,12 +249,8 @@ public class PoolingHttpClientConnectionManagerBuilder {
schemePortResolver,
dnsResolver,
connectionFactory);
if (validateAfterInactivity != null) {
poolingmgr.setValidateAfterInactivity(validateAfterInactivity);
}
if (defaultSocketConfig != null) {
poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
}
poolingmgr.setSocketConfigResolver(socketConfigResolver);
poolingmgr.setConnectionConfigResolver(connectionConfigResolver);
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}

View File

@ -37,6 +37,7 @@ import java.util.concurrent.atomic.AtomicReference;
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.impl.ConnPoolSupport;
import org.apache.hc.client5.http.impl.ConnectionShutdownException;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
@ -49,6 +50,7 @@ import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.annotation.ThreadingBehavior;
import org.apache.hc.core5.concurrent.ComplexFuture;
import org.apache.hc.core5.concurrent.FutureCallback;
import org.apache.hc.core5.function.Resolver;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.ProtocolVersion;
@ -111,7 +113,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
private final AsyncClientConnectionOperator connectionOperator;
private final AtomicBoolean closed;
private volatile TimeValue validateAfterInactivity;
private volatile Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
public PoolingAsyncClientConnectionManager() {
this(RegistryBuilder.<TlsStrategy>create()
@ -210,6 +212,12 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
throw new IllegalStateException("Unexpected endpoint class: " + endpoint.getClass());
}
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;
}
@Override
public Future<AsyncConnectionEndpoint> lease(
final String id,
@ -221,6 +229,7 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
LOG.debug("{} endpoint lease request ({}) {}", id, requestTimeout, ConnPoolSupport.formatStats(route, state, pool));
}
final ComplexFuture<AsyncConnectionEndpoint> resultFuture = new ComplexFuture<>(callback);
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final Future<PoolEntry<HttpRoute, ManagedAsyncClientConnection>> leaseFuture = pool.lease(
route, state, requestTimeout, new FutureCallback<PoolEntry<HttpRoute, ManagedAsyncClientConnection>>() {
@ -242,25 +251,19 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
@Override
public void completed(final PoolEntry<HttpRoute, ManagedAsyncClientConnection> poolEntry) {
final ManagedAsyncClientConnection connection = poolEntry.getConnection();
if (connection != null) {
if (connection.isOpen()) {
final ProtocolVersion protocolVersion = connection.getProtocolVersion();
if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
final TimeValue timeValue = PoolingAsyncClientConnectionManager.this.validateAfterInactivity;
if (TimeValue.isNonNegative(timeValue) &&
poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
if (result == null || !result) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
}
poolEntry.discardConnection(CloseMode.IMMEDIATE);
}
leaseCompleted(poolEntry);
})), Command.Priority.IMMEDIATE);
return;
final TimeValue timeValue = connectionConfig != null ? connectionConfig.getValidateAfterInactivity() : null;
if (TimeValue.isNonNegative(timeValue) && connection != null &&
poolEntry.getUpdated() + timeValue.toMilliseconds() <= System.currentTimeMillis()) {
final ProtocolVersion protocolVersion = connection.getProtocolVersion();
if (protocolVersion != null && protocolVersion.greaterEquals(HttpVersion.HTTP_2_0)) {
connection.submitCommand(new PingCommand(new BasicPingHandler(result -> {
if (result == null || !result) {
if (LOG.isDebugEnabled()) {
LOG.debug("{} connection {} is stale", id, ConnPoolSupport.getId(connection));
}
poolEntry.discardConnection(CloseMode.IMMEDIATE);
}
}
})), Command.Priority.IMMEDIATE);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("{} connection {} is closed", id, ConnPoolSupport.getId(connection));
@ -336,13 +339,12 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
public Future<AsyncConnectionEndpoint> connect(
final AsyncConnectionEndpoint endpoint,
final ConnectionInitiator connectionInitiator,
final Timeout connectTimeout,
final Timeout timeout,
final Object attachment,
final HttpContext context,
final FutureCallback<AsyncConnectionEndpoint> callback) {
Args.notNull(endpoint, "Endpoint");
Args.notNull(connectionInitiator, "Connection initiator");
Args.notNull(connectTimeout, "Timeout");
final InternalConnectionEndpoint internalEndpoint = cast(endpoint);
final ComplexFuture<AsyncConnectionEndpoint> resultFuture = new ComplexFuture<>(callback);
if (internalEndpoint.isConnected()) {
@ -358,6 +360,9 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
host = route.getTargetHost();
}
final InetSocketAddress localAddress = route.getLocalSocketAddress();
final ConnectionConfig connectionConfig = resolveConnectionConfig(route);
final Timeout connectTimeout = timeout != null ? timeout : connectionConfig.getConnectTimeout();
if (LOG.isDebugEnabled()) {
LOG.debug("{} connecting endpoint to {} ({})", ConnPoolSupport.getId(endpoint), host, connectTimeout);
}
@ -370,6 +375,10 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
if (LOG.isDebugEnabled()) {
LOG.debug("{} connected {}", ConnPoolSupport.getId(endpoint), ConnPoolSupport.getId(connection));
}
final Timeout socketTimeout = connectionConfig.getSocketTimeout();
if (socketTimeout != null) {
connection.setSocketTimeout(socketTimeout);
}
poolEntry.assignConnection(connection);
resultFuture.completed(internalEndpoint);
} catch (final RuntimeException ex) {
@ -463,8 +472,30 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
return pool.getStats(route);
}
/**
* Sets the same {@link ConnectionConfig} for all routes
*
* @since 5.2
*/
public void setDefaultConnectionConfig(final ConnectionConfig config) {
this.connectionConfigResolver = (route) -> config;
}
/**
* Sets {@link Resolver} of {@link ConnectionConfig} on a per route basis.
*
* @since 5.2
*/
public void setConnectionConfigResolver(final Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
}
/**
* @deprecated Use custom {@link #setConnectionConfigResolver(Resolver)}
*/
@Deprecated
public TimeValue getValidateAfterInactivity() {
return validateAfterInactivity;
return ConnectionConfig.DEFAULT.getValidateAfterInactivity();
}
/**
@ -473,9 +504,14 @@ public class PoolingAsyncClientConnectionManager implements AsyncClientConnectio
* FutureCallback)} leased} to the consumer. Negative values passed
* to this method disable connection validation. This check helps detect connections
* that have become stale (half-closed) while kept inactive in the pool.
*
* @deprecated Use {@link #setConnectionConfigResolver(Resolver)}.
*/
@Deprecated
public void setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
this.validateAfterInactivity = validateAfterInactivity;
setDefaultConnectionConfig(ConnectionConfig.custom()
.setValidateAfterInactivity(validateAfterInactivity)
.build());
}
private static final AtomicLong COUNT = new AtomicLong(0);

View File

@ -28,10 +28,14 @@
package org.apache.hc.client5.http.impl.nio;
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.ssl.ConscryptClientTlsStrategy;
import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
import org.apache.hc.core5.function.Resolver;
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;
import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.pool.PoolReusePolicy;
@ -77,8 +81,9 @@ public class PoolingAsyncClientConnectionManagerBuilder {
private int maxConnTotal;
private int maxConnPerRoute;
private Resolver<HttpRoute, SocketConfig> socketConfigResolver;
private Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver;
private TimeValue timeToLive;
private TimeValue validateAfterInactivity;
public static PoolingAsyncClientConnectionManagerBuilder create() {
return new PoolingAsyncClientConnectionManagerBuilder();
@ -145,6 +150,28 @@ public class PoolingAsyncClientConnectionManagerBuilder {
return this;
}
/**
* Assigns the same {@link ConnectionConfig} for all routes.
*
* @since 5.2
*/
public final PoolingAsyncClientConnectionManagerBuilder setDefaultConnectionConfig(final ConnectionConfig config) {
this.connectionConfigResolver = (route) -> config;
return this;
}
/**
* Assigns {@link Resolver} of {@link ConnectionConfig} on a per route basis.
*
* @since 5.2
*/
public final PoolingAsyncClientConnectionManagerBuilder setConnectionConfigResolver(
final Resolver<HttpRoute, ConnectionConfig> connectionConfigResolver) {
this.connectionConfigResolver = connectionConfigResolver;
return this;
}
/**
* Sets maximum time to live for persistent connections
*/
@ -157,10 +184,13 @@ public class PoolingAsyncClientConnectionManagerBuilder {
* Sets period after inactivity after which persistent
* connections must be checked to ensure they are still valid.
*
* @see org.apache.hc.core5.http.io.HttpClientConnection#isStale()
* @deprecated Use {@link #setConnectionConfigResolver(Resolver)}.
*/
@Deprecated
public final PoolingAsyncClientConnectionManagerBuilder setValidateAfterInactivity(final TimeValue validateAfterInactivity) {
this.validateAfterInactivity = validateAfterInactivity;
setDefaultConnectionConfig(ConnectionConfig.custom()
.setValidateAfterInactivity(validateAfterInactivity)
.build());
return this;
}
@ -201,9 +231,7 @@ public class PoolingAsyncClientConnectionManagerBuilder {
timeToLive,
schemePortResolver,
dnsResolver);
if (validateAfterInactivity != null) {
poolingmgr.setValidateAfterInactivity(validateAfterInactivity);
}
poolingmgr.setConnectionConfigResolver(connectionConfigResolver);
if (maxConnTotal > 0) {
poolingmgr.setMaxTotal(maxConnTotal);
}

View File

@ -32,7 +32,6 @@ import java.util.concurrent.TimeUnit;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.cookie.StandardCookieSpec;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.Timeout;
import org.junit.Assert;
@ -49,7 +48,6 @@ public class TestRequestConfig {
@Test
public void testDefaults() {
final RequestConfig config = RequestConfig.DEFAULT;
Assert.assertEquals(Timeout.ofMinutes(3), config.getConnectTimeout());
Assert.assertEquals(Timeout.ofMinutes(3), config.getConnectionRequestTimeout());
Assert.assertEquals(false, config.isExpectContinueEnabled());
Assert.assertEquals(true, config.isAuthenticationEnabled());
@ -66,7 +64,6 @@ public class TestRequestConfig {
@Test
public void testBuildAndCopy() throws Exception {
final RequestConfig config0 = RequestConfig.custom()
.setConnectTimeout(33, TimeUnit.MILLISECONDS)
.setConnectionRequestTimeout(44, TimeUnit.MILLISECONDS)
.setExpectContinueEnabled(true)
.setAuthenticationEnabled(false)
@ -74,13 +71,11 @@ public class TestRequestConfig {
.setCircularRedirectsAllowed(true)
.setMaxRedirects(100)
.setCookieSpec(StandardCookieSpec.STRICT)
.setProxy(new HttpHost("someproxy"))
.setTargetPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.NTLM))
.setProxyPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.DIGEST))
.setContentCompressionEnabled(false)
.build();
final RequestConfig config = RequestConfig.copy(config0).build();
Assert.assertEquals(TimeValue.ofMilliseconds(33), config.getConnectTimeout());
Assert.assertEquals(TimeValue.ofMilliseconds(44), config.getConnectionRequestTimeout());
Assert.assertEquals(true, config.isExpectContinueEnabled());
Assert.assertEquals(false, config.isAuthenticationEnabled());
@ -88,7 +83,6 @@ public class TestRequestConfig {
Assert.assertEquals(true, config.isCircularRedirectsAllowed());
Assert.assertEquals(100, config.getMaxRedirects());
Assert.assertEquals(StandardCookieSpec.STRICT, config.getCookieSpec());
Assert.assertEquals(new HttpHost("someproxy"), config.getProxy());
Assert.assertEquals(Collections.singletonList(StandardAuthScheme.NTLM), config.getTargetPreferredAuthSchemes());
Assert.assertEquals(Collections.singletonList(StandardAuthScheme.DIGEST), config.getProxyPreferredAuthSchemes());
Assert.assertEquals(false, config.isContentCompressionEnabled());

View File

@ -39,13 +39,14 @@ import javax.net.ssl.SSLContext;
import org.apache.hc.client5.http.DnsResolver;
import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.auth.StandardAuthScheme;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.StandardCookieSpec;
import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.cookie.StandardCookieSpec;
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
@ -172,15 +173,17 @@ public class ClientConfiguration {
socketFactoryRegistry, PoolConcurrencyPolicy.STRICT, PoolReusePolicy.LIFO, TimeValue.ofMinutes(5),
null, dnsResolver, null);
// Create socket configuration
final SocketConfig socketConfig = SocketConfig.custom()
.setTcpNoDelay(true)
.build();
// Configure the connection manager to use socket configuration either
// by default or for a specific host.
connManager.setDefaultSocketConfig(socketConfig);
// Validate connections after 1 sec of inactivity
connManager.setValidateAfterInactivity(TimeValue.ofSeconds(10));
connManager.setDefaultSocketConfig(SocketConfig.custom()
.setTcpNoDelay(true)
.build());
// Validate connections after 10 sec of inactivity
connManager.setDefaultConnectionConfig(ConnectionConfig.custom()
.setConnectTimeout(Timeout.ofSeconds(30))
.setSocketTimeout(Timeout.ofSeconds(30))
.setValidateAfterInactivity(TimeValue.ofSeconds(10))
.build());
// Configure total max or per route limits for persistent connections
// that can be kept in the pool or leased by the connection manager.
@ -214,8 +217,6 @@ public class ClientConfiguration {
// They will take precedence over the one set at the client level.
final RequestConfig requestConfig = RequestConfig.copy(defaultRequestConfig)
.setConnectionRequestTimeout(Timeout.ofSeconds(5))
.setConnectTimeout(Timeout.ofSeconds(5))
.setProxy(new HttpHost("myotherproxy", 8080))
.build();
httpget.setConfig(requestConfig);