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