1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit;
16
17 import java.io.ByteArrayInputStream;
18 import java.io.ByteArrayOutputStream;
19 import java.io.EOFException;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.OutputStream;
24 import java.net.InetAddress;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.net.URL;
28 import java.nio.charset.Charset;
29 import java.nio.file.Files;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.WeakHashMap;
35 import java.util.concurrent.TimeUnit;
36
37 import javax.net.ssl.HostnameVerifier;
38 import javax.net.ssl.SSLContext;
39 import javax.net.ssl.SSLPeerUnverifiedException;
40 import javax.net.ssl.SSLSocketFactory;
41
42 import org.apache.commons.io.IOUtils;
43 import org.apache.commons.lang3.StringUtils;
44 import org.apache.commons.lang3.reflect.FieldUtils;
45 import org.apache.commons.logging.Log;
46 import org.apache.commons.logging.LogFactory;
47 import org.apache.http.ConnectionClosedException;
48 import org.apache.http.Header;
49 import org.apache.http.HttpEntity;
50 import org.apache.http.HttpEntityEnclosingRequest;
51 import org.apache.http.HttpException;
52 import org.apache.http.HttpHost;
53 import org.apache.http.HttpRequest;
54 import org.apache.http.HttpRequestInterceptor;
55 import org.apache.http.HttpResponse;
56 import org.apache.http.auth.AuthScheme;
57 import org.apache.http.auth.AuthScope;
58 import org.apache.http.auth.Credentials;
59 import org.apache.http.client.AuthCache;
60 import org.apache.http.client.CredentialsProvider;
61 import org.apache.http.client.config.RequestConfig;
62 import org.apache.http.client.methods.CloseableHttpResponse;
63 import org.apache.http.client.methods.HttpGet;
64 import org.apache.http.client.methods.HttpHead;
65 import org.apache.http.client.methods.HttpPatch;
66 import org.apache.http.client.methods.HttpPost;
67 import org.apache.http.client.methods.HttpPut;
68 import org.apache.http.client.methods.HttpRequestBase;
69 import org.apache.http.client.methods.HttpTrace;
70 import org.apache.http.client.methods.HttpUriRequest;
71 import org.apache.http.client.protocol.HttpClientContext;
72 import org.apache.http.client.protocol.RequestAcceptEncoding;
73 import org.apache.http.client.protocol.RequestAddCookies;
74 import org.apache.http.client.protocol.RequestAuthCache;
75 import org.apache.http.client.protocol.RequestDefaultHeaders;
76 import org.apache.http.client.protocol.RequestExpectContinue;
77 import org.apache.http.client.protocol.ResponseProcessCookies;
78 import org.apache.http.client.utils.URLEncodedUtils;
79 import org.apache.http.config.ConnectionConfig;
80 import org.apache.http.config.RegistryBuilder;
81 import org.apache.http.config.SocketConfig;
82 import org.apache.http.conn.DnsResolver;
83 import org.apache.http.conn.routing.RouteInfo;
84 import org.apache.http.conn.socket.ConnectionSocketFactory;
85 import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
86 import org.apache.http.conn.ssl.DefaultHostnameVerifier;
87 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
88 import org.apache.http.conn.util.PublicSuffixMatcher;
89 import org.apache.http.conn.util.PublicSuffixMatcherLoader;
90 import org.apache.http.cookie.CookieSpecProvider;
91 import org.apache.http.entity.ContentType;
92 import org.apache.http.entity.StringEntity;
93 import org.apache.http.entity.mime.MultipartEntityBuilder;
94 import org.apache.http.entity.mime.content.InputStreamBody;
95 import org.apache.http.impl.client.BasicAuthCache;
96 import org.apache.http.impl.client.CloseableHttpClient;
97 import org.apache.http.impl.client.HttpClientBuilder;
98 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
99 import org.apache.http.protocol.HttpContext;
100 import org.apache.http.protocol.HttpProcessorBuilder;
101 import org.apache.http.protocol.RequestContent;
102 import org.apache.http.protocol.RequestTargetHost;
103 import org.apache.http.ssl.SSLContexts;
104 import org.apache.http.util.TextUtils;
105 import org.htmlunit.WebRequest.HttpHint;
106 import org.htmlunit.http.HttpUtils;
107 import org.htmlunit.httpclient.HtmlUnitCookieSpecProvider;
108 import org.htmlunit.httpclient.HtmlUnitCookieStore;
109 import org.htmlunit.httpclient.HtmlUnitRedirectStrategie;
110 import org.htmlunit.httpclient.HtmlUnitSSLConnectionSocketFactory;
111 import org.htmlunit.httpclient.SocksConnectionSocketFactory;
112 import org.htmlunit.util.KeyDataPair;
113 import org.htmlunit.util.MimeType;
114 import org.htmlunit.util.NameValuePair;
115 import org.htmlunit.util.UrlUtils;
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public class HttpWebConnection implements WebConnection {
134
135 private static final Log LOG = LogFactory.getLog(HttpWebConnection.class);
136
137 private static final String HACKED_COOKIE_POLICY = "mine";
138
139
140
141 private final Map<Thread, HttpClientBuilder> httpClientBuilder_ = new WeakHashMap<>();
142 private final WebClient webClient_;
143
144 private String virtualHost_;
145 private final HtmlUnitCookieSpecProvider htmlUnitCookieSpecProvider_;
146 private final WebClientOptions usedOptions_;
147 private PoolingHttpClientConnectionManager connectionManager_;
148
149
150 private final AuthCache sharedAuthCache_ = new SynchronizedAuthCache();
151
152
153 private final Map<Thread, HttpClientContext> httpClientContextByThread_ = new WeakHashMap<>();
154
155
156
157
158
159 public HttpWebConnection(final WebClient webClient) {
160 super();
161 webClient_ = webClient;
162 htmlUnitCookieSpecProvider_ = new HtmlUnitCookieSpecProvider(webClient.getBrowserVersion());
163 usedOptions_ = new WebClientOptions();
164 }
165
166
167
168
169 @Override
170 public WebResponse getResponse(final WebRequest webRequest) throws IOException {
171 final HttpClientBuilder builder = reconfigureHttpClientIfNeeded(getHttpClientBuilder(), webRequest);
172
173 HttpUriRequest httpMethod = null;
174 try {
175 try {
176 httpMethod = makeHttpMethod(webRequest, builder);
177 }
178 catch (final URISyntaxException e) {
179 throw new IOException("Unable to create URI from URL: " + webRequest.getUrl().toExternalForm()
180 + " (reason: " + e.getMessage() + ")", e);
181 }
182
183 final URL url = webRequest.getUrl();
184 final HttpHost httpHost = new HttpHost(url.getHost(), url.getPort(), url.getProtocol());
185 final long startTime = System.currentTimeMillis();
186
187 final HttpContext httpContext = getHttpContext();
188 try {
189 try (CloseableHttpClient closeableHttpClient = builder.build()) {
190 try (CloseableHttpResponse httpResponse =
191 closeableHttpClient.execute(httpHost, httpMethod, httpContext)) {
192 return downloadResponse(httpMethod, webRequest, httpResponse, startTime);
193 }
194 }
195 }
196 catch (final SSLPeerUnverifiedException ex) {
197
198 if (webClient_.getOptions().isUseInsecureSSL()) {
199 HtmlUnitSSLConnectionSocketFactory.setUseSSL3Only(httpContext, true);
200 try (CloseableHttpClient closeableHttpClient = builder.build()) {
201 try (CloseableHttpResponse httpResponse =
202 closeableHttpClient.execute(httpHost, httpMethod, httpContext)) {
203 return downloadResponse(httpMethod, webRequest, httpResponse, startTime);
204 }
205 }
206 }
207 throw ex;
208 }
209 catch (final Error e) {
210
211
212
213
214 httpClientBuilder_.remove(Thread.currentThread());
215 throw e;
216 }
217 }
218 finally {
219 if (httpMethod != null) {
220 onResponseGenerated(httpMethod);
221 }
222 }
223 }
224
225
226
227
228
229
230 protected void onResponseGenerated(final HttpUriRequest httpMethod) {
231
232 }
233
234
235
236
237 private synchronized HttpContext getHttpContext() {
238 HttpClientContext httpClientContext = httpClientContextByThread_.get(Thread.currentThread());
239 if (httpClientContext == null) {
240 httpClientContext = new HttpClientContext();
241
242
243 httpClientContext.setAttribute(HttpClientContext.AUTH_CACHE, sharedAuthCache_);
244
245 httpClientContextByThread_.put(Thread.currentThread(), httpClientContext);
246 }
247 return httpClientContext;
248 }
249
250 private void setProxy(final HttpRequestBase httpRequest, final WebRequest webRequest) {
251 final InetAddress localAddress = webClient_.getOptions().getLocalAddress();
252 final RequestConfig.Builder requestBuilder = createRequestConfigBuilder(getTimeout(webRequest), localAddress);
253
254 if (webRequest.getProxyHost() == null) {
255 requestBuilder.setProxy(null);
256 httpRequest.setConfig(requestBuilder.build());
257 return;
258 }
259
260 final HttpHost proxy = new HttpHost(webRequest.getProxyHost(),
261 webRequest.getProxyPort(), webRequest.getProxyScheme());
262 if (webRequest.isSocksProxy()) {
263 SocksConnectionSocketFactory.setSocksProxy(getHttpContext(), proxy);
264 }
265 else {
266 requestBuilder.setProxy(proxy);
267 httpRequest.setConfig(requestBuilder.build());
268 }
269 }
270
271
272
273
274
275
276
277
278 private HttpUriRequest makeHttpMethod(final WebRequest webRequest, final HttpClientBuilder httpClientBuilder)
279 throws URISyntaxException {
280
281 final HttpContext httpContext = getHttpContext();
282 final Charset charset = webRequest.getCharset();
283
284
285
286
287 final URL url = UrlUtils.encodeUrl(webRequest.getUrl(), charset);
288
289 URI uri = UrlUtils.toURI(url, escapeQuery(url.getQuery()));
290 if (getVirtualHost() != null) {
291 uri = URI.create(getVirtualHost());
292 }
293 final HttpRequestBase httpMethod = buildHttpMethod(webRequest.getHttpMethod(), uri);
294 setProxy(httpMethod, webRequest);
295
296
297
298
299
300 if ((httpMethod instanceof HttpEntityEnclosingRequest)
301 && (httpMethod instanceof HttpPost
302 || httpMethod instanceof HttpPut
303 || httpMethod instanceof HttpPatch
304 || httpMethod instanceof org.htmlunit.httpclient.HttpDelete
305 || httpMethod instanceof org.htmlunit.httpclient.HttpOptions)) {
306
307 final HttpEntityEnclosingRequest method = (HttpEntityEnclosingRequest) httpMethod;
308
309 if (FormEncodingType.URL_ENCODED == webRequest.getEncodingType()) {
310 if (webRequest.getRequestBody() == null) {
311 final List<NameValuePair> pairs = webRequest.getRequestParameters();
312 final String query = HttpUtils.toQueryFormFields(pairs, charset);
313
314 final StringEntity urlEncodedEntity;
315 if (webRequest.hasHint(HttpHint.IncludeCharsetInContentTypeHeader)) {
316 urlEncodedEntity = new StringEntity(query,
317 ContentType.create(URLEncodedUtils.CONTENT_TYPE, charset));
318
319 }
320 else {
321 urlEncodedEntity = new StringEntity(query, charset);
322 urlEncodedEntity.setContentType(URLEncodedUtils.CONTENT_TYPE);
323 }
324 method.setEntity(urlEncodedEntity);
325 }
326 else {
327 final String body = StringUtils.defaultString(webRequest.getRequestBody());
328 final StringEntity urlEncodedEntity = new StringEntity(body, charset);
329 urlEncodedEntity.setContentType(URLEncodedUtils.CONTENT_TYPE);
330 method.setEntity(urlEncodedEntity);
331 }
332 }
333 else if (FormEncodingType.TEXT_PLAIN == webRequest.getEncodingType()) {
334 if (webRequest.getRequestBody() == null) {
335 final StringBuilder body = new StringBuilder();
336 for (final NameValuePair pair : webRequest.getRequestParameters()) {
337 body.append(StringUtils.remove(StringUtils.remove(pair.getName(), '\r'), '\n'))
338 .append('=')
339 .append(StringUtils.remove(StringUtils.remove(pair.getValue(), '\r'), '\n'))
340 .append("\r\n");
341 }
342 final StringEntity bodyEntity = new StringEntity(body.toString(), charset);
343 bodyEntity.setContentType(MimeType.TEXT_PLAIN);
344 method.setEntity(bodyEntity);
345 }
346 else {
347 final String body = StringUtils.defaultString(webRequest.getRequestBody());
348 final StringEntity bodyEntity =
349 new StringEntity(body, ContentType.create(MimeType.TEXT_PLAIN, charset));
350 method.setEntity(bodyEntity);
351 }
352 }
353 else if (FormEncodingType.MULTIPART == webRequest.getEncodingType()) {
354 final Charset c = getCharset(charset, webRequest.getRequestParameters());
355 final MultipartEntityBuilder builder = MultipartEntityBuilder.create().setLaxMode();
356 builder.setCharset(c);
357
358 for (final NameValuePair pair : webRequest.getRequestParameters()) {
359 if (pair instanceof KeyDataPair) {
360 buildFilePart((KeyDataPair) pair, builder);
361 }
362 else {
363 builder.addTextBody(pair.getName(), pair.getValue(),
364 ContentType.create(MimeType.TEXT_PLAIN, charset));
365 }
366 }
367 method.setEntity(builder.build());
368 }
369 else {
370
371 final String body = webRequest.getRequestBody();
372 if (body != null) {
373 method.setEntity(new StringEntity(body, charset));
374 }
375 }
376 }
377 else {
378
379 final List<NameValuePair> pairs = webRequest.getRequestParameters();
380 if (!pairs.isEmpty()) {
381 final String query = HttpUtils.toQueryFormFields(pairs, charset);
382 uri = UrlUtils.toURI(url, query);
383 httpMethod.setURI(uri);
384 }
385 }
386
387 configureHttpProcessorBuilder(httpClientBuilder, webRequest);
388
389
390
391 final CredentialsProvider credentialsProvider = webClient_.getCredentialsProvider();
392
393
394 final Credentials requestUrlCredentials = webRequest.getUrlCredentials();
395 if (null != requestUrlCredentials) {
396 final URL requestUrl = webRequest.getUrl();
397 final AuthScope authScope = new AuthScope(requestUrl.getHost(), requestUrl.getPort());
398
399 credentialsProvider.setCredentials(authScope, requestUrlCredentials);
400 }
401
402
403 final Credentials requestCredentials = webRequest.getCredentials();
404 if (null != requestCredentials) {
405 final URL requestUrl = webRequest.getUrl();
406 final AuthScope authScope = new AuthScope(requestUrl.getHost(), requestUrl.getPort());
407
408 credentialsProvider.setCredentials(authScope, requestCredentials);
409 }
410 httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
411 httpContext.removeAttribute(HttpClientContext.CREDS_PROVIDER);
412 httpContext.removeAttribute(HttpClientContext.TARGET_AUTH_STATE);
413 return httpMethod;
414 }
415
416 private static String escapeQuery(final String query) {
417 if (query == null) {
418 return null;
419 }
420 return query.replace("%%", "%25%25");
421 }
422
423 private static Charset getCharset(final Charset charset, final List<NameValuePair> pairs) {
424 for (final NameValuePair pair : pairs) {
425 if (pair instanceof KeyDataPair) {
426 final KeyDataPair pairWithFile = (KeyDataPair) pair;
427 if (pairWithFile.getData() == null && pairWithFile.getFile() != null) {
428 final String fileName = pairWithFile.getFile().getName();
429 for (int i = 0; i < fileName.length(); i++) {
430 if (fileName.codePointAt(i) > 127) {
431 return charset;
432 }
433 }
434 }
435 }
436 }
437 return null;
438 }
439
440 void buildFilePart(final KeyDataPair pairWithFile, final MultipartEntityBuilder builder) {
441 String mimeType = pairWithFile.getMimeType();
442 if (mimeType == null) {
443 mimeType = MimeType.APPLICATION_OCTET_STREAM;
444 }
445
446 final ContentType contentType = ContentType.create(mimeType);
447
448 final File file = pairWithFile.getFile();
449 if (file != null) {
450 String filename = pairWithFile.getFileName();
451 if (filename == null) {
452 filename = pairWithFile.getFile().getName();
453 }
454 builder.addBinaryBody(pairWithFile.getName(), file, contentType, filename);
455 return;
456 }
457
458 final byte[] data = pairWithFile.getData();
459 if (data != null) {
460 String filename = pairWithFile.getFileName();
461 if (filename == null) {
462 filename = pairWithFile.getValue();
463 }
464
465 builder.addBinaryBody(pairWithFile.getName(), new ByteArrayInputStream(data),
466 contentType, filename);
467 return;
468 }
469
470 builder.addPart(pairWithFile.getName(),
471
472 new InputStreamBody(new ByteArrayInputStream(new byte[0]), contentType, pairWithFile.getValue()) {
473 @Override
474 public long getContentLength() {
475 return 0;
476 }
477 });
478 }
479
480
481
482
483
484
485
486 private static HttpRequestBase buildHttpMethod(final HttpMethod submitMethod, final URI uri) {
487 final HttpRequestBase method;
488 switch (submitMethod) {
489 case GET:
490 method = new HttpGet(uri);
491 break;
492
493 case POST:
494 method = new HttpPost(uri);
495 break;
496
497 case PUT:
498 method = new HttpPut(uri);
499 break;
500
501 case DELETE:
502 method = new org.htmlunit.httpclient.HttpDelete(uri);
503 break;
504
505 case OPTIONS:
506 method = new org.htmlunit.httpclient.HttpOptions(uri);
507 break;
508
509 case HEAD:
510 method = new HttpHead(uri);
511 break;
512
513 case TRACE:
514 method = new HttpTrace(uri);
515 break;
516
517 case PATCH:
518 method = new HttpPatch(uri);
519 break;
520
521 default:
522 throw new IllegalStateException("Submit method not yet supported: " + submitMethod);
523 }
524 return method;
525 }
526
527
528
529
530
531
532 protected HttpClientBuilder getHttpClientBuilder() {
533 final Thread currentThread = Thread.currentThread();
534 HttpClientBuilder builder = httpClientBuilder_.get(currentThread);
535 if (builder == null) {
536 builder = createHttpClientBuilder();
537
538
539
540 final RegistryBuilder<CookieSpecProvider> registeryBuilder
541 = RegistryBuilder.<CookieSpecProvider>create()
542 .register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecProvider_);
543 builder.setDefaultCookieSpecRegistry(registeryBuilder.build());
544
545 builder.setDefaultCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager()));
546 builder.setUserAgent(webClient_.getBrowserVersion().getUserAgent());
547 httpClientBuilder_.put(currentThread, builder);
548 }
549
550 return builder;
551 }
552
553
554
555
556
557
558
559
560 protected int getTimeout(final WebRequest webRequest) {
561 if (webRequest == null || webRequest.getTimeout() < 0) {
562 return webClient_.getOptions().getTimeout();
563 }
564
565 return webRequest.getTimeout();
566 }
567
568
569
570
571
572
573
574
575
576 protected HttpClientBuilder createHttpClientBuilder() {
577 final HttpClientBuilder builder = HttpClientBuilder.create();
578 builder.setRedirectStrategy(new HtmlUnitRedirectStrategie());
579 configureTimeout(builder, getTimeout(null));
580 configureHttpsScheme(builder);
581 builder.setMaxConnPerRoute(6);
582
583 builder.setConnectionManagerShared(true);
584 return builder;
585 }
586
587 private void configureTimeout(final HttpClientBuilder builder, final int timeout) {
588 final InetAddress localAddress = webClient_.getOptions().getLocalAddress();
589 final RequestConfig.Builder requestBuilder = createRequestConfigBuilder(timeout, localAddress);
590 builder.setDefaultRequestConfig(requestBuilder.build());
591
592 builder.setDefaultSocketConfig(createSocketConfigBuilder(timeout).build());
593
594 getHttpContext().removeAttribute(HttpClientContext.REQUEST_CONFIG);
595 usedOptions_.setTimeout(timeout);
596 }
597
598 private static RequestConfig.Builder createRequestConfigBuilder(final int timeout, final InetAddress localAddress) {
599 return RequestConfig.custom()
600 .setCookieSpec(HACKED_COOKIE_POLICY)
601 .setRedirectsEnabled(false)
602 .setLocalAddress(localAddress)
603
604
605 .setConnectTimeout(timeout)
606 .setConnectionRequestTimeout(timeout)
607 .setSocketTimeout(timeout);
608 }
609
610 private static SocketConfig.Builder createSocketConfigBuilder(final int timeout) {
611 return SocketConfig.custom()
612
613 .setSoTimeout(timeout);
614 }
615
616
617
618
619
620 private HttpClientBuilder reconfigureHttpClientIfNeeded(final HttpClientBuilder httpClientBuilder,
621 final WebRequest webRequest) {
622 final WebClientOptions options = webClient_.getOptions();
623
624
625 if (options.isUseInsecureSSL() != usedOptions_.isUseInsecureSSL()
626 || options.getSSLClientCertificateStore() != usedOptions_.getSSLClientCertificateStore()
627 || options.getSSLTrustStore() != usedOptions_.getSSLTrustStore()
628 || options.getSSLClientCipherSuites() != usedOptions_.getSSLClientCipherSuites()
629 || options.getSSLClientProtocols() != usedOptions_.getSSLClientProtocols()
630 || options.getProxyConfig() != usedOptions_.getProxyConfig()) {
631 configureHttpsScheme(httpClientBuilder);
632
633 if (connectionManager_ != null) {
634 connectionManager_.shutdown();
635 connectionManager_ = null;
636 }
637 }
638
639 final int timeout = getTimeout(webRequest);
640 if (timeout != usedOptions_.getTimeout()) {
641 configureTimeout(httpClientBuilder, timeout);
642 }
643
644 final long connectionTimeToLive = webClient_.getOptions().getConnectionTimeToLive();
645 if (connectionTimeToLive != usedOptions_.getConnectionTimeToLive()) {
646 httpClientBuilder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
647 usedOptions_.setConnectionTimeToLive(connectionTimeToLive);
648 }
649
650 if (connectionManager_ == null) {
651 connectionManager_ = createConnectionManager(httpClientBuilder);
652 }
653 httpClientBuilder.setConnectionManager(connectionManager_);
654
655 return httpClientBuilder;
656 }
657
658 private void configureHttpsScheme(final HttpClientBuilder builder) {
659 final WebClientOptions options = webClient_.getOptions();
660
661 final SSLConnectionSocketFactory socketFactory =
662 HtmlUnitSSLConnectionSocketFactory.buildSSLSocketFactory(options);
663
664 builder.setSSLSocketFactory(socketFactory);
665
666 usedOptions_.setUseInsecureSSL(options.isUseInsecureSSL());
667 usedOptions_.setSSLClientCertificateKeyStore(options.getSSLClientCertificateStore(),
668 options.getSSLClientCertificatePassword());
669 usedOptions_.setSSLTrustStore(options.getSSLTrustStore());
670 usedOptions_.setSSLClientCipherSuites(options.getSSLClientCipherSuites());
671 usedOptions_.setSSLClientProtocols(options.getSSLClientProtocols());
672 usedOptions_.setProxyConfig(options.getProxyConfig());
673 }
674
675 private void configureHttpProcessorBuilder(final HttpClientBuilder builder, final WebRequest webRequest) {
676 final HttpProcessorBuilder b = HttpProcessorBuilder.create();
677 for (final HttpRequestInterceptor i : getHttpRequestInterceptors(webRequest)) {
678 b.add(i);
679 }
680
681
682
683 b.addAll(new RequestDefaultHeaders(null),
684 new RequestContent(),
685 new RequestTargetHost(),
686 new RequestExpectContinue());
687 b.add(new RequestAcceptEncoding());
688 b.add(new RequestAuthCache());
689
690 if (!webRequest.hasHint(HttpHint.BlockCookies)) {
691 b.add(new ResponseProcessCookies());
692 }
693 builder.setHttpProcessor(b.build());
694 }
695
696
697
698
699
700 public void setVirtualHost(final String virtualHost) {
701 virtualHost_ = virtualHost;
702 }
703
704
705
706
707
708 public String getVirtualHost() {
709 return virtualHost_;
710 }
711
712
713
714
715
716
717
718
719
720 protected WebResponse makeWebResponse(final HttpResponse httpResponse,
721 final WebRequest webRequest, final DownloadedContent responseBody, final long loadTime) {
722
723 String statusMessage = httpResponse.getStatusLine().getReasonPhrase();
724 if (statusMessage == null) {
725 statusMessage = "Unknown status message";
726 }
727 final int statusCode = httpResponse.getStatusLine().getStatusCode();
728 final List<NameValuePair> headers = new ArrayList<>();
729 for (final Header header : httpResponse.getAllHeaders()) {
730 headers.add(new NameValuePair(header.getName(), header.getValue()));
731 }
732 final WebResponseData responseData = new WebResponseData(responseBody, statusCode, statusMessage, headers);
733 return newWebResponseInstance(responseData, loadTime, webRequest);
734 }
735
736
737
738
739
740
741
742
743
744
745
746 protected WebResponse downloadResponse(final HttpUriRequest httpMethod,
747 final WebRequest webRequest, final HttpResponse httpResponse,
748 final long startTime) throws IOException {
749
750 final DownloadedContent downloadedBody = downloadResponseBody(httpResponse);
751 final long endTime = System.currentTimeMillis();
752
753 return makeWebResponse(httpResponse, webRequest, downloadedBody, endTime - startTime);
754 }
755
756
757
758
759
760
761
762 protected DownloadedContent downloadResponseBody(final HttpResponse httpResponse) throws IOException {
763 final HttpEntity httpEntity = httpResponse.getEntity();
764 if (httpEntity == null) {
765 return new DownloadedContent.InMemory(null);
766 }
767
768 try (InputStream is = httpEntity.getContent()) {
769 return downloadContent(is, webClient_.getOptions().getMaxInMemory(),
770 webClient_.getOptions().getTempFileDirectory());
771 }
772 }
773
774
775
776
777
778
779
780
781
782 public static DownloadedContent downloadContent(final InputStream is, final int maxInMemory,
783 final File tempFileDirectory) throws IOException {
784 if (is == null) {
785 return new DownloadedContent.InMemory(null);
786 }
787
788 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
789 final byte[] buffer = new byte[1024];
790 int nbRead;
791 try {
792 while ((nbRead = is.read(buffer)) != -1) {
793 bos.write(buffer, 0, nbRead);
794 if (maxInMemory > 0 && bos.size() > maxInMemory) {
795
796 final File file = File.createTempFile("htmlunit", ".tmp", tempFileDirectory);
797 file.deleteOnExit();
798 try (OutputStream fos = Files.newOutputStream(file.toPath())) {
799 bos.writeTo(fos);
800 IOUtils.copyLarge(is, fos);
801 }
802 return new DownloadedContent.OnFile(file, true);
803 }
804 }
805 }
806 catch (final ConnectionClosedException e) {
807 LOG.warn("Connection was closed while reading from stream.", e);
808 return new DownloadedContent.InMemory(bos.toByteArray());
809 }
810 catch (final EOFException e) {
811
812 LOG.warn("EOFException while reading from stream.", e);
813 return new DownloadedContent.InMemory(bos.toByteArray());
814 }
815
816 return new DownloadedContent.InMemory(bos.toByteArray());
817 }
818 }
819
820
821
822
823
824
825
826
827
828 protected WebResponse newWebResponseInstance(
829 final WebResponseData responseData,
830 final long loadTime,
831 final WebRequest webRequest) {
832 return new WebResponse(responseData, webRequest, loadTime);
833 }
834
835 private List<HttpRequestInterceptor> getHttpRequestInterceptors(final WebRequest webRequest) {
836 final List<HttpRequestInterceptor> list = new ArrayList<>();
837 final Map<String, String> requestHeaders = webRequest.getAdditionalHeaders();
838 final URL url = webRequest.getUrl();
839 final StringBuilder host = new StringBuilder(url.getHost());
840
841 final int port = url.getPort();
842 if (port > 0 && port != url.getDefaultPort()) {
843 host.append(':').append(port);
844 }
845
846
847 final String[] headerNames = webClient_.getBrowserVersion().getHeaderNamesOrdered();
848 for (final String header : headerNames) {
849 if (HttpHeader.HOST.equals(header)) {
850 list.add(new HostHeaderHttpRequestInterceptor(host.toString()));
851 }
852 else if (HttpHeader.USER_AGENT.equals(header)) {
853 String headerValue = webRequest.getAdditionalHeader(HttpHeader.USER_AGENT);
854 if (headerValue == null) {
855 headerValue = webClient_.getBrowserVersion().getUserAgent();
856 }
857 list.add(new UserAgentHeaderHttpRequestInterceptor(headerValue));
858 }
859 else if (HttpHeader.ACCEPT.equals(header)) {
860 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT);
861 if (headerValue != null) {
862 list.add(new AcceptHeaderHttpRequestInterceptor(headerValue));
863 }
864 }
865 else if (HttpHeader.ACCEPT_LANGUAGE.equals(header)) {
866 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT_LANGUAGE);
867 if (headerValue != null) {
868 list.add(new AcceptLanguageHeaderHttpRequestInterceptor(headerValue));
869 }
870 }
871 else if (HttpHeader.ACCEPT_ENCODING.equals(header)) {
872 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT_ENCODING);
873 if (headerValue != null) {
874 list.add(new AcceptEncodingHeaderHttpRequestInterceptor(headerValue));
875 }
876 }
877 else if (HttpHeader.SEC_FETCH_DEST.equals(header)) {
878 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_DEST);
879 if (headerValue != null) {
880 list.add(new SecFetchDestHeaderHttpRequestInterceptor(headerValue));
881 }
882 }
883 else if (HttpHeader.SEC_FETCH_MODE.equals(header)) {
884 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_MODE);
885 if (headerValue != null) {
886 list.add(new SecFetchModeHeaderHttpRequestInterceptor(headerValue));
887 }
888 }
889 else if (HttpHeader.SEC_FETCH_SITE.equals(header)) {
890 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_SITE);
891 if (headerValue != null) {
892 list.add(new SecFetchSiteHeaderHttpRequestInterceptor(headerValue));
893 }
894 }
895 else if (HttpHeader.SEC_FETCH_USER.equals(header)) {
896 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_USER);
897 if (headerValue != null) {
898 list.add(new SecFetchUserHeaderHttpRequestInterceptor(headerValue));
899 }
900 }
901 else if (HttpHeader.SEC_CH_UA.equals(header)) {
902 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA);
903 if (headerValue != null) {
904 list.add(new SecClientHintUserAgentHeaderHttpRequestInterceptor(headerValue));
905 }
906 }
907 else if (HttpHeader.SEC_CH_UA_MOBILE.equals(header)) {
908 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA_MOBILE);
909 if (headerValue != null) {
910 list.add(new SecClientHintUserAgentMobileHeaderHttpRequestInterceptor(headerValue));
911 }
912 }
913 else if (HttpHeader.SEC_CH_UA_PLATFORM.equals(header)) {
914 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA_PLATFORM);
915 if (headerValue != null) {
916 list.add(new SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor(headerValue));
917 }
918 }
919 else if (HttpHeader.PRIORITY.equals(header)) {
920 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.PRIORITY);
921 if (headerValue != null) {
922 list.add(new PriorityHeaderHttpRequestInterceptor(headerValue));
923 }
924 }
925 else if (HttpHeader.UPGRADE_INSECURE_REQUESTS.equals(header)) {
926 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.UPGRADE_INSECURE_REQUESTS);
927 if (headerValue != null) {
928 list.add(new UpgradeInsecureRequestHeaderHttpRequestInterceptor(headerValue));
929 }
930 }
931 else if (HttpHeader.REFERER.equals(header)) {
932 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.REFERER);
933 if (headerValue != null) {
934 list.add(new RefererHeaderHttpRequestInterceptor(headerValue));
935 }
936 }
937 else if (HttpHeader.CONNECTION.equals(header)) {
938 list.add(new RequestClientConnControl());
939 }
940 else if (HttpHeader.COOKIE.equals(header)) {
941 if (!webRequest.hasHint(HttpHint.BlockCookies)) {
942 list.add(new RequestAddCookies());
943 }
944 }
945 else if (HttpHeader.DNT.equals(header) && webClient_.getOptions().isDoNotTrackEnabled()) {
946 list.add(new DntHeaderHttpRequestInterceptor("1"));
947 }
948 }
949
950
951
952 if (webClient_.getOptions().isDoNotTrackEnabled()) {
953 list.add(new DntHeaderHttpRequestInterceptor("1"));
954 }
955
956 synchronized (requestHeaders) {
957 list.add(new MultiHttpRequestInterceptor(new HashMap<>(requestHeaders)));
958 }
959 return list;
960 }
961
962
963 private static final class HostHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
964 private final String value_;
965
966 HostHeaderHttpRequestInterceptor(final String value) {
967 value_ = value;
968 }
969
970 @Override
971 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
972 request.setHeader(HttpHeader.HOST, value_);
973 }
974 }
975
976 private static final class UserAgentHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
977 private final String value_;
978
979 UserAgentHeaderHttpRequestInterceptor(final String value) {
980 value_ = value;
981 }
982
983 @Override
984 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
985 request.setHeader(HttpHeader.USER_AGENT, value_);
986 }
987 }
988
989 private static final class AcceptHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
990 private final String value_;
991
992 AcceptHeaderHttpRequestInterceptor(final String value) {
993 value_ = value;
994 }
995
996 @Override
997 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
998 request.setHeader(HttpHeader.ACCEPT, value_);
999 }
1000 }
1001
1002 private static final class AcceptLanguageHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1003 private final String value_;
1004
1005 AcceptLanguageHeaderHttpRequestInterceptor(final String value) {
1006 value_ = value;
1007 }
1008
1009 @Override
1010 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1011 request.setHeader(HttpHeader.ACCEPT_LANGUAGE, value_);
1012 }
1013 }
1014
1015 private static final class UpgradeInsecureRequestHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1016 private final String value_;
1017
1018 UpgradeInsecureRequestHeaderHttpRequestInterceptor(final String value) {
1019 value_ = value;
1020 }
1021
1022 @Override
1023 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1024 request.setHeader(HttpHeader.UPGRADE_INSECURE_REQUESTS, value_);
1025 }
1026 }
1027
1028 private static final class AcceptEncodingHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1029 private final String value_;
1030
1031 AcceptEncodingHeaderHttpRequestInterceptor(final String value) {
1032 value_ = value;
1033 }
1034
1035 @Override
1036 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1037 request.setHeader("Accept-Encoding", value_);
1038 }
1039 }
1040
1041 private static final class RefererHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1042 private final String value_;
1043
1044 RefererHeaderHttpRequestInterceptor(final String value) {
1045 value_ = value;
1046 }
1047
1048 @Override
1049 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1050 request.setHeader(HttpHeader.REFERER, value_);
1051 }
1052 }
1053
1054 private static final class DntHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1055 private final String value_;
1056
1057 DntHeaderHttpRequestInterceptor(final String value) {
1058 value_ = value;
1059 }
1060
1061 @Override
1062 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1063 request.setHeader(HttpHeader.DNT, value_);
1064 }
1065 }
1066
1067 private static final class SecFetchModeHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1068 private final String value_;
1069
1070 SecFetchModeHeaderHttpRequestInterceptor(final String value) {
1071 value_ = value;
1072 }
1073
1074 @Override
1075 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1076 request.setHeader(HttpHeader.SEC_FETCH_MODE, value_);
1077 }
1078 }
1079
1080 private static final class SecFetchSiteHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1081 private final String value_;
1082
1083 SecFetchSiteHeaderHttpRequestInterceptor(final String value) {
1084 value_ = value;
1085 }
1086
1087 @Override
1088 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1089 request.setHeader(HttpHeader.SEC_FETCH_SITE, value_);
1090 }
1091 }
1092
1093 private static final class SecFetchUserHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1094 private final String value_;
1095
1096 SecFetchUserHeaderHttpRequestInterceptor(final String value) {
1097 value_ = value;
1098 }
1099
1100 @Override
1101 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1102 request.setHeader(HttpHeader.SEC_FETCH_USER, value_);
1103 }
1104 }
1105
1106 private static final class SecFetchDestHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1107 private final String value_;
1108
1109 SecFetchDestHeaderHttpRequestInterceptor(final String value) {
1110 value_ = value;
1111 }
1112
1113 @Override
1114 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1115 request.setHeader(HttpHeader.SEC_FETCH_DEST, value_);
1116 }
1117 }
1118
1119 private static final class SecClientHintUserAgentHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1120 private final String value_;
1121
1122 SecClientHintUserAgentHeaderHttpRequestInterceptor(final String value) {
1123 value_ = value;
1124 }
1125
1126 @Override
1127 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1128 request.setHeader(HttpHeader.SEC_CH_UA, value_);
1129 }
1130 }
1131
1132 private static final class SecClientHintUserAgentMobileHeaderHttpRequestInterceptor
1133 implements HttpRequestInterceptor {
1134 private final String value_;
1135
1136 SecClientHintUserAgentMobileHeaderHttpRequestInterceptor(final String value) {
1137 value_ = value;
1138 }
1139
1140 @Override
1141 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1142 request.setHeader(HttpHeader.SEC_CH_UA_MOBILE, value_);
1143 }
1144 }
1145
1146 private static final class SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor
1147 implements HttpRequestInterceptor {
1148 private final String value_;
1149
1150 SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor(final String value) {
1151 value_ = value;
1152 }
1153
1154 @Override
1155 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1156 request.setHeader(HttpHeader.SEC_CH_UA_PLATFORM, value_);
1157 }
1158 }
1159
1160 private static final class PriorityHeaderHttpRequestInterceptor
1161 implements HttpRequestInterceptor {
1162 private final String value_;
1163
1164 PriorityHeaderHttpRequestInterceptor(final String value) {
1165 value_ = value;
1166 }
1167
1168 @Override
1169 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1170 request.setHeader(HttpHeader.PRIORITY, value_);
1171 }
1172 }
1173
1174 private static class MultiHttpRequestInterceptor implements HttpRequestInterceptor {
1175 private final Map<String, String> map_;
1176
1177 MultiHttpRequestInterceptor(final Map<String, String> map) {
1178 map_ = map;
1179 }
1180
1181 @Override
1182 public void process(final HttpRequest request, final HttpContext context)
1183 throws HttpException, IOException {
1184 for (final Map.Entry<String, String> entry : map_.entrySet()) {
1185 request.setHeader(entry.getKey(), entry.getValue());
1186 }
1187 }
1188 }
1189
1190 private static class RequestClientConnControl implements HttpRequestInterceptor {
1191
1192 private static final String PROXY_CONN_DIRECTIVE = "Proxy-Connection";
1193 private static final String CONN_DIRECTIVE = "Connection";
1194 private static final String CONN_KEEP_ALIVE = "keep-alive";
1195
1196
1197
1198
1199 RequestClientConnControl() {
1200 super();
1201 }
1202
1203 @Override
1204 public void process(final HttpRequest request, final HttpContext context)
1205 throws HttpException, IOException {
1206 final String method = request.getRequestLine().getMethod();
1207 if ("CONNECT".equalsIgnoreCase(method)) {
1208 request.setHeader(PROXY_CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1209 return;
1210 }
1211
1212 final HttpClientContext clientContext = HttpClientContext.adapt(context);
1213
1214
1215 final RouteInfo route = clientContext.getHttpRoute();
1216 if (route == null) {
1217 return;
1218 }
1219
1220 if ((route.getHopCount() == 1 || route.isTunnelled())
1221 && !request.containsHeader(CONN_DIRECTIVE)) {
1222 request.addHeader(CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1223 }
1224 if (route.getHopCount() == 2
1225 && !route.isTunnelled()
1226 && !request.containsHeader(PROXY_CONN_DIRECTIVE)) {
1227 request.addHeader(PROXY_CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1228 }
1229 }
1230 }
1231
1232
1233
1234
1235 private static final class SynchronizedAuthCache extends BasicAuthCache {
1236
1237
1238
1239
1240 SynchronizedAuthCache() {
1241 super();
1242 }
1243
1244
1245
1246
1247 @Override
1248 public synchronized void put(final HttpHost host, final AuthScheme authScheme) {
1249 super.put(host, authScheme);
1250 }
1251
1252
1253
1254
1255 @Override
1256 public synchronized AuthScheme get(final HttpHost host) {
1257 return super.get(host);
1258 }
1259
1260
1261
1262
1263 @Override
1264 public synchronized void remove(final HttpHost host) {
1265 super.remove(host);
1266 }
1267
1268
1269
1270
1271 @Override
1272 public synchronized void clear() {
1273 super.clear();
1274 }
1275
1276
1277
1278
1279 @Override
1280 public synchronized String toString() {
1281 return super.toString();
1282 }
1283 }
1284
1285
1286
1287
1288 @Override
1289 public void close() {
1290 httpClientBuilder_.clear();
1291
1292 if (connectionManager_ != null) {
1293 connectionManager_.shutdown();
1294 connectionManager_ = null;
1295 }
1296 }
1297
1298
1299
1300
1301
1302 private static PoolingHttpClientConnectionManager createConnectionManager(final HttpClientBuilder builder) {
1303 try {
1304 PublicSuffixMatcher publicSuffixMatcher = getField(builder, "publicSuffixMatcher");
1305 if (publicSuffixMatcher == null) {
1306 publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
1307 }
1308
1309 LayeredConnectionSocketFactory sslSocketFactory = getField(builder, "sslSocketFactory");
1310 final SocketConfig defaultSocketConfig = getField(builder, "defaultSocketConfig");
1311 final ConnectionConfig defaultConnectionConfig = getField(builder, "defaultConnectionConfig");
1312 final boolean systemProperties = getField(builder, "systemProperties");
1313 final int maxConnTotal = getField(builder, "maxConnTotal");
1314 final int maxConnPerRoute = getField(builder, "maxConnPerRoute");
1315 HostnameVerifier hostnameVerifier = getField(builder, "hostnameVerifier");
1316 final SSLContext sslcontext = getField(builder, "sslContext");
1317 final DnsResolver dnsResolver = getField(builder, "dnsResolver");
1318 final long connTimeToLive = getField(builder, "connTimeToLive");
1319 final TimeUnit connTimeToLiveTimeUnit = getField(builder, "connTimeToLiveTimeUnit");
1320
1321 if (sslSocketFactory == null) {
1322 final String[] supportedProtocols = systemProperties
1323 ? split(System.getProperty("https.protocols")) : null;
1324 final String[] supportedCipherSuites = systemProperties
1325 ? split(System.getProperty("https.cipherSuites")) : null;
1326 if (hostnameVerifier == null) {
1327 hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
1328 }
1329 if (sslcontext == null) {
1330 if (systemProperties) {
1331 sslSocketFactory = new SSLConnectionSocketFactory(
1332 (SSLSocketFactory) SSLSocketFactory.getDefault(),
1333 supportedProtocols, supportedCipherSuites, hostnameVerifier);
1334 }
1335 else {
1336 sslSocketFactory = new SSLConnectionSocketFactory(
1337 SSLContexts.createDefault(),
1338 hostnameVerifier);
1339 }
1340 }
1341 else {
1342 sslSocketFactory = new SSLConnectionSocketFactory(
1343 sslcontext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
1344 }
1345 }
1346
1347 final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
1348 RegistryBuilder.<ConnectionSocketFactory>create()
1349 .register("http", new SocksConnectionSocketFactory())
1350 .register("https", sslSocketFactory)
1351 .build(),
1352 null,
1353 null,
1354 dnsResolver,
1355 connTimeToLive,
1356 connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
1357 if (defaultSocketConfig != null) {
1358 poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
1359 }
1360 if (defaultConnectionConfig != null) {
1361 poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
1362 }
1363 if (systemProperties) {
1364 String s = System.getProperty("http.keepAlive", "true");
1365 if ("true".equalsIgnoreCase(s)) {
1366 s = System.getProperty("http.maxConnections", "5");
1367 final int max = Integer.parseInt(s);
1368 poolingmgr.setDefaultMaxPerRoute(max);
1369 poolingmgr.setMaxTotal(2 * max);
1370 }
1371 }
1372 if (maxConnTotal > 0) {
1373 poolingmgr.setMaxTotal(maxConnTotal);
1374 }
1375 if (maxConnPerRoute > 0) {
1376 poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
1377 }
1378 return poolingmgr;
1379 }
1380 catch (final IllegalAccessException e) {
1381 throw new RuntimeException(e);
1382 }
1383 }
1384
1385 private static String[] split(final String s) {
1386 if (TextUtils.isBlank(s)) {
1387 return null;
1388 }
1389 return s.split(" *, *");
1390 }
1391
1392 @SuppressWarnings("unchecked")
1393 private static <T> T getField(final Object target, final String fieldName) throws IllegalAccessException {
1394 return (T) FieldUtils.readDeclaredField(target, fieldName, true);
1395 }
1396 }