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 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
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
141
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
247
248 public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requestExec) {
249 this.requestExec = requestExec;
250 return this;
251 }
252
253
254
255
256 public final HttpClientBuilder setConnectionManager(
257 final HttpClientConnectionManager connManager) {
258 this.connManager = connManager;
259 return this;
260 }
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276 public final HttpClientBuilder setConnectionManagerShared(
277 final boolean shared) {
278 this.connManagerShared = shared;
279 return this;
280 }
281
282
283
284
285 public final HttpClientBuilder setConnectionReuseStrategy(
286 final ConnectionReuseStrategy reuseStrategy) {
287 this.reuseStrategy = reuseStrategy;
288 return this;
289 }
290
291
292
293
294 public final HttpClientBuilder setKeepAliveStrategy(
295 final ConnectionKeepAliveStrategy keepAliveStrategy) {
296 this.keepAliveStrategy = keepAliveStrategy;
297 return this;
298 }
299
300
301
302
303
304 public final HttpClientBuilder setTargetAuthenticationStrategy(
305 final AuthenticationStrategy targetAuthStrategy) {
306 this.targetAuthStrategy = targetAuthStrategy;
307 return this;
308 }
309
310
311
312
313
314 public final HttpClientBuilder setProxyAuthenticationStrategy(
315 final AuthenticationStrategy proxyAuthStrategy) {
316 this.proxyAuthStrategy = proxyAuthStrategy;
317 return this;
318 }
319
320
321
322
323
324
325
326
327 public final HttpClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
328 this.userTokenHandler = userTokenHandler;
329 return this;
330 }
331
332
333
334
335 public final HttpClientBuilder disableConnectionState() {
336 connectionStateDisabled = true;
337 return this;
338 }
339
340
341
342
343 public final HttpClientBuilder setSchemePortResolver(
344 final SchemePortResolver schemePortResolver) {
345 this.schemePortResolver = schemePortResolver;
346 return this;
347 }
348
349
350
351
352 public final HttpClientBuilder setUserAgent(final String userAgent) {
353 this.userAgent = userAgent;
354 return this;
355 }
356
357
358
359
360 public final HttpClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
361 this.defaultHeaders = defaultHeaders;
362 return this;
363 }
364
365
366
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
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
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
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
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
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
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
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
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
482
483 public final HttpClientBuilder disableCookieManagement() {
484 this.cookieManagementDisabled = true;
485 return this;
486 }
487
488
489
490
491 public final HttpClientBuilder disableContentCompression() {
492 contentCompressionDisabled = true;
493 return this;
494 }
495
496
497
498
499 public final HttpClientBuilder disableAuthCaching() {
500 this.authCachingDisabled = true;
501 return this;
502 }
503
504
505
506
507
508
509
510 public final HttpClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) {
511 this.retryStrategy = retryStrategy;
512 return this;
513 }
514
515
516
517
518 public final HttpClientBuilder disableAutomaticRetries() {
519 automaticRetriesDisabled = true;
520 return this;
521 }
522
523
524
525
526
527
528
529 public final HttpClientBuilder setProxy(final HttpHost proxy) {
530 this.proxy = proxy;
531 return this;
532 }
533
534
535
536
537 public final HttpClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
538 this.routePlanner = routePlanner;
539 return this;
540 }
541
542
543
544
545
546
547
548
549 public final HttpClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
550 this.redirectStrategy = redirectStrategy;
551 return this;
552 }
553
554
555
556
557 public final HttpClientBuilder disableRedirectHandling() {
558 redirectHandlingDisabled = true;
559 return this;
560 }
561
562
563
564
565 public final HttpClientBuilder setConnectionBackoffStrategy(
566 final ConnectionBackoffStrategy connectionBackoffStrategy) {
567 this.connectionBackoffStrategy = connectionBackoffStrategy;
568 return this;
569 }
570
571
572
573
574 public final HttpClientBuilder setBackoffManager(final BackoffManager backoffManager) {
575 this.backoffManager = backoffManager;
576 return this;
577 }
578
579
580
581
582
583 public final HttpClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
584 this.cookieStore = cookieStore;
585 return this;
586 }
587
588
589
590
591
592
593 public final HttpClientBuilder setDefaultCredentialsProvider(
594 final CredentialsProvider credentialsProvider) {
595 this.credentialsProvider = credentialsProvider;
596 return this;
597 }
598
599
600
601
602
603
604 public final HttpClientBuilder setDefaultAuthSchemeRegistry(
605 final Lookup<AuthSchemeFactory> authSchemeRegistry) {
606 this.authSchemeRegistry = authSchemeRegistry;
607 return this;
608 }
609
610
611
612
613
614
615
616
617
618 public final HttpClientBuilder setDefaultCookieSpecRegistry(
619 final Lookup<CookieSpecFactory> cookieSpecRegistry) {
620 this.cookieSpecRegistry = cookieSpecRegistry;
621 return this;
622 }
623
624
625
626
627
628
629 public final HttpClientBuilder setContentDecoderRegistry(
630 final LinkedHashMap<String, InputStreamFactory> contentDecoderMap) {
631 this.contentDecoderMap = contentDecoderMap;
632 return this;
633 }
634
635
636
637
638
639
640 public final HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) {
641 this.defaultRequestConfig = config;
642 return this;
643 }
644
645
646
647
648
649 public final HttpClientBuilder useSystemProperties() {
650 this.systemProperties = true;
651 return this;
652 }
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669 public final HttpClientBuilder evictExpiredConnections() {
670 evictExpiredConnections = true;
671 return this;
672 }
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693 public final HttpClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
694 this.evictIdleConnections = true;
695 this.maxIdleTime = maxIdleTime;
696 return this;
697 }
698
699
700
701
702
703
704 public final HttpClientBuilder disableDefaultUserAgent() {
705 this.defaultUserAgentDisabled = true;
706 return this;
707 }
708
709
710
711
712
713
714
715
716
717
718 public final HttpClientBuilder setProxySelector(final ProxySelector proxySelector) {
719 this.proxySelector = proxySelector;
720 return this;
721 }
722
723
724
725
726
727
728 @Internal
729 protected void customizeExecChain(final NamedElementChain<ExecChainHandler> execChainDefinition) {
730 }
731
732
733
734
735
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
755
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
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
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
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
960
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 }