blob: 619efcac456090319d7368c8e68b7868c65b0fe8 [file] [log] [blame]
/*
* ====================================================================
* 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.io.Closeable;
import java.io.IOException;
import java.net.ProxySelector;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ThreadFactory;
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.SchemePortResolver;
import org.apache.hc.client5.http.SystemDefaultDnsResolver;
import org.apache.hc.client5.http.async.AsyncExecChainHandler;
import org.apache.hc.client5.http.auth.AuthSchemeProvider;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.config.AuthSchemes;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.cookie.BasicCookieStore;
import org.apache.hc.client5.http.cookie.CookieSpecProvider;
import org.apache.hc.client5.http.cookie.CookieStore;
import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
import org.apache.hc.client5.http.impl.DefaultUserTokenHandler;
import org.apache.hc.client5.http.impl.IdleConnectionEvictor;
import org.apache.hc.client5.http.impl.NamedElementChain;
import org.apache.hc.client5.http.impl.NoopUserTokenHandler;
import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
import org.apache.hc.client5.http.impl.auth.CredSspSchemeFactory;
import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
import org.apache.hc.client5.http.impl.auth.KerberosSchemeFactory;
import org.apache.hc.client5.http.impl.auth.NTLMSchemeFactory;
import org.apache.hc.client5.http.impl.auth.SPNegoSchemeFactory;
import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider;
import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
import org.apache.hc.client5.http.impl.protocol.DefaultAuthenticationStrategy;
import org.apache.hc.client5.http.impl.protocol.DefaultRedirectStrategy;
import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
import org.apache.hc.client5.http.impl.sync.BasicCredentialsProvider;
import org.apache.hc.client5.http.impl.sync.ChainElements;
import org.apache.hc.client5.http.impl.sync.CookieSpecRegistries;
import org.apache.hc.client5.http.impl.sync.DefaultHttpRequestRetryHandler;
import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
import org.apache.hc.client5.http.protocol.AuthenticationStrategy;
import org.apache.hc.client5.http.protocol.RedirectStrategy;
import org.apache.hc.client5.http.protocol.RequestAddCookies;
import org.apache.hc.client5.http.protocol.RequestAuthCache;
import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
import org.apache.hc.client5.http.protocol.RequestExpectContinue;
import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
import org.apache.hc.client5.http.protocol.UserTokenHandler;
import org.apache.hc.client5.http.routing.HttpRoutePlanner;
import org.apache.hc.client5.http.sync.HttpRequestRetryHandler;
import org.apache.hc.core5.annotation.Internal;
import org.apache.hc.core5.concurrent.DefaultThreadFactory;
import org.apache.hc.core5.function.Callback;
import org.apache.hc.core5.http.ConnectionReuseStrategy;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestInterceptor;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.HttpResponseInterceptor;
import org.apache.hc.core5.http.config.CharCodingConfig;
import org.apache.hc.core5.http.config.H1Config;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
import org.apache.hc.core5.http.nio.AsyncPushConsumer;
import org.apache.hc.core5.http.nio.HandlerFactory;
import org.apache.hc.core5.http.nio.command.ShutdownCommand;
import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
import org.apache.hc.core5.http.protocol.HttpContext;
import org.apache.hc.core5.http.protocol.HttpProcessor;
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.HttpVersionPolicy;
import org.apache.hc.core5.http2.config.H2Config;
import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
import org.apache.hc.core5.http2.protocol.H2RequestContent;
import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
import org.apache.hc.core5.io.ShutdownType;
import org.apache.hc.core5.pool.ConnPoolControl;
import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
import org.apache.hc.core5.reactor.IOEventHandlerFactory;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.reactor.IOReactorException;
import org.apache.hc.core5.reactor.IOSession;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.TimeValue;
import org.apache.hc.core5.util.VersionInfo;
/**
* Builder for {@link CloseableHttpAsyncClient} instances.
* <p>
* When a particular component is not explicitly set this class will
* use its default implementation. System properties will be taken
* into account when configuring the default implementations when
* {@link #useSystemProperties()} method is called prior to calling
* {@link #build()}.
* </p>
* <ul>
* <li>http.proxyHost</li>
* <li>http.proxyPort</li>
* <li>http.nonProxyHosts</li>
* <li>http.keepAlive</li>
* <li>http.agent</li>
* </ul>
* <p>
* Please note that some settings used by this class can be mutually
* exclusive and may not apply when building {@link CloseableHttpAsyncClient}
* instances.
* </p>
*
* @since 5.0
*/
public class HttpAsyncClientBuilder {
private static class RequestInterceptorEntry {
enum Postion { FIRST, LAST }
final RequestInterceptorEntry.Postion postion;
final HttpRequestInterceptor interceptor;
private RequestInterceptorEntry(final RequestInterceptorEntry.Postion postion, final HttpRequestInterceptor interceptor) {
this.postion = postion;
this.interceptor = interceptor;
}
}
private static class ResponseInterceptorEntry {
enum Postion { FIRST, LAST }
final ResponseInterceptorEntry.Postion postion;
final HttpResponseInterceptor interceptor;
private ResponseInterceptorEntry(final ResponseInterceptorEntry.Postion postion, final HttpResponseInterceptor interceptor) {
this.postion = postion;
this.interceptor = interceptor;
}
}
private static class ExecInterceptorEntry {
enum Postion { BEFORE, AFTER, REPLACE }
final ExecInterceptorEntry.Postion postion;
final String name;
final AsyncExecChainHandler interceptor;
final String existing;
private ExecInterceptorEntry(
final ExecInterceptorEntry.Postion postion,
final String name,
final AsyncExecChainHandler interceptor,
final String existing) {
this.postion = postion;
this.name = name;
this.interceptor = interceptor;
this.existing = existing;
}
}
private HttpVersionPolicy versionPolicy;
private AsyncClientConnectionManager connManager;
private boolean connManagerShared;
private IOReactorConfig ioReactorConfig;
private H1Config h1Config;
private H2Config h2Config;
private CharCodingConfig charCodingConfig;
private SchemePortResolver schemePortResolver;
private ConnectionKeepAliveStrategy keepAliveStrategy;
private UserTokenHandler userTokenHandler;
private AuthenticationStrategy targetAuthStrategy;
private AuthenticationStrategy proxyAuthStrategy;
private LinkedList<RequestInterceptorEntry> requestInterceptors;
private LinkedList<ResponseInterceptorEntry> responseInterceptors;
private LinkedList<ExecInterceptorEntry> execInterceptors;
private HttpRoutePlanner routePlanner;
private RedirectStrategy redirectStrategy;
private HttpRequestRetryHandler retryHandler;
private ConnectionReuseStrategy reuseStrategy;
private Lookup<AuthSchemeProvider> authSchemeRegistry;
private Lookup<CookieSpecProvider> cookieSpecRegistry;
private CookieStore cookieStore;
private CredentialsProvider credentialsProvider;
private String userAgent;
private HttpHost proxy;
private Collection<? extends Header> defaultHeaders;
private RequestConfig defaultRequestConfig;
private boolean evictExpiredConnections;
private boolean evictIdleConnections;
private TimeValue maxIdleTime;
private boolean systemProperties;
private boolean automaticRetriesDisabled;
private boolean redirectHandlingDisabled;
private boolean cookieManagementDisabled;
private boolean authCachingDisabled;
private boolean connectionStateDisabled;
private ThreadFactory threadFactory;
private List<Closeable> closeables;
public static HttpAsyncClientBuilder create() {
return new HttpAsyncClientBuilder();
}
protected HttpAsyncClientBuilder() {
super();
}
/**
* Sets HTTP protocol version policy.
*/
public final HttpAsyncClientBuilder setVersionPolicy(final HttpVersionPolicy versionPolicy) {
this.versionPolicy = versionPolicy;
return this;
}
/**
* Sets {@link H1Config} configuration.
*/
public final HttpAsyncClientBuilder setH1Config(final H1Config h1Config) {
this.h1Config = h1Config;
return this;
}
/**
* Sets {@link H2Config} configuration.
*/
public final HttpAsyncClientBuilder setH2Config(final H2Config h2Config) {
this.h2Config = h2Config;
return this;
}
/**
* Assigns {@link AsyncClientConnectionManager} instance.
*/
public final HttpAsyncClientBuilder setConnectionManager(final AsyncClientConnectionManager connManager) {
this.connManager = connManager;
return this;
}
/**
* Defines the connection manager is to be shared by multiple
* client instances.
* <p>
* If the connection manager is shared its life-cycle is expected
* to be managed by the caller and it will not be shut down
* if the client is closed.
*
* @param shared defines whether or not the connection manager can be shared
* by multiple clients.
*/
public final HttpAsyncClientBuilder setConnectionManagerShared(final boolean shared) {
this.connManagerShared = shared;
return this;
}
/**
* Sets {@link IOReactorConfig} configuration.
*/
public final HttpAsyncClientBuilder setIOReactorConfig(final IOReactorConfig ioReactorConfig) {
this.ioReactorConfig = ioReactorConfig;
return this;
}
/**
* Sets {@link CharCodingConfig} configuration.
*/
public final HttpAsyncClientBuilder setCharCodingConfig(final CharCodingConfig charCodingConfig) {
this.charCodingConfig = charCodingConfig;
return this;
}
/**
* Assigns {@link ConnectionReuseStrategy} instance.
* <p>
* Please note this strategy applies to HTTP/1.0 and HTTP/1.1 connections only
*/
public final HttpAsyncClientBuilder setConnectionReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
this.reuseStrategy = reuseStrategy;
return this;
}
/**
* Assigns {@link ConnectionKeepAliveStrategy} instance.
*/
public final HttpAsyncClientBuilder setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
this.keepAliveStrategy = keepAliveStrategy;
return this;
}
/**
* Assigns {@link UserTokenHandler} instance.
* <p>
* Please note this value can be overridden by the {@link #disableConnectionState()}
* method.
* </p>
*/
public final HttpAsyncClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
this.userTokenHandler = userTokenHandler;
return this;
}
/**
* Assigns {@link AuthenticationStrategy} instance for target
* host authentication.
*/
public final HttpAsyncClientBuilder setTargetAuthenticationStrategy(
final AuthenticationStrategy targetAuthStrategy) {
this.targetAuthStrategy = targetAuthStrategy;
return this;
}
/**
* Assigns {@link AuthenticationStrategy} instance for proxy
* authentication.
*/
public final HttpAsyncClientBuilder setProxyAuthenticationStrategy(
final AuthenticationStrategy proxyAuthStrategy) {
this.proxyAuthStrategy = proxyAuthStrategy;
return this;
}
/**
* Adds this protocol interceptor to the head of the protocol processing list.
*/
public final HttpAsyncClientBuilder addRequestInterceptorFirst(final HttpResponseInterceptor interceptor) {
Args.notNull(interceptor, "Interceptor");
if (responseInterceptors == null) {
responseInterceptors = new LinkedList<>();
}
responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Postion.FIRST, interceptor));
return this;
}
/**
* Adds this protocol interceptor to the tail of the protocol processing list.
*/
public final HttpAsyncClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) {
Args.notNull(interceptor, "Interceptor");
if (responseInterceptors == null) {
responseInterceptors = new LinkedList<>();
}
responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Postion.LAST, interceptor));
return this;
}
/**
* Adds this execution interceptor before an existing interceptor.
*/
public final HttpAsyncClientBuilder addExecInterceptorBefore(final String existing, final String name, final AsyncExecChainHandler interceptor) {
Args.notBlank(existing, "Existing");
Args.notBlank(name, "Name");
Args.notNull(interceptor, "Interceptor");
if (execInterceptors == null) {
execInterceptors = new LinkedList<>();
}
execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Postion.BEFORE, name, interceptor, existing));
return this;
}
/**
* Adds this execution interceptor after interceptor with the given name.
*/
public final HttpAsyncClientBuilder addExecInterceptorAfter(final String existing, final String name, final AsyncExecChainHandler interceptor) {
Args.notBlank(existing, "Existing");
Args.notBlank(name, "Name");
Args.notNull(interceptor, "Interceptor");
if (execInterceptors == null) {
execInterceptors = new LinkedList<>();
}
execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Postion.AFTER, name, interceptor, existing));
return this;
}
/**
* Replace an existing interceptor with the given name with new interceptor.
*/
public final HttpAsyncClientBuilder replaceExecInterceptor(final String existing, final AsyncExecChainHandler interceptor) {
Args.notBlank(existing, "Existing");
Args.notNull(interceptor, "Interceptor");
if (execInterceptors == null) {
execInterceptors = new LinkedList<>();
}
execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Postion.REPLACE, existing, interceptor, existing));
return this;
}
/**
* Adds this protocol interceptor to the head of the protocol processing list.
*/
public final HttpAsyncClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) {
Args.notNull(interceptor, "Interceptor");
if (requestInterceptors == null) {
requestInterceptors = new LinkedList<>();
}
requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Postion.FIRST, interceptor));
return this;
}
/**
* Adds this protocol interceptor to the tail of the protocol processing list.
*/
public final HttpAsyncClientBuilder addResponseInterceptorLast(final HttpRequestInterceptor interceptor) {
Args.notNull(interceptor, "Interceptor");
if (requestInterceptors == null) {
requestInterceptors = new LinkedList<>();
}
requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Postion.LAST, interceptor));
return this;
}
/**
* Assigns {@link HttpRequestRetryHandler} instance.
* <p>
* Please note this value can be overridden by the {@link #disableAutomaticRetries()}
* method.
*/
public final HttpAsyncClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) {
this.retryHandler = retryHandler;
return this;
}
/**
* Assigns {@link RedirectStrategy} instance.
* <p>
* Please note this value can be overridden by the {@link #disableRedirectHandling()}
* method.
* </p>
*/
public HttpAsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
this.redirectStrategy = redirectStrategy;
return this;
}
/**
* Assigns {@link SchemePortResolver} instance.
*/
public final HttpAsyncClientBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) {
this.schemePortResolver = schemePortResolver;
return this;
}
/**
* Assigns {@link ThreadFactory} instance.
*/
public final HttpAsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) {
this.threadFactory = threadFactory;
return this;
}
/**
* Assigns {@code User-Agent} value.
*/
public final HttpAsyncClientBuilder setUserAgent(final String userAgent) {
this.userAgent = userAgent;
return this;
}
/**
* Assigns default request header values.
*/
public final HttpAsyncClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
this.defaultHeaders = defaultHeaders;
return this;
}
/**
* Assigns default proxy value.
* <p>
* Please note this value can be overridden by the {@link #setRoutePlanner(
* HttpRoutePlanner)} method.
*/
public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) {
this.proxy = proxy;
return this;
}
/**
* Assigns {@link HttpRoutePlanner} instance.
*/
public final HttpAsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
this.routePlanner = routePlanner;
return this;
}
/**
* Assigns default {@link CredentialsProvider} instance which will be used
* for request execution if not explicitly set in the client execution
* context.
*/
public final HttpAsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) {
this.credentialsProvider = credentialsProvider;
return this;
}
/**
* Assigns default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will
* be used for request execution if not explicitly set in the client execution
* context.
*/
public final HttpAsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup<AuthSchemeProvider> authSchemeRegistry) {
this.authSchemeRegistry = authSchemeRegistry;
return this;
}
/**
* Assigns default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry
* which will be used for request execution if not explicitly set in the client
* execution context.
*/
public final HttpAsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup<CookieSpecProvider> cookieSpecRegistry) {
this.cookieSpecRegistry = cookieSpecRegistry;
return this;
}
/**
* Assigns default {@link CookieStore} instance which will be used for
* request execution if not explicitly set in the client execution context.
*/
public final HttpAsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
this.cookieStore = cookieStore;
return this;
}
/**
* Assigns default {@link RequestConfig} instance which will be used
* for request execution if not explicitly set in the client execution
* context.
*/
public final HttpAsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) {
this.defaultRequestConfig = config;
return this;
}
/**
* Use system properties when creating and configuring default
* implementations.
*/
public final HttpAsyncClientBuilder useSystemProperties() {
this.systemProperties = true;
return this;
}
/**
* Disables connection state tracking.
*/
public final HttpAsyncClientBuilder disableConnectionState() {
connectionStateDisabled = true;
return this;
}
/**
* Disables automatic redirect handling.
*/
public final HttpAsyncClientBuilder disableRedirectHandling() {
redirectHandlingDisabled = true;
return this;
}
/**
* Disables automatic request recovery and re-execution.
*/
public final HttpAsyncClientBuilder disableAutomaticRetries() {
automaticRetriesDisabled = true;
return this;
}
/**
* Disables state (cookie) management.
*/
public final HttpAsyncClientBuilder disableCookieManagement() {
this.cookieManagementDisabled = true;
return this;
}
/**
* Disables authentication scheme caching.
*/
public final HttpAsyncClientBuilder disableAuthCaching() {
this.authCachingDisabled = true;
return this;
}
/**
* Makes this instance of HttpClient proactively evict expired connections from the
* connection pool using a background thread.
* <p>
* One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} in order
* to stop and release the background thread.
* <p>
* Please note this method has no effect if the instance of HttpClient is configuted to
* use a shared connection manager.
*
* @see #setConnectionManagerShared(boolean)
* @see ConnPoolControl#closeExpired()
*/
public final HttpAsyncClientBuilder evictExpiredConnections() {
evictExpiredConnections = true;
return this;
}
/**
* Makes this instance of HttpClient proactively evict idle connections from the
* connection pool using a background thread.
* <p>
* One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()}
* in order to stop and release the background thread.
* <p>
* Please note this method has no effect if the instance of HttpClient is configuted to
* use a shared connection manager.
*
* @see #setConnectionManagerShared(boolean)
* @see ConnPoolControl#closeIdle(TimeValue)
*
* @param maxIdleTime maximum time persistent connections can stay idle while kept alive
* in the connection pool. Connections whose inactivity period exceeds this value will
* get closed and evicted from the pool.
*/
public final HttpAsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
this.evictIdleConnections = true;
this.maxIdleTime = maxIdleTime;
return this;
}
/**
* Request exec chain customization and extension.
* <p>
* For internal use.
*/
@Internal
protected void customizeExecChain(final NamedElementChain<AsyncExecChainHandler> execChainDefinition) {
}
/**
* Adds to the list of {@link Closeable} resources to be managed by the client.
* <p>
* For internal use.
*/
@Internal
protected void addCloseable(final Closeable closeable) {
if (closeable == null) {
return;
}
if (closeables == null) {
closeables = new ArrayList<>();
}
closeables.add(closeable);
}
public CloseableHttpAsyncClient build() {
AsyncClientConnectionManager connManagerCopy = this.connManager;
if (connManagerCopy == null) {
connManagerCopy = PoolingAsyncClientConnectionManagerBuilder.create().build();
}
ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
if (keepAliveStrategyCopy == null) {
keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
}
UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
if (userTokenHandlerCopy == null) {
if (!connectionStateDisabled) {
userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
} else {
userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
}
}
final NamedElementChain<AsyncExecChainHandler> execChainDefinition = new NamedElementChain<>();
execChainDefinition.addLast(
new AsyncMainClientExec(keepAliveStrategyCopy, userTokenHandlerCopy),
ChainElements.MAIN_TRANSPORT.name());
AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
if (targetAuthStrategyCopy == null) {
targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
}
AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
if (proxyAuthStrategyCopy == null) {
proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
}
String userAgentCopy = this.userAgent;
if (userAgentCopy == null) {
if (systemProperties) {
userAgentCopy = System.getProperty("http.agent");
}
if (userAgentCopy == null) {
userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient",
"org.apache.hc.client5", getClass());
}
}
execChainDefinition.addFirst(
new AsyncConnectExec(
new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
proxyAuthStrategyCopy),
ChainElements.CONNECT.name());
final HttpProcessorBuilder b = HttpProcessorBuilder.create();
if (requestInterceptors != null) {
for (final RequestInterceptorEntry entry: requestInterceptors) {
if (entry.postion == RequestInterceptorEntry.Postion.FIRST) {
b.addFirst(entry.interceptor);
}
}
}
if (responseInterceptors != null) {
for (final ResponseInterceptorEntry entry: responseInterceptors) {
if (entry.postion == ResponseInterceptorEntry.Postion.FIRST) {
b.addFirst(entry.interceptor);
}
}
}
b.addAll(
new RequestDefaultHeaders(defaultHeaders),
new RequestUserAgent(userAgentCopy),
new RequestExpectContinue());
if (!cookieManagementDisabled) {
b.add(new RequestAddCookies());
}
if (!authCachingDisabled) {
b.add(new RequestAuthCache());
}
if (!cookieManagementDisabled) {
b.add(new ResponseProcessCookies());
}
if (requestInterceptors != null) {
for (final RequestInterceptorEntry entry: requestInterceptors) {
if (entry.postion == RequestInterceptorEntry.Postion.LAST) {
b.addFirst(entry.interceptor);
}
}
}
if (responseInterceptors != null) {
for (final ResponseInterceptorEntry entry: responseInterceptors) {
if (entry.postion == ResponseInterceptorEntry.Postion.LAST) {
b.addFirst(entry.interceptor);
}
}
}
final HttpProcessor httpProcessor = b.build();
execChainDefinition.addFirst(
new AsyncProtocolExec(httpProcessor, targetAuthStrategyCopy, proxyAuthStrategyCopy),
ChainElements.PROTOCOL.name());
// Add request retry executor, if not disabled
if (!automaticRetriesDisabled) {
HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
if (retryHandlerCopy == null) {
retryHandlerCopy = DefaultHttpRequestRetryHandler.INSTANCE;
}
execChainDefinition.addFirst(
new AsyncRetryExec(retryHandlerCopy),
ChainElements.RETRY_IO_ERROR.name());
}
HttpRoutePlanner routePlannerCopy = this.routePlanner;
if (routePlannerCopy == null) {
SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
if (schemePortResolverCopy == null) {
schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
}
if (proxy != null) {
routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
} else if (systemProperties) {
routePlannerCopy = new SystemDefaultRoutePlanner(
schemePortResolverCopy, ProxySelector.getDefault());
} else {
routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
}
}
// Add redirect executor, if not disabled
if (!redirectHandlingDisabled) {
RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
if (redirectStrategyCopy == null) {
redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
}
execChainDefinition.addFirst(
new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),
ChainElements.REDIRECT.name());
}
List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
if (!this.connManagerShared) {
if (closeablesCopy == null) {
closeablesCopy = new ArrayList<>(1);
}
if (evictExpiredConnections || evictIdleConnections) {
if (connManagerCopy instanceof ConnPoolControl) {
final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl<?>) connManagerCopy,
maxIdleTime, maxIdleTime);
closeablesCopy.add(new Closeable() {
@Override
public void close() throws IOException {
connectionEvictor.shutdown();
}
});
connectionEvictor.start();
}
}
closeablesCopy.add(connManagerCopy);
}
ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
if (reuseStrategyCopy == null) {
if (systemProperties) {
final String s = System.getProperty("http.keepAlive", "true");
if ("true".equalsIgnoreCase(s)) {
reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
} else {
reuseStrategyCopy = new ConnectionReuseStrategy() {
@Override
public boolean keepAlive(
final HttpRequest request, final HttpResponse response, final HttpContext context) {
return false;
}
};
}
} else {
reuseStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
}
}
final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
final IOEventHandlerFactory ioEventHandlerFactory = new HttpAsyncClientEventHandlerFactory(
new DefaultHttpProcessor(new H2RequestContent(), new H2RequestTargetHost(), new H2RequestConnControl()),
new HandlerFactory<AsyncPushConsumer>() {
@Override
public AsyncPushConsumer create(final HttpRequest request) throws HttpException {
return pushConsumerRegistry.get(request);
}
},
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
h2Config != null ? h2Config : H2Config.DEFAULT,
h1Config != null ? h1Config : H1Config.DEFAULT,
charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
reuseStrategyCopy);
final DefaultConnectingIOReactor ioReactor;
try {
ioReactor = new DefaultConnectingIOReactor(
ioEventHandlerFactory,
ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT,
threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true),
new Callback<IOSession>() {
@Override
public void execute(final IOSession ioSession) {
ioSession.addFirst(new ShutdownCommand(ShutdownType.GRACEFUL));
}
});
} catch (final IOReactorException ex) {
throw new IllegalStateException(ex.getMessage(), ex);
}
if (execInterceptors != null) {
for (final ExecInterceptorEntry entry: execInterceptors) {
switch (entry.postion) {
case AFTER:
execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name);
break;
case BEFORE:
execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name);
break;
}
}
}
customizeExecChain(execChainDefinition);
NamedElementChain<AsyncExecChainHandler>.Node current = execChainDefinition.getLast();
AsyncExecChainElement execChain = null;
while (current != null) {
execChain = new AsyncExecChainElement(current.getValue(), execChain);
current = current.getPrevious();
}
Lookup<AuthSchemeProvider> authSchemeRegistryCopy = this.authSchemeRegistry;
if (authSchemeRegistryCopy == null) {
authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeProvider>create()
.register(AuthSchemes.BASIC, new BasicSchemeFactory())
.register(AuthSchemes.DIGEST, new DigestSchemeFactory())
.register(AuthSchemes.CREDSSP, new CredSspSchemeFactory())
.register(AuthSchemes.NTLM, new NTLMSchemeFactory())
.register(AuthSchemes.SPNEGO, new SPNegoSchemeFactory(SystemDefaultDnsResolver.INSTANCE, true, true))
.register(AuthSchemes.KERBEROS, new KerberosSchemeFactory(SystemDefaultDnsResolver.INSTANCE, true, true))
.build();
}
Lookup<CookieSpecProvider> cookieSpecRegistryCopy = this.cookieSpecRegistry;
if (cookieSpecRegistryCopy == null) {
cookieSpecRegistryCopy = CookieSpecRegistries.createDefault();
}
CookieStore cookieStoreCopy = this.cookieStore;
if (cookieStoreCopy == null) {
cookieStoreCopy = new BasicCookieStore();
}
CredentialsProvider credentialsProviderCopy = this.credentialsProvider;
if (credentialsProviderCopy == null) {
if (systemProperties) {
credentialsProviderCopy = new SystemDefaultCredentialsProvider();
} else {
credentialsProviderCopy = new BasicCredentialsProvider();
}
}
return new InternalHttpAsyncClient(
ioReactor,
execChain,
pushConsumerRegistry,
threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true),
connManagerCopy,
routePlannerCopy,
versionPolicy != null ? versionPolicy : HttpVersionPolicy.NEGOTIATE,
cookieSpecRegistryCopy,
authSchemeRegistryCopy,
cookieStoreCopy,
credentialsProviderCopy,
defaultRequestConfig,
closeablesCopy);
}
}