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
249
250 public final HttpClientBuilder setRequestExecutor(final HttpRequestExecutor requestExec) {
251 this.requestExec = requestExec;
252 return this;
253 }
254
255
256
257
258
259
260 public final HttpClientBuilder setConnectionManager(
261 final HttpClientConnectionManager connManager) {
262 this.connManager = connManager;
263 return this;
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281 public final HttpClientBuilder setConnectionManagerShared(
282 final boolean shared) {
283 this.connManagerShared = shared;
284 return this;
285 }
286
287
288
289
290
291
292 public final HttpClientBuilder setConnectionReuseStrategy(
293 final ConnectionReuseStrategy reuseStrategy) {
294 this.reuseStrategy = reuseStrategy;
295 return this;
296 }
297
298
299
300
301
302
303 public final HttpClientBuilder setKeepAliveStrategy(
304 final ConnectionKeepAliveStrategy keepAliveStrategy) {
305 this.keepAliveStrategy = keepAliveStrategy;
306 return this;
307 }
308
309
310
311
312
313
314
315 public final HttpClientBuilder setTargetAuthenticationStrategy(
316 final AuthenticationStrategy targetAuthStrategy) {
317 this.targetAuthStrategy = targetAuthStrategy;
318 return this;
319 }
320
321
322
323
324
325
326
327 public final HttpClientBuilder setProxyAuthenticationStrategy(
328 final AuthenticationStrategy proxyAuthStrategy) {
329 this.proxyAuthStrategy = proxyAuthStrategy;
330 return this;
331 }
332
333
334
335
336
337
338
339
340
341
342 public final HttpClientBuilder setUserTokenHandler(final UserTokenHandler userTokenHandler) {
343 this.userTokenHandler = userTokenHandler;
344 return this;
345 }
346
347
348
349
350
351
352 public final HttpClientBuilder disableConnectionState() {
353 connectionStateDisabled = true;
354 return this;
355 }
356
357
358
359
360
361
362 public final HttpClientBuilder setSchemePortResolver(
363 final SchemePortResolver schemePortResolver) {
364 this.schemePortResolver = schemePortResolver;
365 return this;
366 }
367
368
369
370
371
372
373 public final HttpClientBuilder setUserAgent(final String userAgent) {
374 this.userAgent = userAgent;
375 return this;
376 }
377
378
379
380
381
382
383 public final HttpClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
384 this.defaultHeaders = defaultHeaders;
385 return this;
386 }
387
388
389
390
391
392
393 public final HttpClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
394 Args.notNull(interceptor, "Interceptor");
395 if (responseInterceptors == null) {
396 responseInterceptors = new LinkedList<>();
397 }
398 responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor));
399 return this;
400 }
401
402
403
404
405
406
407 public final HttpClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) {
408 Args.notNull(interceptor, "Interceptor");
409 if (responseInterceptors == null) {
410 responseInterceptors = new LinkedList<>();
411 }
412 responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor));
413 return this;
414 }
415
416
417
418
419
420
421 public final HttpClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) {
422 Args.notNull(interceptor, "Interceptor");
423 if (requestInterceptors == null) {
424 requestInterceptors = new LinkedList<>();
425 }
426 requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor));
427 return this;
428 }
429
430
431
432
433
434
435 public final HttpClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) {
436 Args.notNull(interceptor, "Interceptor");
437 if (requestInterceptors == null) {
438 requestInterceptors = new LinkedList<>();
439 }
440 requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor));
441 return this;
442 }
443
444
445
446
447
448
449 public final HttpClientBuilder addExecInterceptorBefore(final String existing, final String name, final ExecChainHandler interceptor) {
450 Args.notBlank(existing, "Existing");
451 Args.notBlank(name, "Name");
452 Args.notNull(interceptor, "Interceptor");
453 if (execInterceptors == null) {
454 execInterceptors = new LinkedList<>();
455 }
456 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing));
457 return this;
458 }
459
460
461
462
463
464
465 public final HttpClientBuilder addExecInterceptorAfter(final String existing, final String name, final ExecChainHandler interceptor) {
466 Args.notBlank(existing, "Existing");
467 Args.notBlank(name, "Name");
468 Args.notNull(interceptor, "Interceptor");
469 if (execInterceptors == null) {
470 execInterceptors = new LinkedList<>();
471 }
472 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing));
473 return this;
474 }
475
476
477
478
479
480
481 public final HttpClientBuilder replaceExecInterceptor(final String existing, final ExecChainHandler interceptor) {
482 Args.notBlank(existing, "Existing");
483 Args.notNull(interceptor, "Interceptor");
484 if (execInterceptors == null) {
485 execInterceptors = new LinkedList<>();
486 }
487 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing));
488 return this;
489 }
490
491
492
493
494
495
496 public final HttpClientBuilder addExecInterceptorFirst(final String name, final ExecChainHandler interceptor) {
497 Args.notNull(name, "Name");
498 Args.notNull(interceptor, "Interceptor");
499 if (execInterceptors == null) {
500 execInterceptors = new LinkedList<>();
501 }
502 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null));
503 return this;
504 }
505
506
507
508
509
510
511 public final HttpClientBuilder addExecInterceptorLast(final String name, final ExecChainHandler interceptor) {
512 Args.notNull(name, "Name");
513 Args.notNull(interceptor, "Interceptor");
514 if (execInterceptors == null) {
515 execInterceptors = new LinkedList<>();
516 }
517 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null));
518 return this;
519 }
520
521
522
523
524
525
526 public final HttpClientBuilder disableCookieManagement() {
527 this.cookieManagementDisabled = true;
528 return this;
529 }
530
531
532
533
534
535
536 public final HttpClientBuilder disableContentCompression() {
537 contentCompressionDisabled = true;
538 return this;
539 }
540
541
542
543
544
545
546 public final HttpClientBuilder disableAuthCaching() {
547 this.authCachingDisabled = true;
548 return this;
549 }
550
551
552
553
554
555
556
557
558
559
560 public final HttpClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) {
561 this.retryStrategy = retryStrategy;
562 return this;
563 }
564
565
566
567
568
569
570 public final HttpClientBuilder disableAutomaticRetries() {
571 automaticRetriesDisabled = true;
572 return this;
573 }
574
575
576
577
578
579
580
581
582
583
584 public final HttpClientBuilder setProxy(final HttpHost proxy) {
585 this.proxy = proxy;
586 return this;
587 }
588
589
590
591
592 public final HttpClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
593 this.routePlanner = routePlanner;
594 return this;
595 }
596
597
598
599
600
601
602
603
604
605
606 public final HttpClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
607 this.redirectStrategy = redirectStrategy;
608 return this;
609 }
610
611
612
613
614
615
616 public final HttpClientBuilder disableRedirectHandling() {
617 redirectHandlingDisabled = true;
618 return this;
619 }
620
621
622
623
624
625
626 public final HttpClientBuilder setConnectionBackoffStrategy(
627 final ConnectionBackoffStrategy connectionBackoffStrategy) {
628 this.connectionBackoffStrategy = connectionBackoffStrategy;
629 return this;
630 }
631
632
633
634
635
636
637 public final HttpClientBuilder setBackoffManager(final BackoffManager backoffManager) {
638 this.backoffManager = backoffManager;
639 return this;
640 }
641
642
643
644
645
646
647
648 public final HttpClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
649 this.cookieStore = cookieStore;
650 return this;
651 }
652
653
654
655
656
657
658
659
660 public final HttpClientBuilder setDefaultCredentialsProvider(
661 final CredentialsProvider credentialsProvider) {
662 this.credentialsProvider = credentialsProvider;
663 return this;
664 }
665
666
667
668
669
670
671
672
673 public final HttpClientBuilder setDefaultAuthSchemeRegistry(
674 final Lookup<AuthSchemeFactory> authSchemeRegistry) {
675 this.authSchemeRegistry = authSchemeRegistry;
676 return this;
677 }
678
679
680
681
682
683
684
685
686
687 public final HttpClientBuilder setDefaultCookieSpecRegistry(
688 final Lookup<CookieSpecFactory> cookieSpecRegistry) {
689 this.cookieSpecRegistry = cookieSpecRegistry;
690 return this;
691 }
692
693
694
695
696
697
698
699
700 public final HttpClientBuilder setContentDecoderRegistry(
701 final LinkedHashMap<String, InputStreamFactory> contentDecoderMap) {
702 this.contentDecoderMap = contentDecoderMap;
703 return this;
704 }
705
706
707
708
709
710
711
712
713 public final HttpClientBuilder setDefaultRequestConfig(final RequestConfig config) {
714 this.defaultRequestConfig = config;
715 return this;
716 }
717
718
719
720
721
722
723
724 public final HttpClientBuilder useSystemProperties() {
725 this.systemProperties = true;
726 return this;
727 }
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747 public final HttpClientBuilder evictExpiredConnections() {
748 evictExpiredConnections = true;
749 return this;
750 }
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774 public final HttpClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
775 this.evictIdleConnections = true;
776 this.maxIdleTime = maxIdleTime;
777 return this;
778 }
779
780
781
782
783
784
785
786 public final HttpClientBuilder disableDefaultUserAgent() {
787 this.defaultUserAgentDisabled = true;
788 return this;
789 }
790
791
792
793
794
795
796
797
798
799
800 public final HttpClientBuilder setProxySelector(final ProxySelector proxySelector) {
801 this.proxySelector = proxySelector;
802 return this;
803 }
804
805
806
807
808
809
810
811 @Internal
812 protected void customizeExecChain(final NamedElementChain<ExecChainHandler> execChainDefinition) {
813 }
814
815
816
817
818
819
820
821 @Internal
822 protected void addCloseable(final Closeable closeable) {
823 if (closeable == null) {
824 return;
825 }
826 if (closeables == null) {
827 closeables = new ArrayList<>();
828 }
829 closeables.add(closeable);
830 }
831
832 @Internal
833 protected Function<HttpContext, HttpClientContext> contextAdaptor() {
834 return HttpClientContext::castOrCreate;
835 }
836
837 public CloseableHttpClient build() {
838
839
840 HttpRequestExecutor requestExecCopy = this.requestExec;
841 if (requestExecCopy == null) {
842 requestExecCopy = new HttpRequestExecutor();
843 }
844 HttpClientConnectionManager connManagerCopy = this.connManager;
845 if (connManagerCopy == null) {
846 final PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder.create();
847 if (systemProperties) {
848 connectionManagerBuilder.useSystemProperties();
849 }
850 connManagerCopy = connectionManagerBuilder.build();
851 }
852 ConnectionReuseStrategy reuseStrategyCopy = this.reuseStrategy;
853 if (reuseStrategyCopy == null) {
854 if (systemProperties) {
855 final String s = System.getProperty("http.keepAlive", "true");
856 if ("true".equalsIgnoreCase(s)) {
857 reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
858 } else {
859 reuseStrategyCopy = (request, response, context) -> false;
860 }
861 } else {
862 reuseStrategyCopy = DefaultClientConnectionReuseStrategy.INSTANCE;
863 }
864 }
865
866 ConnectionKeepAliveStrategy keepAliveStrategyCopy = this.keepAliveStrategy;
867 if (keepAliveStrategyCopy == null) {
868 keepAliveStrategyCopy = DefaultConnectionKeepAliveStrategy.INSTANCE;
869 }
870 AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
871 if (targetAuthStrategyCopy == null) {
872 targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
873 }
874 AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
875 if (proxyAuthStrategyCopy == null) {
876 proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
877 }
878 UserTokenHandler userTokenHandlerCopy = this.userTokenHandler;
879 if (userTokenHandlerCopy == null) {
880 if (!connectionStateDisabled) {
881 userTokenHandlerCopy = DefaultUserTokenHandler.INSTANCE;
882 } else {
883 userTokenHandlerCopy = NoopUserTokenHandler.INSTANCE;
884 }
885 }
886
887 String userAgentCopy = this.userAgent;
888 if (userAgentCopy == null) {
889 if (systemProperties) {
890 userAgentCopy = System.getProperty("http.agent");
891 }
892 if (userAgentCopy == null && !defaultUserAgentDisabled) {
893 userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpClient",
894 "org.apache.hc.client5", getClass());
895 }
896 }
897
898 final HttpProcessorBuilder b = HttpProcessorBuilder.create();
899 if (requestInterceptors != null) {
900 for (final RequestInterceptorEntry entry: requestInterceptors) {
901 if (entry.position == RequestInterceptorEntry.Position.FIRST) {
902 b.addFirst(entry.interceptor);
903 }
904 }
905 }
906 if (responseInterceptors != null) {
907 for (final ResponseInterceptorEntry entry: responseInterceptors) {
908 if (entry.position == ResponseInterceptorEntry.Position.FIRST) {
909 b.addFirst(entry.interceptor);
910 }
911 }
912 }
913 b.addAll(
914 new RequestTargetHost(),
915 new RequestValidateTrace(),
916 new RequestDefaultHeaders(defaultHeaders),
917 new RequestContent(),
918 new RequestClientConnControl(),
919 new RequestUserAgent(userAgentCopy),
920 new RequestExpectContinue(),
921 new RequestUpgrade());
922 if (!cookieManagementDisabled) {
923 b.add(RequestAddCookies.INSTANCE);
924 }
925 if (!cookieManagementDisabled) {
926 b.add(ResponseProcessCookies.INSTANCE);
927 }
928 if (requestInterceptors != null) {
929 for (final RequestInterceptorEntry entry: requestInterceptors) {
930 if (entry.position == RequestInterceptorEntry.Position.LAST) {
931 b.addLast(entry.interceptor);
932 }
933 }
934 }
935 if (responseInterceptors != null) {
936 for (final ResponseInterceptorEntry entry: responseInterceptors) {
937 if (entry.position == ResponseInterceptorEntry.Position.LAST) {
938 b.addLast(entry.interceptor);
939 }
940 }
941 }
942 final HttpProcessor httpProcessor = b.build();
943
944 final NamedElementChain<ExecChainHandler> execChainDefinition = new NamedElementChain<>();
945 execChainDefinition.addLast(
946 new MainClientExec(connManagerCopy, httpProcessor, reuseStrategyCopy, keepAliveStrategyCopy, userTokenHandlerCopy),
947 ChainElement.MAIN_TRANSPORT.name());
948 execChainDefinition.addFirst(
949 new ConnectExec(
950 reuseStrategyCopy,
951 new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
952 proxyAuthStrategyCopy,
953 schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
954 authCachingDisabled),
955 ChainElement.CONNECT.name());
956
957 execChainDefinition.addFirst(
958 new ProtocolExec(
959 targetAuthStrategyCopy,
960 proxyAuthStrategyCopy,
961 schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
962 authCachingDisabled),
963 ChainElement.PROTOCOL.name());
964
965 if (!contentCompressionDisabled) {
966 if (contentDecoderMap != null) {
967 final List<String> encodings = new ArrayList<>(contentDecoderMap.keySet());
968 final RegistryBuilder<InputStreamFactory> b2 = RegistryBuilder.create();
969 for (final Map.Entry<String, InputStreamFactory> entry: contentDecoderMap.entrySet()) {
970 b2.register(entry.getKey(), entry.getValue());
971 }
972 final Registry<InputStreamFactory> decoderRegistry = b2.build();
973 execChainDefinition.addFirst(
974 new ContentCompressionExec(encodings, decoderRegistry, true),
975 ChainElement.COMPRESS.name());
976 } else {
977 execChainDefinition.addFirst(new ContentCompressionExec(true), ChainElement.COMPRESS.name());
978 }
979 }
980
981
982 if (!automaticRetriesDisabled) {
983 HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
984 if (retryStrategyCopy == null) {
985 retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
986 }
987 execChainDefinition.addFirst(
988 new HttpRequestRetryExec(retryStrategyCopy),
989 ChainElement.RETRY.name());
990 }
991
992 HttpRoutePlanner routePlannerCopy = this.routePlanner;
993 if (routePlannerCopy == null) {
994 SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
995 if (schemePortResolverCopy == null) {
996 schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
997 }
998 if (proxy != null) {
999 routePlannerCopy = new DefaultProxyRoutePlanner(proxy, schemePortResolverCopy);
1000 } else if (this.proxySelector != null) {
1001 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, this.proxySelector);
1002 } else if (systemProperties) {
1003 final ProxySelector defaultProxySelector = AccessController.doPrivileged((PrivilegedAction<ProxySelector>) ProxySelector::getDefault);
1004 routePlannerCopy = new SystemDefaultRoutePlanner(schemePortResolverCopy, defaultProxySelector);
1005 } else {
1006 routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
1007 }
1008 }
1009
1010
1011 if (!redirectHandlingDisabled) {
1012 RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
1013 if (redirectStrategyCopy == null) {
1014 redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
1015 }
1016 execChainDefinition.addFirst(
1017 new RedirectExec(routePlannerCopy, redirectStrategyCopy),
1018 ChainElement.REDIRECT.name());
1019 }
1020
1021
1022 if (this.backoffManager != null && this.connectionBackoffStrategy != null) {
1023 execChainDefinition.addFirst(new BackoffStrategyExec(this.connectionBackoffStrategy, this.backoffManager),
1024 ChainElement.BACK_OFF.name());
1025 }
1026
1027 if (execInterceptors != null) {
1028 for (final ExecInterceptorEntry entry: execInterceptors) {
1029 switch (entry.position) {
1030 case AFTER:
1031 execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name);
1032 break;
1033 case BEFORE:
1034 execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name);
1035 break;
1036 case REPLACE:
1037 execChainDefinition.replace(entry.existing, entry.interceptor);
1038 break;
1039 case FIRST:
1040 execChainDefinition.addFirst(entry.interceptor, entry.name);
1041 break;
1042 case LAST:
1043
1044
1045 execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name);
1046 break;
1047 }
1048 }
1049 }
1050
1051 customizeExecChain(execChainDefinition);
1052
1053 NamedElementChain<ExecChainHandler>.Node current = execChainDefinition.getLast();
1054 ExecChainElement execChain = null;
1055 while (current != null) {
1056 execChain = new ExecChainElement(current.getValue(), execChain);
1057 current = current.getPrevious();
1058 }
1059
1060 Lookup<AuthSchemeFactory> authSchemeRegistryCopy = this.authSchemeRegistry;
1061 if (authSchemeRegistryCopy == null) {
1062 authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeFactory>create()
1063 .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE)
1064 .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE)
1065 .register(StandardAuthScheme.BEARER, BearerSchemeFactory.INSTANCE)
1066 .build();
1067 }
1068 Lookup<CookieSpecFactory> cookieSpecRegistryCopy = this.cookieSpecRegistry;
1069 if (cookieSpecRegistryCopy == null) {
1070 cookieSpecRegistryCopy = CookieSpecSupport.createDefault();
1071 }
1072
1073 CookieStore defaultCookieStore = this.cookieStore;
1074 if (defaultCookieStore == null) {
1075 defaultCookieStore = new BasicCookieStore();
1076 }
1077
1078 CredentialsProvider defaultCredentialsProvider = this.credentialsProvider;
1079 if (defaultCredentialsProvider == null) {
1080 if (systemProperties) {
1081 defaultCredentialsProvider = new SystemDefaultCredentialsProvider();
1082 } else {
1083 defaultCredentialsProvider = new BasicCredentialsProvider();
1084 }
1085 }
1086
1087 List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
1088 if (!this.connManagerShared) {
1089 if (closeablesCopy == null) {
1090 closeablesCopy = new ArrayList<>(1);
1091 }
1092 if (evictExpiredConnections || evictIdleConnections) {
1093 if (connManagerCopy instanceof ConnPoolControl) {
1094 final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor((ConnPoolControl<?>) connManagerCopy,
1095 maxIdleTime, maxIdleTime);
1096 closeablesCopy.add(() -> {
1097 connectionEvictor.shutdown();
1098 try {
1099 connectionEvictor.awaitTermination(Timeout.ofSeconds(1));
1100 } catch (final InterruptedException interrupted) {
1101 Thread.currentThread().interrupt();
1102 }
1103 });
1104 connectionEvictor.start();
1105 }
1106 }
1107 closeablesCopy.add(connManagerCopy);
1108 }
1109
1110 return new InternalHttpClient(
1111 connManagerCopy,
1112 requestExecCopy,
1113 execChain,
1114 routePlannerCopy,
1115 cookieSpecRegistryCopy,
1116 authSchemeRegistryCopy,
1117 defaultCookieStore,
1118 defaultCredentialsProvider,
1119 contextAdaptor(),
1120 defaultRequestConfig != null ? defaultRequestConfig : RequestConfig.DEFAULT,
1121 closeablesCopy);
1122 }
1123
1124 }