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