View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.client5.http.impl.async;
29  
30  import java.io.Closeable;
31  import java.net.ProxySelector;
32  import java.security.AccessController;
33  import java.security.PrivilegedAction;
34  import java.util.ArrayList;
35  import java.util.Collection;
36  import java.util.LinkedList;
37  import java.util.List;
38  import java.util.concurrent.ThreadFactory;
39  import java.util.function.Function;
40  
41  import org.apache.hc.client5.http.AuthenticationStrategy;
42  import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
43  import org.apache.hc.client5.http.HttpRequestRetryStrategy;
44  import org.apache.hc.client5.http.SchemePortResolver;
45  import org.apache.hc.client5.http.UserTokenHandler;
46  import org.apache.hc.client5.http.async.AsyncExecChainHandler;
47  import org.apache.hc.client5.http.auth.AuthSchemeFactory;
48  import org.apache.hc.client5.http.auth.CredentialsProvider;
49  import org.apache.hc.client5.http.auth.StandardAuthScheme;
50  import org.apache.hc.client5.http.config.RequestConfig;
51  import org.apache.hc.client5.http.config.TlsConfig;
52  import org.apache.hc.client5.http.cookie.BasicCookieStore;
53  import org.apache.hc.client5.http.cookie.CookieSpecFactory;
54  import org.apache.hc.client5.http.cookie.CookieStore;
55  import org.apache.hc.client5.http.impl.ChainElement;
56  import org.apache.hc.client5.http.impl.CookieSpecSupport;
57  import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
58  import org.apache.hc.client5.http.impl.DefaultClientConnectionReuseStrategy;
59  import org.apache.hc.client5.http.impl.DefaultConnectionKeepAliveStrategy;
60  import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
61  import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
62  import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
63  import org.apache.hc.client5.http.impl.DefaultUserTokenHandler;
64  import org.apache.hc.client5.http.impl.IdleConnectionEvictor;
65  import org.apache.hc.client5.http.impl.NoopUserTokenHandler;
66  import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
67  import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
68  import org.apache.hc.client5.http.impl.auth.BearerSchemeFactory;
69  import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
70  import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider;
71  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
72  import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
73  import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
74  import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
75  import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
76  import org.apache.hc.client5.http.protocol.HttpClientContext;
77  import org.apache.hc.client5.http.protocol.RedirectStrategy;
78  import org.apache.hc.client5.http.protocol.RequestAddCookies;
79  import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
80  import org.apache.hc.client5.http.protocol.RequestExpectContinue;
81  import org.apache.hc.client5.http.protocol.RequestUpgrade;
82  import org.apache.hc.client5.http.protocol.RequestValidateTrace;
83  import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
84  import org.apache.hc.client5.http.routing.HttpRoutePlanner;
85  import org.apache.hc.core5.annotation.Internal;
86  import org.apache.hc.core5.concurrent.DefaultThreadFactory;
87  import org.apache.hc.core5.function.Callback;
88  import org.apache.hc.core5.function.Decorator;
89  import org.apache.hc.core5.http.ConnectionReuseStrategy;
90  import org.apache.hc.core5.http.Header;
91  import org.apache.hc.core5.http.HttpHost;
92  import org.apache.hc.core5.http.HttpRequestInterceptor;
93  import org.apache.hc.core5.http.HttpResponseInterceptor;
94  import org.apache.hc.core5.http.config.CharCodingConfig;
95  import org.apache.hc.core5.http.config.Http1Config;
96  import org.apache.hc.core5.http.config.Lookup;
97  import org.apache.hc.core5.http.config.NamedElementChain;
98  import org.apache.hc.core5.http.config.RegistryBuilder;
99  import org.apache.hc.core5.http.nio.command.ShutdownCommand;
100 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
101 import org.apache.hc.core5.http.protocol.HttpContext;
102 import org.apache.hc.core5.http.protocol.HttpProcessor;
103 import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
104 import org.apache.hc.core5.http.protocol.RequestTargetHost;
105 import org.apache.hc.core5.http.protocol.RequestUserAgent;
106 import org.apache.hc.core5.http2.HttpVersionPolicy;
107 import org.apache.hc.core5.http2.config.H2Config;
108 import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
109 import org.apache.hc.core5.http2.protocol.H2RequestContent;
110 import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
111 import org.apache.hc.core5.io.CloseMode;
112 import org.apache.hc.core5.pool.ConnPoolControl;
113 import org.apache.hc.core5.reactor.Command;
114 import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
115 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
116 import org.apache.hc.core5.reactor.IOReactorConfig;
117 import org.apache.hc.core5.reactor.IOSession;
118 import org.apache.hc.core5.reactor.IOSessionListener;
119 import org.apache.hc.core5.util.Args;
120 import org.apache.hc.core5.util.TimeValue;
121 import org.apache.hc.core5.util.VersionInfo;
122 
123 /**
124  * Builder for {@link CloseableHttpAsyncClient} instances that can negotiate
125  * the most optimal HTTP protocol version during the {@code TLS} handshake
126  * with {@code ALPN} extension if supported by the Java runtime.
127  * <p>
128  * Concurrent message exchanges executed by {@link CloseableHttpAsyncClient}
129  * instances created with this builder will get automatically assigned to
130  * separate connections leased from the connection pool.
131  * </p>
132  * <p>
133  * When a particular component is not explicitly set this class will
134  * use its default implementation. System properties will be taken
135  * into account when configuring the default implementations when
136  * {@link #useSystemProperties()} method is called prior to calling
137  * {@link #build()}.
138  * </p>
139  * <ul>
140  *  <li>http.proxyHost</li>
141  *  <li>http.proxyPort</li>
142  *  <li>https.proxyHost</li>
143  *  <li>https.proxyPort</li>
144  *  <li>http.nonProxyHosts</li>
145  *  <li>http.keepAlive</li>
146  *  <li>http.agent</li>
147  * </ul>
148  * <p>
149  * Please note that some settings used by this class can be mutually
150  * exclusive and may not apply when building {@link CloseableHttpAsyncClient}
151  * instances.
152  * </p>
153  *
154  * @since 5.0
155  */
156 public class HttpAsyncClientBuilder {
157 
158     private static class RequestInterceptorEntry {
159 
160         enum Position { FIRST, LAST }
161 
162         final RequestInterceptorEntry.Position position;
163         final HttpRequestInterceptor interceptor;
164 
165         private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) {
166             this.position = position;
167             this.interceptor = interceptor;
168         }
169     }
170 
171     private static class ResponseInterceptorEntry {
172 
173         enum Position { FIRST, LAST }
174 
175         final ResponseInterceptorEntry.Position position;
176         final HttpResponseInterceptor interceptor;
177 
178         private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) {
179             this.position = position;
180             this.interceptor = interceptor;
181         }
182     }
183 
184     private static class ExecInterceptorEntry {
185 
186         enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST }
187 
188         final ExecInterceptorEntry.Position position;
189         final String name;
190         final AsyncExecChainHandler interceptor;
191         final String existing;
192 
193         private ExecInterceptorEntry(
194                 final ExecInterceptorEntry.Position position,
195                 final String name,
196                 final AsyncExecChainHandler interceptor,
197                 final String existing) {
198             this.position = position;
199             this.name = name;
200             this.interceptor = interceptor;
201             this.existing = existing;
202         }
203 
204     }
205 
206     /**
207      * @deprecated TLS should be configured by the connection manager
208      */
209     @Deprecated
210     private TlsConfig tlsConfig;
211     private AsyncClientConnectionManager connManager;
212     private boolean connManagerShared;
213     private IOReactorConfig ioReactorConfig;
214     private IOSessionListener ioSessionListener;
215     private Callback<Exception> ioReactorExceptionCallback;
216     private Http1Config h1Config;
217     private H2Config h2Config;
218     private CharCodingConfig charCodingConfig;
219     private SchemePortResolver schemePortResolver;
220     private ConnectionKeepAliveStrategy keepAliveStrategy;
221     private UserTokenHandler userTokenHandler;
222     private AuthenticationStrategy targetAuthStrategy;
223     private AuthenticationStrategy proxyAuthStrategy;
224     private Decorator<IOSession> ioSessionDecorator;
225 
226     private LinkedList<RequestInterceptorEntry> requestInterceptors;
227     private LinkedList<ResponseInterceptorEntry> responseInterceptors;
228     private LinkedList<ExecInterceptorEntry> execInterceptors;
229 
230     private HttpRoutePlanner routePlanner;
231     private RedirectStrategy redirectStrategy;
232     private HttpRequestRetryStrategy retryStrategy;
233 
234     private ConnectionReuseStrategy reuseStrategy;
235 
236     private Lookup<AuthSchemeFactory> authSchemeRegistry;
237     private Lookup<CookieSpecFactory> cookieSpecRegistry;
238     private CookieStore cookieStore;
239     private CredentialsProvider credentialsProvider;
240 
241     private String userAgent;
242     private HttpHost proxy;
243     private Collection<? extends Header> defaultHeaders;
244     private RequestConfig defaultRequestConfig;
245     private boolean evictExpiredConnections;
246     private boolean evictIdleConnections;
247     private TimeValue maxIdleTime;
248 
249     private boolean systemProperties;
250     private boolean automaticRetriesDisabled;
251     private boolean redirectHandlingDisabled;
252     private boolean cookieManagementDisabled;
253     private boolean authCachingDisabled;
254     private boolean connectionStateDisabled;
255 
256     private ThreadFactory threadFactory;
257 
258     private List<Closeable> closeables;
259 
260     private ProxySelector proxySelector;
261 
262     public static HttpAsyncClientBuilder create() {
263         return new HttpAsyncClientBuilder();
264     }
265 
266     protected HttpAsyncClientBuilder() {
267         super();
268     }
269 
270     /**
271      * Sets HTTP protocol version policy.
272      *
273      * @return this instance.
274      * @deprecated Use {@link TlsConfig} and connection manager methods
275      */
276     @Deprecated
277     public final HttpAsyncClientBuilder setVersionPolicy(final HttpVersionPolicy versionPolicy) {
278         this.tlsConfig = versionPolicy != null ? TlsConfig.custom().setVersionPolicy(versionPolicy).build() : null;
279         return this;
280     }
281 
282     /**
283      * Sets {@link Http1Config} configuration.
284      *
285      * @return this instance.
286      */
287     public final HttpAsyncClientBuilder setHttp1Config(final Http1Config h1Config) {
288         this.h1Config = h1Config;
289         return this;
290     }
291 
292     /**
293      * Sets {@link H2Config} configuration.
294      *
295      * @return this instance.
296      */
297     public final HttpAsyncClientBuilder setH2Config(final H2Config h2Config) {
298         this.h2Config = h2Config;
299         return this;
300     }
301 
302     /**
303      * Sets {@link AsyncClientConnectionManager} instance.
304      *
305      * @return this instance.
306      */
307     public final HttpAsyncClientBuilder setConnectionManager(final AsyncClientConnectionManager connManager) {
308         this.connManager = connManager;
309         return this;
310     }
311 
312     /**
313      * Defines the connection manager is to be shared by multiple
314      * client instances.
315      * <p>
316      * If the connection manager is shared its life-cycle is expected
317      * to be managed by the caller and it will not be shut down
318      * if the client is closed.
319      * </p>
320      *
321      * @param shared defines whether or not the connection manager can be shared
322      *  by multiple clients.
323      *
324      * @return this instance.
325      */
326     public final HttpAsyncClientBuilder setConnectionManagerShared(final boolean shared) {
327         this.connManagerShared = shared;
328         return this;
329     }
330 
331     /**
332      * Sets {@link IOReactorConfig} configuration.
333      *
334      * @return this instance.
335      */
336     public final HttpAsyncClientBuilder setIOReactorConfig(final IOReactorConfig ioReactorConfig) {
337         this.ioReactorConfig = ioReactorConfig;
338         return this;
339     }
340 
341     /**
342      * Sets {@link IOSessionListener} listener.
343      *
344      * @return this instance.
345      * @since 5.2
346      */
347     public final HttpAsyncClientBuilder setIOSessionListener(final IOSessionListener ioSessionListener) {
348         this.ioSessionListener = ioSessionListener;
349         return this;
350     }
351 
352     /**
353      * Sets the callback that will be invoked when the client's IOReactor encounters an uncaught exception.
354      *
355      * @return this instance.
356      * @since 5.1
357      */
358     public final HttpAsyncClientBuilder setIoReactorExceptionCallback(final Callback<Exception> ioReactorExceptionCallback) {
359         this.ioReactorExceptionCallback = ioReactorExceptionCallback;
360         return this;
361     }
362 
363     /**
364      * Sets {@link CharCodingConfig} configuration.
365      *
366      * @return this instance.
367      */
368     public final HttpAsyncClientBuilder setCharCodingConfig(final CharCodingConfig charCodingConfig) {
369         this.charCodingConfig = charCodingConfig;
370         return this;
371     }
372 
373     /**
374      * Sets {@link ConnectionReuseStrategy} instance.
375      * <p>
376      * Please note this strategy applies to HTTP/1.0 and HTTP/1.1 connections only
377      * </p>
378      *
379      * @return this instance.
380      */
381     public final HttpAsyncClientBuilder setConnectionReuseStrategy(final ConnectionReuseStrategy reuseStrategy) {
382         this.reuseStrategy = reuseStrategy;
383         return this;
384     }
385 
386     /**
387      * Sets {@link ConnectionKeepAliveStrategy} instance.
388      *
389      * @return this instance.
390      */
391     public final HttpAsyncClientBuilder setKeepAliveStrategy(final ConnectionKeepAliveStrategy keepAliveStrategy) {
392         this.keepAliveStrategy = keepAliveStrategy;
393         return this;
394     }
395 
396     /**
397      * Sets {@link UserTokenHandler} instance.
398      * <p>
399      * Please note this value can be overridden by the {@link #disableConnectionState()}
400      * method.
401      * </p>
402      *
403      * @return this instance.
404      */
405     public final HttpAsyncClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
406         this.userTokenHandler = userTokenHandler;
407         return this;
408     }
409 
410     /**
411      * Sets {@link AuthenticationStrategy} instance for target
412      * host authentication.
413      *
414      * @return this instance.
415      */
416     public final HttpAsyncClientBuilder setTargetAuthenticationStrategy(
417             final AuthenticationStrategy targetAuthStrategy) {
418         this.targetAuthStrategy = targetAuthStrategy;
419         return this;
420     }
421 
422     /**
423      * Sets {@link AuthenticationStrategy} instance for proxy
424      * authentication.
425      *
426      * @return this instance.
427      */
428     public final HttpAsyncClientBuilder setProxyAuthenticationStrategy(
429             final AuthenticationStrategy proxyAuthStrategy) {
430         this.proxyAuthStrategy = proxyAuthStrategy;
431         return this;
432     }
433 
434     /**
435      * Sets the {@link IOSession} {@link Decorator} that will be use with the client's IOReactor.
436      *
437      * @return this instance.
438      * @since 5.2
439      */
440     public final HttpAsyncClientBuilder setIoSessionDecorator(final Decorator<IOSession> ioSessionDecorator) {
441         this.ioSessionDecorator = ioSessionDecorator;
442         return this;
443     }
444 
445     /**
446      * Adds this protocol interceptor to the head of the protocol processing list.
447      *
448      * @return this instance.
449      */
450     public final HttpAsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
451         Args.notNull(interceptor, "Interceptor");
452         if (responseInterceptors == null) {
453             responseInterceptors = new LinkedList<>();
454         }
455         responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor));
456         return this;
457     }
458 
459     /**
460      * Adds this protocol interceptor to the tail of the protocol processing list.
461      *
462      * @return this instance.
463      */
464     public final HttpAsyncClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) {
465         Args.notNull(interceptor, "Interceptor");
466         if (responseInterceptors == null) {
467             responseInterceptors = new LinkedList<>();
468         }
469         responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor));
470         return this;
471     }
472 
473     /**
474      * Adds this execution interceptor before an existing interceptor.
475      *
476      * @return this instance.
477      */
478     public final HttpAsyncClientBuilder addExecInterceptorBefore(final String existing, final String name, final AsyncExecChainHandler interceptor) {
479         Args.notBlank(existing, "Existing");
480         Args.notBlank(name, "Name");
481         Args.notNull(interceptor, "Interceptor");
482         if (execInterceptors == null) {
483             execInterceptors = new LinkedList<>();
484         }
485         execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing));
486         return this;
487     }
488 
489     /**
490      * Adds this execution interceptor after interceptor with the given name.
491      *
492      * @return this instance.
493      */
494     public final HttpAsyncClientBuilder addExecInterceptorAfter(final String existing, final String name, final AsyncExecChainHandler interceptor) {
495         Args.notBlank(existing, "Existing");
496         Args.notBlank(name, "Name");
497         Args.notNull(interceptor, "Interceptor");
498         if (execInterceptors == null) {
499             execInterceptors = new LinkedList<>();
500         }
501         execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing));
502         return this;
503     }
504 
505     /**
506      * Replace an existing interceptor with the given name with new interceptor.
507      *
508      * @return this instance.
509      */
510     public final HttpAsyncClientBuilder replaceExecInterceptor(final String existing, final AsyncExecChainHandler interceptor) {
511         Args.notBlank(existing, "Existing");
512         Args.notNull(interceptor, "Interceptor");
513         if (execInterceptors == null) {
514             execInterceptors = new LinkedList<>();
515         }
516         execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing));
517         return this;
518     }
519 
520     /**
521      * Add an interceptor to the head of the processing list.
522      *
523      * @return this instance.
524      */
525     public final HttpAsyncClientBuilder addExecInterceptorFirst(final String name, final AsyncExecChainHandler interceptor) {
526         Args.notNull(name, "Name");
527         Args.notNull(interceptor, "Interceptor");
528         if (execInterceptors == null) {
529             execInterceptors = new LinkedList<>();
530         }
531         execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null));
532         return this;
533     }
534 
535     /**
536      * Add an interceptor to the tail of the processing list.
537      *
538      * @return this instance.
539      */
540     public final HttpAsyncClientBuilder addExecInterceptorLast(final String name, final AsyncExecChainHandler interceptor) {
541         Args.notNull(name, "Name");
542         Args.notNull(interceptor, "Interceptor");
543         if (execInterceptors == null) {
544             execInterceptors = new LinkedList<>();
545         }
546         execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null));
547         return this;
548     }
549 
550     /**
551      * Adds this protocol interceptor to the head of the protocol processing list.
552      *
553      * @return this instance.
554      */
555     public final HttpAsyncClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) {
556         Args.notNull(interceptor, "Interceptor");
557         if (requestInterceptors == null) {
558             requestInterceptors = new LinkedList<>();
559         }
560         requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor));
561         return this;
562     }
563 
564     /**
565      * Adds this protocol interceptor to the tail of the protocol processing list.
566      *
567      * @return this instance.
568      */
569     public final HttpAsyncClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) {
570         Args.notNull(interceptor, "Interceptor");
571         if (requestInterceptors == null) {
572             requestInterceptors = new LinkedList<>();
573         }
574         requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor));
575         return this;
576     }
577 
578     /**
579      * Sets {@link HttpRequestRetryStrategy} instance.
580      * <p>
581      * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
582      * method.
583      * </p>
584      *
585      * @return this instance.
586      */
587     public final HttpAsyncClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) {
588         this.retryStrategy = retryStrategy;
589         return this;
590     }
591 
592     /**
593      * Sets {@link RedirectStrategy} instance.
594      * <p>
595      * Please note this value can be overridden by the {@link #disableRedirectHandling()}
596      * method.
597      * </p>
598      *
599      * @return this instance.
600      */
601     public HttpAsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
602         this.redirectStrategy = redirectStrategy;
603         return this;
604     }
605 
606     /**
607      * Sets {@link SchemePortResolver} instance.
608      *
609      * @return this instance.
610      */
611     public final HttpAsyncClientBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) {
612         this.schemePortResolver = schemePortResolver;
613         return this;
614     }
615 
616     /**
617      * Sets {@link ThreadFactory} instance.
618      *
619      * @return this instance.
620      */
621     public final HttpAsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) {
622         this.threadFactory = threadFactory;
623         return this;
624     }
625 
626     /**
627      * Sets {@code User-Agent} value.
628      *
629      * @return this instance.
630      */
631     public final HttpAsyncClientBuilder setUserAgent(final String userAgent) {
632         this.userAgent = userAgent;
633         return this;
634     }
635 
636     /**
637      * Sets default request header values.
638      *
639      * @return this instance.
640      */
641     public final HttpAsyncClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
642         this.defaultHeaders = defaultHeaders;
643         return this;
644     }
645 
646     /**
647      * Sets the {@link ProxySelector} that will be used to select the proxies
648      * to be used for establishing HTTP connections. If a non-null proxy selector is set,
649      * it will take precedence over the proxy settings configured in the client.
650      *
651      * @param proxySelector the {@link ProxySelector} to be used, or null to use
652      *                      the default system proxy selector.
653      * @return this instance.
654      */
655     public final HttpAsyncClientBuilder setProxySelector(final ProxySelector proxySelector) {
656         this.proxySelector = proxySelector;
657         return this;
658     }
659 
660     /**
661      * Sets default proxy value.
662      * <p>
663      * Please note this value can be overridden by the {@link #setRoutePlanner(
664      *   HttpRoutePlanner)} method.
665      * </p>
666      * @return this instance.
667      */
668     public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) {
669         this.proxy = proxy;
670         return this;
671     }
672 
673     /**
674      * Sets {@link HttpRoutePlanner} instance.
675      *
676      * @return this instance.
677      */
678     public final HttpAsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
679         this.routePlanner = routePlanner;
680         return this;
681     }
682 
683     /**
684      * Sets default {@link CredentialsProvider} instance which will be used
685      * for request execution if not explicitly set in the client execution
686      * context.
687      *
688      * @return this instance.
689      */
690     public final HttpAsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) {
691         this.credentialsProvider = credentialsProvider;
692         return this;
693     }
694 
695     /**
696      * Sets default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will
697      * be used for request execution if not explicitly set in the client execution
698      * context.
699      *
700      * @return this instance.
701      */
702     public final HttpAsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
703         this.authSchemeRegistry = authSchemeRegistry;
704         return this;
705     }
706 
707     /**
708      * Sets default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry
709      * which will be used for request execution if not explicitly set in the client
710      * execution context.
711      *
712      * @return this instance.
713      */
714     public final HttpAsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
715         this.cookieSpecRegistry = cookieSpecRegistry;
716         return this;
717     }
718 
719     /**
720      * Sets default {@link CookieStore} instance which will be used for
721      * request execution if not explicitly set in the client execution context.
722      *
723      * @return this instance.
724      */
725     public final HttpAsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
726         this.cookieStore = cookieStore;
727         return this;
728     }
729 
730     /**
731      * Sets default {@link RequestConfig} instance which will be used
732      * for request execution if not explicitly set in the client execution
733      * context.
734      *
735      * @return this instance.
736      */
737     public final HttpAsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) {
738         this.defaultRequestConfig = config;
739         return this;
740     }
741 
742     /**
743      * Use system properties when creating and configuring default
744      * implementations.
745      *
746      * @return this instance.
747      */
748     public final HttpAsyncClientBuilder useSystemProperties() {
749         this.systemProperties = true;
750         return this;
751     }
752 
753     /**
754      * Disables connection state tracking.
755      *
756      * @return this instance.
757      */
758     public final HttpAsyncClientBuilder disableConnectionState() {
759         connectionStateDisabled = true;
760         return this;
761     }
762 
763     /**
764      * Disables automatic redirect handling.
765      *
766      * @return this instance.
767      */
768     public final HttpAsyncClientBuilder disableRedirectHandling() {
769         redirectHandlingDisabled = true;
770         return this;
771     }
772 
773     /**
774      * Disables automatic request recovery and re-execution.
775      *
776      * @return this instance.
777      */
778     public final HttpAsyncClientBuilder disableAutomaticRetries() {
779         automaticRetriesDisabled = true;
780         return this;
781     }
782 
783     /**
784      * Disables state (cookie) management.
785      *
786      * @return this instance.
787      */
788     public final HttpAsyncClientBuilder disableCookieManagement() {
789         this.cookieManagementDisabled = true;
790         return this;
791     }
792 
793     /**
794      * Disables authentication scheme caching.
795      *
796      * @return this instance.
797      */
798     public final HttpAsyncClientBuilder disableAuthCaching() {
799         this.authCachingDisabled = true;
800         return this;
801     }
802 
803     /**
804      * Makes this instance of HttpClient proactively evict expired connections from the
805      * connection pool using a background thread.
806      * <p>
807      * One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} in order
808      * to stop and release the background thread.
809      * <p>
810      * Please note this method has no effect if the instance of HttpClient is configured to
811      * use a shared connection manager.
812      *
813      * @see #setConnectionManagerShared(boolean)
814      * @see ConnPoolControl#closeExpired()
815      *
816      * @return this instance.
817      */
818     public final HttpAsyncClientBuilder evictExpiredConnections() {
819         evictExpiredConnections = true;
820         return this;
821     }
822 
823     /**
824      * Makes this instance of HttpClient proactively evict idle connections from the
825      * connection pool using a background thread.
826      * <p>
827      * One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()}
828      * in order to stop and release the background thread.
829      * <p>
830      * Please note this method has no effect if the instance of HttpClient is configured to
831      * use a shared connection manager.
832      *
833      * @see #setConnectionManagerShared(boolean)
834      * @see ConnPoolControl#closeIdle(TimeValue)
835      *
836      * @param maxIdleTime maximum time persistent connections can stay idle while kept alive
837      * in the connection pool. Connections whose inactivity period exceeds this value will
838      * get closed and evicted from the pool.
839      *
840      * @return this instance.
841      */
842     public final HttpAsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
843         this.evictIdleConnections = true;
844         this.maxIdleTime = maxIdleTime;
845         return this;
846     }
847 
848     /**
849      * Request exec chain customization and extension.
850      * <p>
851      * For internal use.
852      * </p>
853      */
854     @Internal
855     protected void customizeExecChain(final NamedElementChain<AsyncExecChainHandler> execChainDefinition) {
856     }
857 
858     /**
859      * Adds to the list of {@link Closeable} resources to be managed by the client.
860      * <p>
861      * For internal use.
862      * </p>
863      */
864     @Internal
865     protected void addCloseable(final Closeable closeable) {
866         if (closeable == null) {
867             return;
868         }
869         if (closeables == null) {
870             closeables = new ArrayList<>();
871         }
872         closeables.add(closeable);
873     }
874 
875     @Internal
876     protected Function<HttpContext, HttpClientContext> contextAdaptor() {
877         return HttpClientContext::castOrCreate;
878     }
879 
880     @SuppressWarnings("deprecated")
881     public CloseableHttpAsyncClient build() {
882         AsyncClientConnectionManager connManagerCopy = this.connManager;
883         if (connManagerCopy == null) {
884             final PoolingAsyncClientConnectionManagerBuilder connectionManagerBuilder = PoolingAsyncClientConnectionManagerBuilder.create();
885             if (systemProperties) {
886                 connectionManagerBuilder.useSystemProperties();
887             }
888             connManagerCopy = connectionManagerBuilder.build();
889         }
890 
891         ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
892         if (keepAliveStrategyCopy == null) {
893             keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
894         }
895 
896         UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
897         if (userTokenHandlerCopy == null) {
898             if (!connectionStateDisabled) {
899                 userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
900             } else {
901                 userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
902             }
903         }
904 
905         AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
906         if (targetAuthStrategyCopy == null) {
907             targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
908         }
909         AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
910         if (proxyAuthStrategyCopy == null) {
911             proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
912         }
913 
914         String userAgentCopy = this.userAgent;
915         if (userAgentCopy == null) {
916             if (systemProperties) {
917                 userAgentCopy = getProperty("http.agent", null);
918             }
919             if (userAgentCopy == null) {
920                 userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient",
921                         "org.apache.hc.client5", getClass());
922             }
923         }
924 
925         final HttpProcessorBuilder b = HttpProcessorBuilder.create();
926         if (requestInterceptors != null) {
927             for (final RequestInterceptorEntry entry: requestInterceptors) {
928                 if (entry.position == RequestInterceptorEntry.Position.FIRST) {
929                     b.addFirst(entry.interceptor);
930                 }
931             }
932         }
933         if (responseInterceptors != null) {
934             for (final ResponseInterceptorEntry entry: responseInterceptors) {
935                 if (entry.position == ResponseInterceptorEntry.Position.FIRST) {
936                     b.addFirst(entry.interceptor);
937                 }
938             }
939         }
940         b.addAll(
941                 new H2RequestTargetHost(),
942                 new RequestValidateTrace(),
943                 new RequestDefaultHeaders(defaultHeaders),
944                 new H2RequestContent(),
945                 new H2RequestConnControl(),
946                 new RequestUserAgent(userAgentCopy),
947                 new RequestExpectContinue(),
948                 new RequestUpgrade());
949         if (!cookieManagementDisabled) {
950             b.add(RequestAddCookies.INSTANCE);
951         }
952         if (!cookieManagementDisabled) {
953             b.add(ResponseProcessCookies.INSTANCE);
954         }
955         if (requestInterceptors != null) {
956             for (final RequestInterceptorEntry entry: requestInterceptors) {
957                 if (entry.position == RequestInterceptorEntry.Position.LAST) {
958                     b.addLast(entry.interceptor);
959                 }
960             }
961         }
962         if (responseInterceptors != null) {
963             for (final ResponseInterceptorEntry entry: responseInterceptors) {
964                 if (entry.position == ResponseInterceptorEntry.Position.LAST) {
965                     b.addLast(entry.interceptor);
966                 }
967             }
968         }
969 
970         final HttpProcessor httpProcessor = b.build();
971 
972         final NamedElementChain<AsyncExecChainHandler> execChainDefinition = new NamedElementChain<>();
973         execChainDefinition.addLast(
974                 new HttpAsyncMainClientExec(httpProcessor, keepAliveStrategyCopy, userTokenHandlerCopy),
975                 ChainElement.MAIN_TRANSPORT.name());
976 
977         execChainDefinition.addFirst(
978                 new AsyncConnectExec(
979                         new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
980                         proxyAuthStrategyCopy,
981                         schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
982                         authCachingDisabled),
983                 ChainElement.CONNECT.name());
984 
985         execChainDefinition.addFirst(
986                 new AsyncProtocolExec(
987                         targetAuthStrategyCopy,
988                         proxyAuthStrategyCopy,
989                         schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
990                         authCachingDisabled),
991                 ChainElement.PROTOCOL.name());
992 
993         // Add request retry executor, if not disabled
994         if (!automaticRetriesDisabled) {
995             HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
996             if (retryStrategyCopy == null) {
997                 retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
998             }
999             execChainDefinition.addFirst(
1000                     new AsyncHttpRequestRetryExec(retryStrategyCopy),
1001                     ChainElement.RETRY.name());
1002         }
1003 
1004         HttpRoutePlanner routePlannerCopy = this.routePlanner;
1005         if (routePlannerCopy == null) {
1006             SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
1007             if (schemePortResolverCopy == null) {
1008                 schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
1009             }
1010             if (proxy != null) {
1011                 routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
1012             } else if (this.proxySelector != null) {
1013                 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, this.proxySelector);
1014             } else if (systemProperties) {
1015                 final ProxySelector defaultProxySelector = AccessController.doPrivileged((PrivilegedAction<ProxySelector>) ProxySelector::getDefault);
1016                 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, defaultProxySelector);
1017             } else {
1018                 routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
1019             }
1020         }
1021 
1022         // Add redirect executor, if not disabled
1023         if (!redirectHandlingDisabled) {
1024             RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
1025             if (redirectStrategyCopy == null) {
1026                 redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
1027             }
1028             execChainDefinition.addFirst(
1029                     new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),
1030                     ChainElement.REDIRECT.name());
1031         }
1032 
1033         List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
1034         if (!this.connManagerShared) {
1035             if (closeablesCopy == null) {
1036                 closeablesCopy = new ArrayList<>(1);
1037             }
1038             if (evictExpiredConnections || evictIdleConnections) {
1039                 if (connManagerCopy instanceof ConnPoolControl) {
1040                     final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl<?>) connManagerCopy,
1041                             maxIdleTime, maxIdleTime);
1042                     closeablesCopy.add(connectionEvictor::shutdown);
1043                     connectionEvictor.start();
1044                 }
1045             }
1046             closeablesCopy.add(connManagerCopy);
1047         }
1048         ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
1049         if (reuseStrategyCopy == null) {
1050             if (systemProperties) {
1051                 final String s = getProperty("http.keepAlive", "true");
1052                 if ("true".equalsIgnoreCase(s)) {
1053                     reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
1054                 } else {
1055                     reuseStrategyCopy = (request, response, context) -> false;
1056                 }
1057             } else {
1058                 reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
1059             }
1060         }
1061         final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
1062         final IOEventHandlerFactory ioEventHandlerFactory = new HttpAsyncClientProtocolNegotiationStarter(
1063                 HttpProcessorBuilder.create().build(),
1064                 (request, context) -> pushConsumerRegistry.get(request),
1065                 h2Config != null ? h2Config : H2Config.DEFAULT,
1066                 h1Config != null ? h1Config : Http1Config.DEFAULT,
1067                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
1068                 reuseStrategyCopy,
1069                 ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE);
1070         final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(
1071                 ioEventHandlerFactory,
1072                 ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT,
1073                 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true),
1074                 ioSessionDecorator != null ? ioSessionDecorator : LoggingIOSessionDecorator.INSTANCE,
1075                 ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE,
1076                 ioSessionListener,
1077                 ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE));
1078 
1079         if (execInterceptors != null) {
1080             for (final ExecInterceptorEntry entry: execInterceptors) {
1081                 switch (entry.position) {
1082                     case AFTER:
1083                         execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name);
1084                         break;
1085                     case BEFORE:
1086                         execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name);
1087                         break;
1088                     case REPLACE:
1089                         execChainDefinition.replace(entry.existing, entry.interceptor);
1090                         break;
1091                     case FIRST:
1092                         execChainDefinition.addFirst(entry.interceptor, entry.name);
1093                         break;
1094                     case LAST:
1095                         // Don't add last, after HttpAsyncMainClientExec, as that does not delegate to the chain
1096                         // Instead, add the interceptor just before it, making it effectively the last interceptor
1097                         execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name);
1098                         break;
1099                 }
1100             }
1101         }
1102 
1103         customizeExecChain(execChainDefinition);
1104 
1105         NamedElementChain<AsyncExecChainHandler>.Node current = execChainDefinition.getLast();
1106         AsyncExecChainElement execChain = null;
1107         while (current != null) {
1108             execChain = new AsyncExecChainElement(current.getValue(), execChain);
1109             current = current.getPrevious();
1110         }
1111 
1112         Lookup<AuthSchemeFactory> authSchemeRegistryCopy = this.authSchemeRegistry;
1113         if (authSchemeRegistryCopy == null) {
1114             authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeFactory>create()
1115                     .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE)
1116                     .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE)
1117                     .register(StandardAuthScheme.BEARER, BearerSchemeFactory.INSTANCE)
1118                     .build();
1119         }
1120         Lookup<CookieSpecFactory> cookieSpecRegistryCopy = this.cookieSpecRegistry;
1121         if (cookieSpecRegistryCopy == null) {
1122             cookieSpecRegistryCopy = CookieSpecSupport.createDefault();
1123         }
1124 
1125         CookieStore cookieStoreCopy = this.cookieStore;
1126         if (cookieStoreCopy == null) {
1127             cookieStoreCopy = new BasicCookieStore();
1128         }
1129 
1130         CredentialsProvider credentialsProviderCopy = this.credentialsProvider;
1131         if (credentialsProviderCopy == null) {
1132             if (systemProperties) {
1133                 credentialsProviderCopy = new SystemDefaultCredentialsProvider();
1134             } else {
1135                 credentialsProviderCopy = new BasicCredentialsProvider();
1136             }
1137         }
1138 
1139         return new InternalHttpAsyncClient(
1140                 ioReactor,
1141                 execChain,
1142                 pushConsumerRegistry,
1143                 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true),
1144                 connManagerCopy,
1145                 routePlannerCopy,
1146                 tlsConfig,
1147                 cookieSpecRegistryCopy,
1148                 authSchemeRegistryCopy,
1149                 cookieStoreCopy,
1150                 credentialsProviderCopy,
1151                 contextAdaptor(),
1152                 defaultRequestConfig,
1153                 closeablesCopy);
1154     }
1155 
1156     private String getProperty(final String key, final String defaultValue) {
1157         return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(key, defaultValue));
1158     }
1159 
1160 }