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