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