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