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