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