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.BearerSchemeFactory;
68  import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
69  import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider;
70  import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder;
71  import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
72  import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
73  import org.apache.hc.client5.http.impl.routing.SystemDefaultRoutePlanner;
74  import org.apache.hc.client5.http.nio.AsyncClientConnectionManager;
75  import org.apache.hc.client5.http.protocol.RedirectStrategy;
76  import org.apache.hc.client5.http.protocol.RequestAddCookies;
77  import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
78  import org.apache.hc.client5.http.protocol.RequestExpectContinue;
79  import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
80  import org.apache.hc.client5.http.routing.HttpRoutePlanner;
81  import org.apache.hc.core5.annotation.Internal;
82  import org.apache.hc.core5.concurrent.DefaultThreadFactory;
83  import org.apache.hc.core5.function.Callback;
84  import org.apache.hc.core5.function.Decorator;
85  import org.apache.hc.core5.http.ConnectionReuseStrategy;
86  import org.apache.hc.core5.http.Header;
87  import org.apache.hc.core5.http.HttpHost;
88  import org.apache.hc.core5.http.HttpRequestInterceptor;
89  import org.apache.hc.core5.http.HttpResponseInterceptor;
90  import org.apache.hc.core5.http.config.CharCodingConfig;
91  import org.apache.hc.core5.http.config.Http1Config;
92  import org.apache.hc.core5.http.config.Lookup;
93  import org.apache.hc.core5.http.config.NamedElementChain;
94  import org.apache.hc.core5.http.config.RegistryBuilder;
95  import org.apache.hc.core5.http.nio.command.ShutdownCommand;
96  import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
97  import org.apache.hc.core5.http.protocol.HttpProcessor;
98  import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
99  import org.apache.hc.core5.http.protocol.RequestTargetHost;
100 import org.apache.hc.core5.http.protocol.RequestUserAgent;
101 import org.apache.hc.core5.http2.HttpVersionPolicy;
102 import org.apache.hc.core5.http2.config.H2Config;
103 import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
104 import org.apache.hc.core5.http2.protocol.H2RequestContent;
105 import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
106 import org.apache.hc.core5.io.CloseMode;
107 import org.apache.hc.core5.pool.ConnPoolControl;
108 import org.apache.hc.core5.reactor.Command;
109 import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
110 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
111 import org.apache.hc.core5.reactor.IOReactorConfig;
112 import org.apache.hc.core5.reactor.IOSession;
113 import org.apache.hc.core5.reactor.IOSessionListener;
114 import org.apache.hc.core5.util.Args;
115 import org.apache.hc.core5.util.TimeValue;
116 import org.apache.hc.core5.util.VersionInfo;
117 
118 /**
119  * Builder for {@link CloseableHttpAsyncClient} instances that can negotiate
120  * the most optimal HTTP protocol version during the {@code TLS} handshake
121  * with {@code ALPN} extension if supported by the Java runtime.
122  * <p>
123  * Concurrent message exchanges executed by {@link CloseableHttpAsyncClient}
124  * instances created with this builder will get automatically assigned to
125  * separate connections leased from the connection pool.
126  * </p>
127  * <p>
128  * When a particular component is not explicitly set this class will
129  * use its default implementation. System properties will be taken
130  * into account when configuring the default implementations when
131  * {@link #useSystemProperties()} method is called prior to calling
132  * {@link #build()}.
133  * </p>
134  * <ul>
135  *  <li>http.proxyHost</li>
136  *  <li>http.proxyPort</li>
137  *  <li>https.proxyHost</li>
138  *  <li>https.proxyPort</li>
139  *  <li>http.nonProxyHosts</li>
140  *  <li>http.keepAlive</li>
141  *  <li>http.agent</li>
142  * </ul>
143  * <p>
144  * Please note that some settings used by this class can be mutually
145  * exclusive and may not apply when building {@link CloseableHttpAsyncClient}
146  * instances.
147  * </p>
148  *
149  * @since 5.0
150  */
151 public class HttpAsyncClientBuilder {
152 
153     private static class RequestInterceptorEntry {
154 
155         enum Position { FIRST, LAST }
156 
157         final RequestInterceptorEntry.Position position;
158         final HttpRequestInterceptor interceptor;
159 
160         private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) {
161             this.position = position;
162             this.interceptor = interceptor;
163         }
164     }
165 
166     private static class ResponseInterceptorEntry {
167 
168         enum Position { FIRST, LAST }
169 
170         final ResponseInterceptorEntry.Position position;
171         final HttpResponseInterceptor interceptor;
172 
173         private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) {
174             this.position = position;
175             this.interceptor = interceptor;
176         }
177     }
178 
179     private static class ExecInterceptorEntry {
180 
181         enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST }
182 
183         final ExecInterceptorEntry.Position position;
184         final String name;
185         final AsyncExecChainHandler interceptor;
186         final String existing;
187 
188         private ExecInterceptorEntry(
189                 final ExecInterceptorEntry.Position position,
190                 final String name,
191                 final AsyncExecChainHandler interceptor,
192                 final String existing) {
193             this.position = position;
194             this.name = name;
195             this.interceptor = interceptor;
196             this.existing = existing;
197         }
198 
199     }
200 
201     /**
202      * @deprecated TLS should be configured by the connection manager
203      */
204     @Deprecated
205     private TlsConfig tlsConfig;
206     private AsyncClientConnectionManager connManager;
207     private boolean connManagerShared;
208     private IOReactorConfig ioReactorConfig;
209     private IOSessionListener ioSessionListener;
210     private Callback<Exception> ioReactorExceptionCallback;
211     private Http1Config h1Config;
212     private H2Config h2Config;
213     private CharCodingConfig charCodingConfig;
214     private SchemePortResolver schemePortResolver;
215     private ConnectionKeepAliveStrategy keepAliveStrategy;
216     private UserTokenHandler userTokenHandler;
217     private AuthenticationStrategy targetAuthStrategy;
218     private AuthenticationStrategy proxyAuthStrategy;
219     private Decorator<IOSession> ioSessionDecorator;
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     private ProxySelector proxySelector;
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      * Sets the {@link java.net.ProxySelector} that will be used to select the proxies
584      * to be used for establishing HTTP connections. If a non-null proxy selector is set,
585      * it will take precedence over the proxy settings configured in the client.
586      *
587      * @param proxySelector the {@link java.net.ProxySelector} to be used, or null to use
588      *                      the default system proxy selector.
589      * @return this {@link HttpAsyncClientBuilder} instance, to allow for method chaining.
590      */
591     public final HttpAsyncClientBuilder setProxySelector(final ProxySelector proxySelector) {
592         this.proxySelector = proxySelector;
593         return this;
594     }
595 
596     /**
597      * Assigns default proxy value.
598      * <p>
599      * Please note this value can be overridden by the {@link #setRoutePlanner(
600      *   HttpRoutePlanner)} method.
601      */
602     public final HttpAsyncClientBuilder setProxy(final HttpHost proxy) {
603         this.proxy = proxy;
604         return this;
605     }
606 
607     /**
608      * Assigns {@link HttpRoutePlanner} instance.
609      */
610     public final HttpAsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
611         this.routePlanner = routePlanner;
612         return this;
613     }
614 
615     /**
616      * Assigns default {@link CredentialsProvider} instance which will be used
617      * for request execution if not explicitly set in the client execution
618      * context.
619      */
620     public final HttpAsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) {
621         this.credentialsProvider = credentialsProvider;
622         return this;
623     }
624 
625     /**
626      * Assigns default {@link org.apache.hc.client5.http.auth.AuthScheme} registry which will
627      * be used for request execution if not explicitly set in the client execution
628      * context.
629      */
630     public final HttpAsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
631         this.authSchemeRegistry = authSchemeRegistry;
632         return this;
633     }
634 
635     /**
636      * Assigns default {@link org.apache.hc.client5.http.cookie.CookieSpec} registry
637      * which will be used for request execution if not explicitly set in the client
638      * execution context.
639      */
640     public final HttpAsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
641         this.cookieSpecRegistry = cookieSpecRegistry;
642         return this;
643     }
644 
645     /**
646      * Assigns default {@link CookieStore} instance which will be used for
647      * request execution if not explicitly set in the client execution context.
648      */
649     public final HttpAsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
650         this.cookieStore = cookieStore;
651         return this;
652     }
653 
654     /**
655      * Assigns default {@link RequestConfig} instance which will be used
656      * for request execution if not explicitly set in the client execution
657      * context.
658      */
659     public final HttpAsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) {
660         this.defaultRequestConfig = config;
661         return this;
662     }
663 
664     /**
665      * Use system properties when creating and configuring default
666      * implementations.
667      */
668     public final HttpAsyncClientBuilder useSystemProperties() {
669         this.systemProperties = true;
670         return this;
671     }
672 
673     /**
674      * Disables connection state tracking.
675      */
676     public final HttpAsyncClientBuilder disableConnectionState() {
677         connectionStateDisabled = true;
678         return this;
679     }
680 
681     /**
682      * Disables automatic redirect handling.
683      */
684     public final HttpAsyncClientBuilder disableRedirectHandling() {
685         redirectHandlingDisabled = true;
686         return this;
687     }
688 
689     /**
690      * Disables automatic request recovery and re-execution.
691      */
692     public final HttpAsyncClientBuilder disableAutomaticRetries() {
693         automaticRetriesDisabled = true;
694         return this;
695     }
696 
697     /**
698      * Disables state (cookie) management.
699      */
700     public final HttpAsyncClientBuilder disableCookieManagement() {
701         this.cookieManagementDisabled = true;
702         return this;
703     }
704 
705     /**
706      * Disables authentication scheme caching.
707      */
708     public final HttpAsyncClientBuilder disableAuthCaching() {
709         this.authCachingDisabled = true;
710         return this;
711     }
712 
713     /**
714      * Makes this instance of HttpClient proactively evict expired connections from the
715      * connection pool using a background thread.
716      * <p>
717      * One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()} in order
718      * to stop and release the background thread.
719      * <p>
720      * Please note this method has no effect if the instance of HttpClient is configured to
721      * use a shared connection manager.
722      *
723      * @see #setConnectionManagerShared(boolean)
724      * @see ConnPoolControl#closeExpired()
725      */
726     public final HttpAsyncClientBuilder evictExpiredConnections() {
727         evictExpiredConnections = true;
728         return this;
729     }
730 
731     /**
732      * Makes this instance of HttpClient proactively evict idle connections from the
733      * connection pool using a background thread.
734      * <p>
735      * One MUST explicitly close HttpClient with {@link CloseableHttpAsyncClient#close()}
736      * in order to stop and release the background thread.
737      * <p>
738      * Please note this method has no effect if the instance of HttpClient is configured to
739      * use a shared connection manager.
740      *
741      * @see #setConnectionManagerShared(boolean)
742      * @see ConnPoolControl#closeIdle(TimeValue)
743      *
744      * @param maxIdleTime maximum time persistent connections can stay idle while kept alive
745      * in the connection pool. Connections whose inactivity period exceeds this value will
746      * get closed and evicted from the pool.
747      */
748     public final HttpAsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
749         this.evictIdleConnections = true;
750         this.maxIdleTime = maxIdleTime;
751         return this;
752     }
753 
754     /**
755      * Request exec chain customization and extension.
756      * <p>
757      * For internal use.
758      */
759     @Internal
760     protected void customizeExecChain(final NamedElementChain<AsyncExecChainHandler> execChainDefinition) {
761     }
762 
763     /**
764      * Adds to the list of {@link Closeable} resources to be managed by the client.
765      * <p>
766      * For internal use.
767      */
768     @Internal
769     protected void addCloseable(final Closeable closeable) {
770         if (closeable == null) {
771             return;
772         }
773         if (closeables == null) {
774             closeables = new ArrayList<>();
775         }
776         closeables.add(closeable);
777     }
778     @SuppressWarnings("deprecated")
779     public CloseableHttpAsyncClient build() {
780         AsyncClientConnectionManager connManagerCopy = this.connManager;
781         if (connManagerCopy == null) {
782             connManagerCopy = PoolingAsyncClientConnectionManagerBuilder.create().build();
783         }
784 
785         ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
786         if (keepAliveStrategyCopy == null) {
787             keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
788         }
789 
790         UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
791         if (userTokenHandlerCopy == null) {
792             if (!connectionStateDisabled) {
793                 userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
794             } else {
795                 userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
796             }
797         }
798 
799         AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
800         if (targetAuthStrategyCopy == null) {
801             targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
802         }
803         AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
804         if (proxyAuthStrategyCopy == null) {
805             proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
806         }
807 
808         String userAgentCopy = this.userAgent;
809         if (userAgentCopy == null) {
810             if (systemProperties) {
811                 userAgentCopy = getProperty("http.agent", null);
812             }
813             if (userAgentCopy == null) {
814                 userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient",
815                         "org.apache.hc.client5", getClass());
816             }
817         }
818 
819         final HttpProcessorBuilder b = HttpProcessorBuilder.create();
820         if (requestInterceptors != null) {
821             for (final RequestInterceptorEntry entry: requestInterceptors) {
822                 if (entry.position == RequestInterceptorEntry.Position.FIRST) {
823                     b.addFirst(entry.interceptor);
824                 }
825             }
826         }
827         if (responseInterceptors != null) {
828             for (final ResponseInterceptorEntry entry: responseInterceptors) {
829                 if (entry.position == ResponseInterceptorEntry.Position.FIRST) {
830                     b.addFirst(entry.interceptor);
831                 }
832             }
833         }
834         b.addAll(
835                 new RequestDefaultHeaders(defaultHeaders),
836                 new RequestUserAgent(userAgentCopy),
837                 new RequestExpectContinue(),
838                 new H2RequestContent(),
839                 new H2RequestTargetHost(),
840                 new H2RequestConnControl());
841         if (!cookieManagementDisabled) {
842             b.add(RequestAddCookies.INSTANCE);
843         }
844         if (!cookieManagementDisabled) {
845             b.add(ResponseProcessCookies.INSTANCE);
846         }
847         if (requestInterceptors != null) {
848             for (final RequestInterceptorEntry entry: requestInterceptors) {
849                 if (entry.position == RequestInterceptorEntry.Position.LAST) {
850                     b.addLast(entry.interceptor);
851                 }
852             }
853         }
854         if (responseInterceptors != null) {
855             for (final ResponseInterceptorEntry entry: responseInterceptors) {
856                 if (entry.position == ResponseInterceptorEntry.Position.LAST) {
857                     b.addLast(entry.interceptor);
858                 }
859             }
860         }
861 
862         final HttpProcessor httpProcessor = b.build();
863 
864         final NamedElementChain<AsyncExecChainHandler> execChainDefinition = new NamedElementChain<>();
865         execChainDefinition.addLast(
866                 new HttpAsyncMainClientExec(httpProcessor, keepAliveStrategyCopy, userTokenHandlerCopy),
867                 ChainElement.MAIN_TRANSPORT.name());
868 
869         execChainDefinition.addFirst(
870                 new AsyncConnectExec(
871                         new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
872                         proxyAuthStrategyCopy,
873                         schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
874                         authCachingDisabled),
875                 ChainElement.CONNECT.name());
876 
877         execChainDefinition.addFirst(
878                 new AsyncProtocolExec(
879                         targetAuthStrategyCopy,
880                         proxyAuthStrategyCopy,
881                         schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
882                         authCachingDisabled),
883                 ChainElement.PROTOCOL.name());
884 
885         // Add request retry executor, if not disabled
886         if (!automaticRetriesDisabled) {
887             HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
888             if (retryStrategyCopy == null) {
889                 retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
890             }
891             execChainDefinition.addFirst(
892                     new AsyncHttpRequestRetryExec(retryStrategyCopy),
893                     ChainElement.RETRY.name());
894         }
895 
896         HttpRoutePlanner routePlannerCopy = this.routePlanner;
897         if (routePlannerCopy == null) {
898             SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
899             if (schemePortResolverCopy == null) {
900                 schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
901             }
902             if (proxy != null) {
903                 routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
904             } else if (this.proxySelector != null) {
905                 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, this.proxySelector);
906             } else if (systemProperties) {
907                 final ProxySelector defaultProxySelector = AccessController.doPrivileged((PrivilegedAction<ProxySelector>) ProxySelector::getDefault);
908                 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, defaultProxySelector);
909             } else {
910                 routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
911             }
912         }
913 
914         // Add redirect executor, if not disabled
915         if (!redirectHandlingDisabled) {
916             RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
917             if (redirectStrategyCopy == null) {
918                 redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
919             }
920             execChainDefinition.addFirst(
921                     new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),
922                     ChainElement.REDIRECT.name());
923         }
924 
925         List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
926         if (!this.connManagerShared) {
927             if (closeablesCopy == null) {
928                 closeablesCopy = new ArrayList<>(1);
929             }
930             if (evictExpiredConnections || evictIdleConnections) {
931                 if (connManagerCopy instanceof ConnPoolControl) {
932                     final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl<?>) connManagerCopy,
933                             maxIdleTime,  maxIdleTime);
934                     closeablesCopy.add(connectionEvictor::shutdown);
935                     connectionEvictor.start();
936                 }
937             }
938             closeablesCopy.add(connManagerCopy);
939         }
940         ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
941         if (reuseStrategyCopy == null) {
942             if (systemProperties) {
943                 final String s = getProperty("http.keepAlive", "true");
944                 if ("true".equalsIgnoreCase(s)) {
945                     reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
946                 } else {
947                     reuseStrategyCopy = (request, response, context) -> false;
948                 }
949             } else {
950                 reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
951             }
952         }
953         final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
954         final IOEventHandlerFactory ioEventHandlerFactory = new HttpAsyncClientProtocolNegotiationStarter(
955                 HttpProcessorBuilder.create().build(),
956                 (request, context) -> pushConsumerRegistry.get(request),
957                 h2Config != null ? h2Config : H2Config.DEFAULT,
958                 h1Config != null ? h1Config : Http1Config.DEFAULT,
959                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
960                 reuseStrategyCopy);
961         final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(
962                 ioEventHandlerFactory,
963                 ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT,
964                 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true),
965                 ioSessionDecorator != null ? ioSessionDecorator : LoggingIOSessionDecorator.INSTANCE,
966                 ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE,
967                 ioSessionListener,
968                 ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE));
969 
970         if (execInterceptors != null) {
971             for (final ExecInterceptorEntry entry: execInterceptors) {
972                 switch (entry.position) {
973                     case AFTER:
974                         execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name);
975                         break;
976                     case BEFORE:
977                         execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name);
978                         break;
979                     case REPLACE:
980                         execChainDefinition.replace(entry.existing, entry.interceptor);
981                         break;
982                     case FIRST:
983                         execChainDefinition.addFirst(entry.interceptor, entry.name);
984                         break;
985                     case LAST:
986                         // Don't add last, after HttpAsyncMainClientExec, as that does not delegate to the chain
987                         // Instead, add the interceptor just before it, making it effectively the last interceptor
988                         execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name);
989                         break;
990                 }
991             }
992         }
993 
994         customizeExecChain(execChainDefinition);
995 
996         NamedElementChain<AsyncExecChainHandler>.Node current = execChainDefinition.getLast();
997         AsyncExecChainElement execChain = null;
998         while (current != null) {
999             execChain = new AsyncExecChainElement(current.getValue(), execChain);
1000             current = current.getPrevious();
1001         }
1002 
1003         Lookup<AuthSchemeFactory> authSchemeRegistryCopy = this.authSchemeRegistry;
1004         if (authSchemeRegistryCopy == null) {
1005             authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeFactory>create()
1006                     .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE)
1007                     .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE)
1008                     .register(StandardAuthScheme.BEARER, BearerSchemeFactory.INSTANCE)
1009                     .build();
1010         }
1011         Lookup<CookieSpecFactory> cookieSpecRegistryCopy = this.cookieSpecRegistry;
1012         if (cookieSpecRegistryCopy == null) {
1013             cookieSpecRegistryCopy = CookieSpecSupport.createDefault();
1014         }
1015 
1016         CookieStore cookieStoreCopy = this.cookieStore;
1017         if (cookieStoreCopy == null) {
1018             cookieStoreCopy = new BasicCookieStore();
1019         }
1020 
1021         CredentialsProvider credentialsProviderCopy = this.credentialsProvider;
1022         if (credentialsProviderCopy == null) {
1023             if (systemProperties) {
1024                 credentialsProviderCopy = new SystemDefaultCredentialsProvider();
1025             } else {
1026                 credentialsProviderCopy = new BasicCredentialsProvider();
1027             }
1028         }
1029 
1030         return new InternalHttpAsyncClient(
1031                 ioReactor,
1032                 execChain,
1033                 pushConsumerRegistry,
1034                 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true),
1035                 connManagerCopy,
1036                 routePlannerCopy,
1037                 tlsConfig,
1038                 cookieSpecRegistryCopy,
1039                 authSchemeRegistryCopy,
1040                 cookieStoreCopy,
1041                 credentialsProviderCopy,
1042                 defaultRequestConfig,
1043                 closeablesCopy);
1044     }
1045 
1046     private String getProperty(final String key, final String defaultValue) {
1047         return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(key, defaultValue));
1048     }
1049 
1050 }