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