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