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.async;
29
30 import java.io.Closeable;
31 import java.io.IOException;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34 import java.util.ArrayList;
35 import java.util.Collection;
36 import java.util.LinkedList;
37 import java.util.List;
38 import java.util.concurrent.ThreadFactory;
39
40 import org.apache.hc.client5.http.AuthenticationStrategy;
41 import org.apache.hc.client5.http.DnsResolver;
42 import org.apache.hc.client5.http.HttpRequestRetryStrategy;
43 import org.apache.hc.client5.http.SchemePortResolver;
44 import org.apache.hc.client5.http.async.AsyncExecChainHandler;
45 import org.apache.hc.client5.http.auth.AuthSchemeFactory;
46 import org.apache.hc.client5.http.auth.CredentialsProvider;
47 import org.apache.hc.client5.http.auth.StandardAuthScheme;
48 import org.apache.hc.client5.http.config.ConnectionConfig;
49 import org.apache.hc.client5.http.config.RequestConfig;
50 import org.apache.hc.client5.http.cookie.BasicCookieStore;
51 import org.apache.hc.client5.http.cookie.CookieSpecFactory;
52 import org.apache.hc.client5.http.cookie.CookieStore;
53 import org.apache.hc.client5.http.impl.ChainElement;
54 import org.apache.hc.client5.http.impl.CookieSpecSupport;
55 import org.apache.hc.client5.http.impl.DefaultAuthenticationStrategy;
56 import org.apache.hc.client5.http.impl.DefaultHttpRequestRetryStrategy;
57 import org.apache.hc.client5.http.impl.DefaultRedirectStrategy;
58 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
59 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
60 import org.apache.hc.client5.http.impl.auth.BasicSchemeFactory;
61 import org.apache.hc.client5.http.impl.auth.BearerSchemeFactory;
62 import org.apache.hc.client5.http.impl.auth.DigestSchemeFactory;
63 import org.apache.hc.client5.http.impl.auth.SystemDefaultCredentialsProvider;
64 import org.apache.hc.client5.http.impl.nio.MultihomeConnectionInitiator;
65 import org.apache.hc.client5.http.impl.routing.DefaultRoutePlanner;
66 import org.apache.hc.client5.http.protocol.RedirectStrategy;
67 import org.apache.hc.client5.http.protocol.RequestAddCookies;
68 import org.apache.hc.client5.http.protocol.RequestDefaultHeaders;
69 import org.apache.hc.client5.http.protocol.RequestExpectContinue;
70 import org.apache.hc.client5.http.protocol.ResponseProcessCookies;
71 import org.apache.hc.client5.http.routing.HttpRoutePlanner;
72 import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy;
73 import org.apache.hc.core5.annotation.Internal;
74 import org.apache.hc.core5.concurrent.DefaultThreadFactory;
75 import org.apache.hc.core5.function.Callback;
76 import org.apache.hc.core5.function.Decorator;
77 import org.apache.hc.core5.function.Resolver;
78 import org.apache.hc.core5.http.Header;
79 import org.apache.hc.core5.http.HttpHost;
80 import org.apache.hc.core5.http.HttpRequestInterceptor;
81 import org.apache.hc.core5.http.HttpResponseInterceptor;
82 import org.apache.hc.core5.http.config.CharCodingConfig;
83 import org.apache.hc.core5.http.config.Lookup;
84 import org.apache.hc.core5.http.config.NamedElementChain;
85 import org.apache.hc.core5.http.config.RegistryBuilder;
86 import org.apache.hc.core5.http.nio.command.ShutdownCommand;
87 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
88 import org.apache.hc.core5.http.protocol.DefaultHttpProcessor;
89 import org.apache.hc.core5.http.protocol.HttpProcessor;
90 import org.apache.hc.core5.http.protocol.HttpProcessorBuilder;
91 import org.apache.hc.core5.http.protocol.RequestTargetHost;
92 import org.apache.hc.core5.http.protocol.RequestUserAgent;
93 import org.apache.hc.core5.http2.config.H2Config;
94 import org.apache.hc.core5.http2.protocol.H2RequestConnControl;
95 import org.apache.hc.core5.http2.protocol.H2RequestContent;
96 import org.apache.hc.core5.http2.protocol.H2RequestTargetHost;
97 import org.apache.hc.core5.io.CloseMode;
98 import org.apache.hc.core5.reactor.Command;
99 import org.apache.hc.core5.reactor.DefaultConnectingIOReactor;
100 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
101 import org.apache.hc.core5.reactor.IOReactorConfig;
102 import org.apache.hc.core5.reactor.IOSession;
103 import org.apache.hc.core5.reactor.IOSessionListener;
104 import org.apache.hc.core5.util.Args;
105 import org.apache.hc.core5.util.TimeValue;
106 import org.apache.hc.core5.util.VersionInfo;
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122 public class H2AsyncClientBuilder {
123
124 private static class RequestInterceptorEntry {
125
126 enum Position { FIRST, LAST }
127
128 final RequestInterceptorEntry.Position position;
129 final HttpRequestInterceptor interceptor;
130
131 private RequestInterceptorEntry(final RequestInterceptorEntry.Position position, final HttpRequestInterceptor interceptor) {
132 this.position = position;
133 this.interceptor = interceptor;
134 }
135 }
136
137 private static class ResponseInterceptorEntry {
138
139 enum Position { FIRST, LAST }
140
141 final ResponseInterceptorEntry.Position position;
142 final HttpResponseInterceptor interceptor;
143
144 private ResponseInterceptorEntry(final ResponseInterceptorEntry.Position position, final HttpResponseInterceptor interceptor) {
145 this.position = position;
146 this.interceptor = interceptor;
147 }
148 }
149
150 private static class ExecInterceptorEntry {
151
152 enum Position { BEFORE, AFTER, REPLACE, FIRST, LAST }
153
154 final ExecInterceptorEntry.Position position;
155 final String name;
156 final AsyncExecChainHandler interceptor;
157 final String existing;
158
159 private ExecInterceptorEntry(
160 final ExecInterceptorEntry.Position position,
161 final String name,
162 final AsyncExecChainHandler interceptor,
163 final String existing) {
164 this.position = position;
165 this.name = name;
166 this.interceptor = interceptor;
167 this.existing = existing;
168 }
169
170 }
171
172 private IOReactorConfig ioReactorConfig;
173 private IOSessionListener ioSessionListener;
174 private H2Config h2Config;
175 private CharCodingConfig charCodingConfig;
176 private SchemePortResolver schemePortResolver;
177 private AuthenticationStrategy targetAuthStrategy;
178 private AuthenticationStrategy proxyAuthStrategy;
179
180 private LinkedList<RequestInterceptorEntry> requestInterceptors;
181 private LinkedList<ResponseInterceptorEntry> responseInterceptors;
182 private LinkedList<ExecInterceptorEntry> execInterceptors;
183
184 private HttpRoutePlanner routePlanner;
185 private RedirectStrategy redirectStrategy;
186 private HttpRequestRetryStrategy retryStrategy;
187
188 private Lookup<AuthSchemeFactory> authSchemeRegistry;
189 private Lookup<CookieSpecFactory> cookieSpecRegistry;
190 private CookieStore cookieStore;
191 private CredentialsProvider credentialsProvider;
192
193 private String userAgent;
194 private Collection<? extends Header> defaultHeaders;
195 private RequestConfig defaultRequestConfig;
196 private Resolver<HttpHost, ConnectionConfig> connectionConfigResolver;
197 private boolean evictIdleConnections;
198 private TimeValue maxIdleTime;
199
200 private boolean systemProperties;
201 private boolean automaticRetriesDisabled;
202 private boolean redirectHandlingDisabled;
203 private boolean cookieManagementDisabled;
204 private boolean authCachingDisabled;
205
206 private DnsResolver dnsResolver;
207 private TlsStrategy tlsStrategy;
208
209 private ThreadFactory threadFactory;
210
211 private List<Closeable> closeables;
212
213
214 private Callback<Exception> ioReactorExceptionCallback;
215
216 private Decorator<IOSession> ioSessionDecorator;
217
218 public static H2AsyncClientBuilder create() {
219 return new H2AsyncClientBuilder();
220 }
221
222 protected H2AsyncClientBuilder() {
223 super();
224 }
225
226
227
228
229
230
231 public final H2AsyncClientBuilder setH2Config(final H2Config h2Config) {
232 this.h2Config = h2Config;
233 return this;
234 }
235
236
237
238
239
240
241 public final H2AsyncClientBuilder setIOReactorConfig(final IOReactorConfig ioReactorConfig) {
242 this.ioReactorConfig = ioReactorConfig;
243 return this;
244 }
245
246
247
248
249
250
251
252 public final H2AsyncClientBuilder setIOSessionListener(final IOSessionListener ioSessionListener) {
253 this.ioSessionListener = ioSessionListener;
254 return this;
255 }
256
257
258
259
260
261
262 public final H2AsyncClientBuilder setCharCodingConfig(final CharCodingConfig charCodingConfig) {
263 this.charCodingConfig = charCodingConfig;
264 return this;
265 }
266
267
268
269
270
271
272
273 public final H2AsyncClientBuilder setTargetAuthenticationStrategy(
274 final AuthenticationStrategy targetAuthStrategy) {
275 this.targetAuthStrategy = targetAuthStrategy;
276 return this;
277 }
278
279
280
281
282
283
284
285 public final H2AsyncClientBuilder setProxyAuthenticationStrategy(
286 final AuthenticationStrategy proxyAuthStrategy) {
287 this.proxyAuthStrategy = proxyAuthStrategy;
288 return this;
289 }
290
291
292
293
294
295
296
297 public final H2AsyncClientBuilder setIoReactorExceptionCallback(final Callback<Exception> ioReactorExceptionCallback) {
298 this.ioReactorExceptionCallback = ioReactorExceptionCallback;
299 return this;
300 }
301
302
303
304
305
306
307
308
309 public final H2AsyncClientBuilder setIoSessionDecorator(final Decorator<IOSession> ioSessionDecorator) {
310 this.ioSessionDecorator = ioSessionDecorator;
311 return this;
312 }
313
314
315
316
317
318
319 public final H2AsyncClientBuilder addResponseInterceptorFirst(final HttpResponseInterceptor interceptor) {
320 Args.notNull(interceptor, "Interceptor");
321 if (responseInterceptors == null) {
322 responseInterceptors = new LinkedList<>();
323 }
324 responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.FIRST, interceptor));
325 return this;
326 }
327
328
329
330
331
332
333 public final H2AsyncClientBuilder addResponseInterceptorLast(final HttpResponseInterceptor interceptor) {
334 Args.notNull(interceptor, "Interceptor");
335 if (responseInterceptors == null) {
336 responseInterceptors = new LinkedList<>();
337 }
338 responseInterceptors.add(new ResponseInterceptorEntry(ResponseInterceptorEntry.Position.LAST, interceptor));
339 return this;
340 }
341
342
343
344
345
346
347 public final H2AsyncClientBuilder addExecInterceptorBefore(final String existing, final String name, final AsyncExecChainHandler interceptor) {
348 Args.notBlank(existing, "Existing");
349 Args.notBlank(name, "Name");
350 Args.notNull(interceptor, "Interceptor");
351 if (execInterceptors == null) {
352 execInterceptors = new LinkedList<>();
353 }
354 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.BEFORE, name, interceptor, existing));
355 return this;
356 }
357
358
359
360
361
362
363 public final H2AsyncClientBuilder addExecInterceptorAfter(final String existing, final String name, final AsyncExecChainHandler interceptor) {
364 Args.notBlank(existing, "Existing");
365 Args.notBlank(name, "Name");
366 Args.notNull(interceptor, "Interceptor");
367 if (execInterceptors == null) {
368 execInterceptors = new LinkedList<>();
369 }
370 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.AFTER, name, interceptor, existing));
371 return this;
372 }
373
374
375
376
377
378
379 public final H2AsyncClientBuilder replaceExecInterceptor(final String existing, final AsyncExecChainHandler interceptor) {
380 Args.notBlank(existing, "Existing");
381 Args.notNull(interceptor, "Interceptor");
382 if (execInterceptors == null) {
383 execInterceptors = new LinkedList<>();
384 }
385 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.REPLACE, existing, interceptor, existing));
386 return this;
387 }
388
389
390
391
392
393
394 public final H2AsyncClientBuilder addExecInterceptorFirst(final String name, final AsyncExecChainHandler interceptor) {
395 Args.notNull(name, "Name");
396 Args.notNull(interceptor, "Interceptor");
397 if (execInterceptors == null) {
398 execInterceptors = new LinkedList<>();
399 }
400 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.FIRST, name, interceptor, null));
401 return this;
402 }
403
404
405
406
407
408
409 public final H2AsyncClientBuilder addExecInterceptorLast(final String name, final AsyncExecChainHandler interceptor) {
410 Args.notNull(name, "Name");
411 Args.notNull(interceptor, "Interceptor");
412 if (execInterceptors == null) {
413 execInterceptors = new LinkedList<>();
414 }
415 execInterceptors.add(new ExecInterceptorEntry(ExecInterceptorEntry.Position.LAST, name, interceptor, null));
416 return this;
417 }
418
419
420
421
422
423
424 public final H2AsyncClientBuilder addRequestInterceptorFirst(final HttpRequestInterceptor interceptor) {
425 Args.notNull(interceptor, "Interceptor");
426 if (requestInterceptors == null) {
427 requestInterceptors = new LinkedList<>();
428 }
429 requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.FIRST, interceptor));
430 return this;
431 }
432
433
434
435
436
437
438 public final H2AsyncClientBuilder addRequestInterceptorLast(final HttpRequestInterceptor interceptor) {
439 Args.notNull(interceptor, "Interceptor");
440 if (requestInterceptors == null) {
441 requestInterceptors = new LinkedList<>();
442 }
443 requestInterceptors.add(new RequestInterceptorEntry(RequestInterceptorEntry.Position.LAST, interceptor));
444 return this;
445 }
446
447
448
449
450
451
452
453
454
455
456 public final H2AsyncClientBuilder setRetryStrategy(final HttpRequestRetryStrategy retryStrategy) {
457 this.retryStrategy = retryStrategy;
458 return this;
459 }
460
461
462
463
464
465
466
467
468
469
470 public H2AsyncClientBuilder setRedirectStrategy(final RedirectStrategy redirectStrategy) {
471 this.redirectStrategy = redirectStrategy;
472 return this;
473 }
474
475
476
477
478
479
480 public final H2AsyncClientBuilder setSchemePortResolver(final SchemePortResolver schemePortResolver) {
481 this.schemePortResolver = schemePortResolver;
482 return this;
483 }
484
485
486
487
488
489
490 public final H2AsyncClientBuilder setDnsResolver(final DnsResolver dnsResolver) {
491 this.dnsResolver = dnsResolver;
492 return this;
493 }
494
495
496
497
498
499
500 public final H2AsyncClientBuilder setTlsStrategy(final TlsStrategy tlsStrategy) {
501 this.tlsStrategy = tlsStrategy;
502 return this;
503 }
504
505
506
507
508
509
510 public final H2AsyncClientBuilder setThreadFactory(final ThreadFactory threadFactory) {
511 this.threadFactory = threadFactory;
512 return this;
513 }
514
515
516
517
518
519
520 public final H2AsyncClientBuilder setUserAgent(final String userAgent) {
521 this.userAgent = userAgent;
522 return this;
523 }
524
525
526
527
528
529
530 public final H2AsyncClientBuilder setDefaultHeaders(final Collection<? extends Header> defaultHeaders) {
531 this.defaultHeaders = defaultHeaders;
532 return this;
533 }
534
535
536
537
538
539
540 public final H2AsyncClientBuilder setRoutePlanner(final HttpRoutePlanner routePlanner) {
541 this.routePlanner = routePlanner;
542 return this;
543 }
544
545
546
547
548
549
550
551
552 public final H2AsyncClientBuilder setDefaultCredentialsProvider(final CredentialsProvider credentialsProvider) {
553 this.credentialsProvider = credentialsProvider;
554 return this;
555 }
556
557
558
559
560
561
562
563
564 public final H2AsyncClientBuilder setDefaultAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
565 this.authSchemeRegistry = authSchemeRegistry;
566 return this;
567 }
568
569
570
571
572
573
574
575
576 public final H2AsyncClientBuilder setDefaultCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
577 this.cookieSpecRegistry = cookieSpecRegistry;
578 return this;
579 }
580
581
582
583
584
585
586
587 public final H2AsyncClientBuilder setDefaultCookieStore(final CookieStore cookieStore) {
588 this.cookieStore = cookieStore;
589 return this;
590 }
591
592
593
594
595
596
597
598
599 public final H2AsyncClientBuilder setDefaultRequestConfig(final RequestConfig config) {
600 this.defaultRequestConfig = config;
601 return this;
602 }
603
604
605
606
607
608
609
610 public final H2AsyncClientBuilder setConnectionConfigResolver(final Resolver<HttpHost, ConnectionConfig> connectionConfigResolver) {
611 this.connectionConfigResolver = connectionConfigResolver;
612 return this;
613 }
614
615
616
617
618
619
620
621 public final H2AsyncClientBuilder setDefaultConnectionConfig(final ConnectionConfig connectionConfig) {
622 this.connectionConfigResolver = host -> connectionConfig;
623 return this;
624 }
625
626
627
628
629
630
631
632 public final H2AsyncClientBuilder useSystemProperties() {
633 this.systemProperties = true;
634 return this;
635 }
636
637
638
639
640
641
642 public final H2AsyncClientBuilder disableRedirectHandling() {
643 redirectHandlingDisabled = true;
644 return this;
645 }
646
647
648
649
650
651
652 public final H2AsyncClientBuilder disableAutomaticRetries() {
653 automaticRetriesDisabled = true;
654 return this;
655 }
656
657
658
659
660
661
662 public final H2AsyncClientBuilder disableCookieManagement() {
663 this.cookieManagementDisabled = true;
664 return this;
665 }
666
667
668
669
670
671
672 public final H2AsyncClientBuilder disableAuthCaching() {
673 this.authCachingDisabled = true;
674 return this;
675 }
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 public final H2AsyncClientBuilder evictIdleConnections(final TimeValue maxIdleTime) {
696 this.evictIdleConnections = true;
697 this.maxIdleTime = maxIdleTime;
698 return this;
699 }
700
701
702
703
704
705
706
707 @Internal
708 protected void customizeExecChain(final NamedElementChain<AsyncExecChainHandler> execChainDefinition) {
709 }
710
711
712
713
714
715
716
717 @Internal
718 protected void addCloseable(final Closeable closeable) {
719 if (closeable == null) {
720 return;
721 }
722 if (closeables == null) {
723 closeables = new ArrayList<>();
724 }
725 closeables.add(closeable);
726 }
727
728 public CloseableHttpAsyncClient build() {
729 AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy;
730 if (targetAuthStrategyCopy == null) {
731 targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
732 }
733 AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy;
734 if (proxyAuthStrategyCopy == null) {
735 proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE;
736 }
737
738 String userAgentCopy = this.userAgent;
739 if (userAgentCopy == null) {
740 if (systemProperties) {
741 userAgentCopy = getProperty("http.agent", null);
742 }
743 if (userAgentCopy == null) {
744 userAgentCopy = VersionInfo.getSoftwareInfo("Apache-HttpAsyncClient",
745 "org.apache.hc.client5", getClass());
746 }
747 }
748
749 final HttpProcessorBuilder b = HttpProcessorBuilder.create();
750 if (requestInterceptors != null) {
751 for (final RequestInterceptorEntry entry: requestInterceptors) {
752 if (entry.position == RequestInterceptorEntry.Position.FIRST) {
753 b.addFirst(entry.interceptor);
754 }
755 }
756 }
757 if (responseInterceptors != null) {
758 for (final ResponseInterceptorEntry entry: responseInterceptors) {
759 if (entry.position == ResponseInterceptorEntry.Position.FIRST) {
760 b.addFirst(entry.interceptor);
761 }
762 }
763 }
764 b.addAll(
765 new H2RequestTargetHost(),
766 new RequestDefaultHeaders(defaultHeaders),
767 new RequestUserAgent(userAgentCopy),
768 new RequestExpectContinue(),
769 new H2RequestContent(),
770 new H2RequestConnControl());
771 if (!cookieManagementDisabled) {
772 b.add(RequestAddCookies.INSTANCE);
773 }
774 if (!cookieManagementDisabled) {
775 b.add(ResponseProcessCookies.INSTANCE);
776 }
777 if (requestInterceptors != null) {
778 for (final RequestInterceptorEntry entry: requestInterceptors) {
779 if (entry.position == RequestInterceptorEntry.Position.LAST) {
780 b.addLast(entry.interceptor);
781 }
782 }
783 }
784 if (responseInterceptors != null) {
785 for (final ResponseInterceptorEntry entry: responseInterceptors) {
786 if (entry.position == ResponseInterceptorEntry.Position.LAST) {
787 b.addLast(entry.interceptor);
788 }
789 }
790 }
791
792 final HttpProcessor httpProcessor = b.build();
793
794 final NamedElementChain<AsyncExecChainHandler> execChainDefinition = new NamedElementChain<>();
795 execChainDefinition.addLast(
796 new H2AsyncMainClientExec(httpProcessor),
797 ChainElement.MAIN_TRANSPORT.name());
798
799 execChainDefinition.addFirst(
800 new AsyncConnectExec(
801 new DefaultHttpProcessor(new RequestTargetHost(), new RequestUserAgent(userAgentCopy)),
802 proxyAuthStrategyCopy,
803 schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
804 authCachingDisabled),
805 ChainElement.CONNECT.name());
806
807 execChainDefinition.addFirst(
808 new AsyncProtocolExec(
809 targetAuthStrategyCopy,
810 proxyAuthStrategyCopy,
811 schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE,
812 authCachingDisabled),
813 ChainElement.PROTOCOL.name());
814
815
816 if (!automaticRetriesDisabled) {
817 HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
818 if (retryStrategyCopy == null) {
819 retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
820 }
821 execChainDefinition.addFirst(
822 new AsyncHttpRequestRetryExec(retryStrategyCopy),
823 ChainElement.RETRY.name());
824 }
825
826 HttpRoutePlanner routePlannerCopy = this.routePlanner;
827 if (routePlannerCopy == null) {
828 SchemePortResolver schemePortResolverCopy = this.schemePortResolver;
829 if (schemePortResolverCopy == null) {
830 schemePortResolverCopy = DefaultSchemePortResolver.INSTANCE;
831 }
832 routePlannerCopy = new DefaultRoutePlanner(schemePortResolverCopy);
833 }
834
835
836 if (!redirectHandlingDisabled) {
837 RedirectStrategy redirectStrategyCopy = this.redirectStrategy;
838 if (redirectStrategyCopy == null) {
839 redirectStrategyCopy = DefaultRedirectStrategy.INSTANCE;
840 }
841 execChainDefinition.addFirst(
842 new AsyncRedirectExec(routePlannerCopy, redirectStrategyCopy),
843 ChainElement.REDIRECT.name());
844 }
845
846 final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
847 final IOEventHandlerFactory ioEventHandlerFactory = new H2AsyncClientProtocolStarter(
848 HttpProcessorBuilder.create().build(),
849 (request, context) -> pushConsumerRegistry.get(request),
850 h2Config != null ? h2Config : H2Config.DEFAULT,
851 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
852 ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE);
853 final DefaultConnectingIOReactor ioReactor = new DefaultConnectingIOReactor(
854 ioEventHandlerFactory,
855 ioReactorConfig != null ? ioReactorConfig : IOReactorConfig.DEFAULT,
856 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-dispatch", true),
857 ioSessionDecorator != null ? ioSessionDecorator : LoggingIOSessionDecorator.INSTANCE,
858 ioReactorExceptionCallback != null ? ioReactorExceptionCallback : LoggingExceptionCallback.INSTANCE,
859 ioSessionListener,
860 ioSession -> ioSession.enqueue(new ShutdownCommand(CloseMode.GRACEFUL), Command.Priority.IMMEDIATE));
861
862 if (execInterceptors != null) {
863 for (final ExecInterceptorEntry entry: execInterceptors) {
864 switch (entry.position) {
865 case AFTER:
866 execChainDefinition.addAfter(entry.existing, entry.interceptor, entry.name);
867 break;
868 case BEFORE:
869 execChainDefinition.addBefore(entry.existing, entry.interceptor, entry.name);
870 break;
871 case REPLACE:
872 execChainDefinition.replace(entry.existing, entry.interceptor);
873 break;
874 case FIRST:
875 execChainDefinition.addFirst(entry.interceptor, entry.name);
876 break;
877 case LAST:
878
879
880 execChainDefinition.addBefore(ChainElement.MAIN_TRANSPORT.name(), entry.interceptor, entry.name);
881 break;
882 }
883 }
884 }
885
886 customizeExecChain(execChainDefinition);
887
888 NamedElementChain<AsyncExecChainHandler>.Node current = execChainDefinition.getLast();
889 AsyncExecChainElement execChain = null;
890 while (current != null) {
891 execChain = new AsyncExecChainElement(current.getValue(), execChain);
892 current = current.getPrevious();
893 }
894
895 Lookup<AuthSchemeFactory> authSchemeRegistryCopy = this.authSchemeRegistry;
896 if (authSchemeRegistryCopy == null) {
897 authSchemeRegistryCopy = RegistryBuilder.<AuthSchemeFactory>create()
898 .register(StandardAuthScheme.BASIC, BasicSchemeFactory.INSTANCE)
899 .register(StandardAuthScheme.DIGEST, DigestSchemeFactory.INSTANCE)
900 .register(StandardAuthScheme.BEARER, BearerSchemeFactory.INSTANCE)
901 .build();
902 }
903 Lookup<CookieSpecFactory> cookieSpecRegistryCopy = this.cookieSpecRegistry;
904 if (cookieSpecRegistryCopy == null) {
905 cookieSpecRegistryCopy = CookieSpecSupport.createDefault();
906 }
907
908 CookieStore cookieStoreCopy = this.cookieStore;
909 if (cookieStoreCopy == null) {
910 cookieStoreCopy = new BasicCookieStore();
911 }
912
913 CredentialsProvider credentialsProviderCopy = this.credentialsProvider;
914 if (credentialsProviderCopy == null) {
915 if (systemProperties) {
916 credentialsProviderCopy = new SystemDefaultCredentialsProvider();
917 } else {
918 credentialsProviderCopy = new BasicCredentialsProvider();
919 }
920 }
921
922 TlsStrategy tlsStrategyCopy = this.tlsStrategy;
923 if (tlsStrategyCopy == null) {
924 if (systemProperties) {
925 tlsStrategyCopy = DefaultClientTlsStrategy.createSystemDefault();
926 } else {
927 tlsStrategyCopy = DefaultClientTlsStrategy.createDefault();
928 }
929 }
930
931 final MultihomeConnectionInitiator connectionInitiator = new MultihomeConnectionInitiator(ioReactor, dnsResolver);
932 final InternalH2ConnPool connPool = new InternalH2ConnPool(connectionInitiator, host -> null, tlsStrategyCopy);
933 connPool.setConnectionConfigResolver(connectionConfigResolver);
934
935 List<Closeable> closeablesCopy = closeables != null ? new ArrayList<>(closeables) : null;
936 if (closeablesCopy == null) {
937 closeablesCopy = new ArrayList<>(1);
938 }
939 if (evictIdleConnections) {
940 final IdleConnectionEvictor connectionEvictor = new IdleConnectionEvictor(connPool,
941 maxIdleTime != null ? maxIdleTime : TimeValue.ofSeconds(30L));
942 closeablesCopy.add(connectionEvictor::shutdown);
943 connectionEvictor.start();
944 }
945 closeablesCopy.add(connPool);
946
947 return new InternalH2AsyncClient(
948 ioReactor,
949 execChain,
950 pushConsumerRegistry,
951 threadFactory != null ? threadFactory : new DefaultThreadFactory("httpclient-main", true),
952 connPool,
953 routePlannerCopy,
954 cookieSpecRegistryCopy,
955 authSchemeRegistryCopy,
956 cookieStoreCopy,
957 credentialsProviderCopy,
958 defaultRequestConfig,
959 closeablesCopy);
960 }
961
962 private static String getProperty(final String key, final String defaultValue) {
963 return AccessController.doPrivileged((PrivilegedAction<String>) () -> System.getProperty(key, defaultValue));
964 }
965
966 static class IdleConnectionEvictor implements Closeable {
967
968 private final Thread thread;
969
970 public IdleConnectionEvictor(final InternalH2ConnPool connPool, final TimeValue maxIdleTime) {
971 this.thread = new DefaultThreadFactory("idle-connection-evictor", true).newThread(() -> {
972 try {
973 while (!Thread.currentThread().isInterrupted()) {
974 maxIdleTime.sleep();
975 connPool.closeIdle(maxIdleTime);
976 }
977 } catch (final InterruptedException ex) {
978 Thread.currentThread().interrupt();
979 } catch (final Exception ignore) {
980 }
981
982 });
983 }
984
985 public void start() {
986 thread.start();
987 }
988
989 public void shutdown() {
990 thread.interrupt();
991 }
992
993 @Override
994 public void close() throws IOException {
995 shutdown();
996 }
997
998 }
999
1000 }