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