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 final int length = fileName.length();
430 for (int i = 0; i < length; i++) {
431 if (fileName.codePointAt(i) > 127) {
432 return charset;
433 }
434 }
435 }
436 }
437 }
438 return null;
439 }
440
441 void buildFilePart(final KeyDataPair pairWithFile, final MultipartEntityBuilder builder) {
442 String mimeType = pairWithFile.getMimeType();
443 if (mimeType == null) {
444 mimeType = MimeType.APPLICATION_OCTET_STREAM;
445 }
446
447 final ContentType contentType = ContentType.create(mimeType);
448
449 final File file = pairWithFile.getFile();
450 if (file != null) {
451 String filename = pairWithFile.getFileName();
452 if (filename == null) {
453 filename = pairWithFile.getFile().getName();
454 }
455 builder.addBinaryBody(pairWithFile.getName(), file, contentType, filename);
456 return;
457 }
458
459 final byte[] data = pairWithFile.getData();
460 if (data != null) {
461 String filename = pairWithFile.getFileName();
462 if (filename == null) {
463 filename = pairWithFile.getValue();
464 }
465
466 builder.addBinaryBody(pairWithFile.getName(), new ByteArrayInputStream(data),
467 contentType, filename);
468 return;
469 }
470
471 builder.addPart(pairWithFile.getName(),
472
473 new InputStreamBody(new ByteArrayInputStream(new byte[0]), contentType, pairWithFile.getValue()) {
474 @Override
475 public long getContentLength() {
476 return 0;
477 }
478 });
479 }
480
481
482
483
484
485
486
487 private static HttpRequestBase buildHttpMethod(final HttpMethod submitMethod, final URI uri) {
488 final HttpRequestBase method;
489 switch (submitMethod) {
490 case GET:
491 method = new HttpGet(uri);
492 break;
493
494 case POST:
495 method = new HttpPost(uri);
496 break;
497
498 case PUT:
499 method = new HttpPut(uri);
500 break;
501
502 case DELETE:
503 method = new org.htmlunit.httpclient.HttpDelete(uri);
504 break;
505
506 case OPTIONS:
507 method = new org.htmlunit.httpclient.HttpOptions(uri);
508 break;
509
510 case HEAD:
511 method = new HttpHead(uri);
512 break;
513
514 case TRACE:
515 method = new HttpTrace(uri);
516 break;
517
518 case PATCH:
519 method = new HttpPatch(uri);
520 break;
521
522 default:
523 throw new IllegalStateException("Submit method not yet supported: " + submitMethod);
524 }
525 return method;
526 }
527
528
529
530
531
532
533 protected HttpClientBuilder getHttpClientBuilder() {
534 final Thread currentThread = Thread.currentThread();
535 HttpClientBuilder builder = httpClientBuilder_.get(currentThread);
536 if (builder == null) {
537 builder = createHttpClientBuilder();
538
539
540
541 final RegistryBuilder<CookieSpecProvider> registeryBuilder
542 = RegistryBuilder.<CookieSpecProvider>create()
543 .register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecProvider_);
544 builder.setDefaultCookieSpecRegistry(registeryBuilder.build());
545
546 builder.setDefaultCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager()));
547 builder.setUserAgent(webClient_.getBrowserVersion().getUserAgent());
548 httpClientBuilder_.put(currentThread, builder);
549 }
550
551 return builder;
552 }
553
554
555
556
557
558
559
560
561 protected int getTimeout(final WebRequest webRequest) {
562 if (webRequest == null || webRequest.getTimeout() < 0) {
563 return webClient_.getOptions().getTimeout();
564 }
565
566 return webRequest.getTimeout();
567 }
568
569
570
571
572
573
574
575
576
577 protected HttpClientBuilder createHttpClientBuilder() {
578 final HttpClientBuilder builder = HttpClientBuilder.create();
579 builder.setRedirectStrategy(new HtmlUnitRedirectStrategie());
580 configureTimeout(builder, getTimeout(null));
581 configureHttpsScheme(builder);
582 builder.setMaxConnPerRoute(6);
583
584 builder.setConnectionManagerShared(true);
585 return builder;
586 }
587
588 private void configureTimeout(final HttpClientBuilder builder, final int timeout) {
589 final InetAddress localAddress = webClient_.getOptions().getLocalAddress();
590 final RequestConfig.Builder requestBuilder = createRequestConfigBuilder(timeout, localAddress);
591 builder.setDefaultRequestConfig(requestBuilder.build());
592
593 builder.setDefaultSocketConfig(createSocketConfigBuilder(timeout).build());
594
595 getHttpContext().removeAttribute(HttpClientContext.REQUEST_CONFIG);
596 usedOptions_.setTimeout(timeout);
597 }
598
599 private static RequestConfig.Builder createRequestConfigBuilder(final int timeout, final InetAddress localAddress) {
600 return RequestConfig.custom()
601 .setCookieSpec(HACKED_COOKIE_POLICY)
602 .setRedirectsEnabled(false)
603 .setLocalAddress(localAddress)
604
605
606 .setConnectTimeout(timeout)
607 .setConnectionRequestTimeout(timeout)
608 .setSocketTimeout(timeout);
609 }
610
611 private static SocketConfig.Builder createSocketConfigBuilder(final int timeout) {
612 return SocketConfig.custom()
613
614 .setSoTimeout(timeout);
615 }
616
617
618
619
620
621 private HttpClientBuilder reconfigureHttpClientIfNeeded(final HttpClientBuilder httpClientBuilder,
622 final WebRequest webRequest) {
623 final WebClientOptions options = webClient_.getOptions();
624
625
626 if (options.isUseInsecureSSL() != usedOptions_.isUseInsecureSSL()
627 || options.getSSLClientCertificateStore() != usedOptions_.getSSLClientCertificateStore()
628 || options.getSSLTrustStore() != usedOptions_.getSSLTrustStore()
629 || options.getSSLClientCipherSuites() != usedOptions_.getSSLClientCipherSuites()
630 || options.getSSLClientProtocols() != usedOptions_.getSSLClientProtocols()
631 || options.getProxyConfig() != usedOptions_.getProxyConfig()) {
632 configureHttpsScheme(httpClientBuilder);
633
634 if (connectionManager_ != null) {
635 connectionManager_.shutdown();
636 connectionManager_ = null;
637 }
638 }
639
640 final int timeout = getTimeout(webRequest);
641 if (timeout != usedOptions_.getTimeout()) {
642 configureTimeout(httpClientBuilder, timeout);
643 }
644
645 final long connectionTimeToLive = webClient_.getOptions().getConnectionTimeToLive();
646 if (connectionTimeToLive != usedOptions_.getConnectionTimeToLive()) {
647 httpClientBuilder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
648 usedOptions_.setConnectionTimeToLive(connectionTimeToLive);
649 }
650
651 if (connectionManager_ == null) {
652 connectionManager_ = createConnectionManager(httpClientBuilder);
653 }
654 httpClientBuilder.setConnectionManager(connectionManager_);
655
656 return httpClientBuilder;
657 }
658
659 private void configureHttpsScheme(final HttpClientBuilder builder) {
660 final WebClientOptions options = webClient_.getOptions();
661
662 final SSLConnectionSocketFactory socketFactory =
663 HtmlUnitSSLConnectionSocketFactory.buildSSLSocketFactory(options);
664
665 builder.setSSLSocketFactory(socketFactory);
666
667 usedOptions_.setUseInsecureSSL(options.isUseInsecureSSL());
668 usedOptions_.setSSLClientCertificateKeyStore(options.getSSLClientCertificateStore(),
669 options.getSSLClientCertificatePassword());
670 usedOptions_.setSSLTrustStore(options.getSSLTrustStore());
671 usedOptions_.setSSLClientCipherSuites(options.getSSLClientCipherSuites());
672 usedOptions_.setSSLClientProtocols(options.getSSLClientProtocols());
673 usedOptions_.setProxyConfig(options.getProxyConfig());
674 }
675
676 private void configureHttpProcessorBuilder(final HttpClientBuilder builder, final WebRequest webRequest) {
677 final HttpProcessorBuilder b = HttpProcessorBuilder.create();
678 for (final HttpRequestInterceptor i : getHttpRequestInterceptors(webRequest)) {
679 b.add(i);
680 }
681
682
683
684 b.addAll(new RequestDefaultHeaders(null),
685 new RequestContent(),
686 new RequestTargetHost(),
687 new RequestExpectContinue());
688 b.add(new RequestAcceptEncoding());
689 b.add(new RequestAuthCache());
690
691 if (!webRequest.hasHint(HttpHint.BlockCookies)) {
692 b.add(new ResponseProcessCookies());
693 }
694 builder.setHttpProcessor(b.build());
695 }
696
697
698
699
700
701 public void setVirtualHost(final String virtualHost) {
702 virtualHost_ = virtualHost;
703 }
704
705
706
707
708
709 public String getVirtualHost() {
710 return virtualHost_;
711 }
712
713
714
715
716
717
718
719
720
721 protected WebResponse makeWebResponse(final HttpResponse httpResponse,
722 final WebRequest webRequest, final DownloadedContent responseBody, final long loadTime) {
723
724 String statusMessage = httpResponse.getStatusLine().getReasonPhrase();
725 if (statusMessage == null) {
726 statusMessage = "Unknown status message";
727 }
728 final int statusCode = httpResponse.getStatusLine().getStatusCode();
729 final List<NameValuePair> headers = new ArrayList<>();
730 for (final Header header : httpResponse.getAllHeaders()) {
731 headers.add(new NameValuePair(header.getName(), header.getValue()));
732 }
733 final WebResponseData responseData = new WebResponseData(responseBody, statusCode, statusMessage, headers);
734 return newWebResponseInstance(responseData, loadTime, webRequest);
735 }
736
737
738
739
740
741
742
743
744
745
746
747 protected WebResponse downloadResponse(final HttpUriRequest httpMethod,
748 final WebRequest webRequest, final HttpResponse httpResponse,
749 final long startTime) throws IOException {
750
751 final DownloadedContent downloadedBody = downloadResponseBody(httpResponse);
752 final long endTime = System.currentTimeMillis();
753
754 return makeWebResponse(httpResponse, webRequest, downloadedBody, endTime - startTime);
755 }
756
757
758
759
760
761
762
763 protected DownloadedContent downloadResponseBody(final HttpResponse httpResponse) throws IOException {
764 final HttpEntity httpEntity = httpResponse.getEntity();
765 if (httpEntity == null) {
766 return new DownloadedContent.InMemory(null);
767 }
768
769 try (InputStream is = httpEntity.getContent()) {
770 return downloadContent(is, webClient_.getOptions().getMaxInMemory(),
771 webClient_.getOptions().getTempFileDirectory());
772 }
773 }
774
775
776
777
778
779
780
781
782
783 public static DownloadedContent downloadContent(final InputStream is, final int maxInMemory,
784 final File tempFileDirectory) throws IOException {
785 if (is == null) {
786 return new DownloadedContent.InMemory(null);
787 }
788
789 try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
790 final byte[] buffer = new byte[1024];
791 int nbRead;
792 try {
793 while ((nbRead = is.read(buffer)) != -1) {
794 bos.write(buffer, 0, nbRead);
795 if (maxInMemory > 0 && bos.size() > maxInMemory) {
796
797 final File file = File.createTempFile("htmlunit", ".tmp", tempFileDirectory);
798 file.deleteOnExit();
799 try (OutputStream fos = Files.newOutputStream(file.toPath())) {
800 bos.writeTo(fos);
801 IOUtils.copyLarge(is, fos);
802 }
803 return new DownloadedContent.OnFile(file, true);
804 }
805 }
806 }
807 catch (final ConnectionClosedException e) {
808 LOG.warn("Connection was closed while reading from stream.", e);
809 return new DownloadedContent.InMemory(bos.toByteArray());
810 }
811 catch (final EOFException e) {
812
813 LOG.warn("EOFException while reading from stream.", e);
814 return new DownloadedContent.InMemory(bos.toByteArray());
815 }
816
817 return new DownloadedContent.InMemory(bos.toByteArray());
818 }
819 }
820
821
822
823
824
825
826
827
828
829 protected WebResponse newWebResponseInstance(
830 final WebResponseData responseData,
831 final long loadTime,
832 final WebRequest webRequest) {
833 return new WebResponse(responseData, webRequest, loadTime);
834 }
835
836 private List<HttpRequestInterceptor> getHttpRequestInterceptors(final WebRequest webRequest) {
837 final List<HttpRequestInterceptor> list = new ArrayList<>();
838 final Map<String, String> requestHeaders = webRequest.getAdditionalHeaders();
839 final URL url = webRequest.getUrl();
840 final StringBuilder host = new StringBuilder(url.getHost());
841
842 final int port = url.getPort();
843 if (port > 0 && port != url.getDefaultPort()) {
844 host.append(':').append(port);
845 }
846
847
848 final String[] headerNames = webClient_.getBrowserVersion().getHeaderNamesOrdered();
849 for (final String header : headerNames) {
850 if (HttpHeader.HOST.equals(header)) {
851 list.add(new HostHeaderHttpRequestInterceptor(host.toString()));
852 }
853 else if (HttpHeader.USER_AGENT.equals(header)) {
854 String headerValue = webRequest.getAdditionalHeader(HttpHeader.USER_AGENT);
855 if (headerValue == null) {
856 headerValue = webClient_.getBrowserVersion().getUserAgent();
857 }
858 list.add(new UserAgentHeaderHttpRequestInterceptor(headerValue));
859 }
860 else if (HttpHeader.ACCEPT.equals(header)) {
861 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT);
862 if (headerValue != null) {
863 list.add(new AcceptHeaderHttpRequestInterceptor(headerValue));
864 }
865 }
866 else if (HttpHeader.ACCEPT_LANGUAGE.equals(header)) {
867 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT_LANGUAGE);
868 if (headerValue != null) {
869 list.add(new AcceptLanguageHeaderHttpRequestInterceptor(headerValue));
870 }
871 }
872 else if (HttpHeader.ACCEPT_ENCODING.equals(header)) {
873 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.ACCEPT_ENCODING);
874 if (headerValue != null) {
875 list.add(new AcceptEncodingHeaderHttpRequestInterceptor(headerValue));
876 }
877 }
878 else if (HttpHeader.SEC_FETCH_DEST.equals(header)) {
879 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_DEST);
880 if (headerValue != null) {
881 list.add(new SecFetchDestHeaderHttpRequestInterceptor(headerValue));
882 }
883 }
884 else if (HttpHeader.SEC_FETCH_MODE.equals(header)) {
885 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_MODE);
886 if (headerValue != null) {
887 list.add(new SecFetchModeHeaderHttpRequestInterceptor(headerValue));
888 }
889 }
890 else if (HttpHeader.SEC_FETCH_SITE.equals(header)) {
891 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_SITE);
892 if (headerValue != null) {
893 list.add(new SecFetchSiteHeaderHttpRequestInterceptor(headerValue));
894 }
895 }
896 else if (HttpHeader.SEC_FETCH_USER.equals(header)) {
897 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_FETCH_USER);
898 if (headerValue != null) {
899 list.add(new SecFetchUserHeaderHttpRequestInterceptor(headerValue));
900 }
901 }
902 else if (HttpHeader.SEC_CH_UA.equals(header)) {
903 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA);
904 if (headerValue != null) {
905 list.add(new SecClientHintUserAgentHeaderHttpRequestInterceptor(headerValue));
906 }
907 }
908 else if (HttpHeader.SEC_CH_UA_MOBILE.equals(header)) {
909 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA_MOBILE);
910 if (headerValue != null) {
911 list.add(new SecClientHintUserAgentMobileHeaderHttpRequestInterceptor(headerValue));
912 }
913 }
914 else if (HttpHeader.SEC_CH_UA_PLATFORM.equals(header)) {
915 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.SEC_CH_UA_PLATFORM);
916 if (headerValue != null) {
917 list.add(new SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor(headerValue));
918 }
919 }
920 else if (HttpHeader.PRIORITY.equals(header)) {
921 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.PRIORITY);
922 if (headerValue != null) {
923 list.add(new PriorityHeaderHttpRequestInterceptor(headerValue));
924 }
925 }
926 else if (HttpHeader.UPGRADE_INSECURE_REQUESTS.equals(header)) {
927 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.UPGRADE_INSECURE_REQUESTS);
928 if (headerValue != null) {
929 list.add(new UpgradeInsecureRequestHeaderHttpRequestInterceptor(headerValue));
930 }
931 }
932 else if (HttpHeader.REFERER.equals(header)) {
933 final String headerValue = webRequest.getAdditionalHeader(HttpHeader.REFERER);
934 if (headerValue != null) {
935 list.add(new RefererHeaderHttpRequestInterceptor(headerValue));
936 }
937 }
938 else if (HttpHeader.CONNECTION.equals(header)) {
939 list.add(new RequestClientConnControl());
940 }
941 else if (HttpHeader.COOKIE.equals(header)) {
942 if (!webRequest.hasHint(HttpHint.BlockCookies)) {
943 list.add(new RequestAddCookies());
944 }
945 }
946 else if (HttpHeader.DNT.equals(header) && webClient_.getOptions().isDoNotTrackEnabled()) {
947 list.add(new DntHeaderHttpRequestInterceptor("1"));
948 }
949 }
950
951
952
953 if (webClient_.getOptions().isDoNotTrackEnabled()) {
954 list.add(new DntHeaderHttpRequestInterceptor("1"));
955 }
956
957 synchronized (requestHeaders) {
958 list.add(new MultiHttpRequestInterceptor(new HashMap<>(requestHeaders)));
959 }
960 return list;
961 }
962
963
964 private static final class HostHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
965 private final String value_;
966
967 HostHeaderHttpRequestInterceptor(final String value) {
968 value_ = value;
969 }
970
971 @Override
972 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
973 request.setHeader(HttpHeader.HOST, value_);
974 }
975 }
976
977 private static final class UserAgentHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
978 private final String value_;
979
980 UserAgentHeaderHttpRequestInterceptor(final String value) {
981 value_ = value;
982 }
983
984 @Override
985 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
986 request.setHeader(HttpHeader.USER_AGENT, value_);
987 }
988 }
989
990 private static final class AcceptHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
991 private final String value_;
992
993 AcceptHeaderHttpRequestInterceptor(final String value) {
994 value_ = value;
995 }
996
997 @Override
998 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
999 request.setHeader(HttpHeader.ACCEPT, value_);
1000 }
1001 }
1002
1003 private static final class AcceptLanguageHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1004 private final String value_;
1005
1006 AcceptLanguageHeaderHttpRequestInterceptor(final String value) {
1007 value_ = value;
1008 }
1009
1010 @Override
1011 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1012 request.setHeader(HttpHeader.ACCEPT_LANGUAGE, value_);
1013 }
1014 }
1015
1016 private static final class UpgradeInsecureRequestHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1017 private final String value_;
1018
1019 UpgradeInsecureRequestHeaderHttpRequestInterceptor(final String value) {
1020 value_ = value;
1021 }
1022
1023 @Override
1024 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1025 request.setHeader(HttpHeader.UPGRADE_INSECURE_REQUESTS, value_);
1026 }
1027 }
1028
1029 private static final class AcceptEncodingHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1030 private final String value_;
1031
1032 AcceptEncodingHeaderHttpRequestInterceptor(final String value) {
1033 value_ = value;
1034 }
1035
1036 @Override
1037 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1038 request.setHeader("Accept-Encoding", value_);
1039 }
1040 }
1041
1042 private static final class RefererHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1043 private final String value_;
1044
1045 RefererHeaderHttpRequestInterceptor(final String value) {
1046 value_ = value;
1047 }
1048
1049 @Override
1050 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1051 request.setHeader(HttpHeader.REFERER, value_);
1052 }
1053 }
1054
1055 private static final class DntHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1056 private final String value_;
1057
1058 DntHeaderHttpRequestInterceptor(final String value) {
1059 value_ = value;
1060 }
1061
1062 @Override
1063 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1064 request.setHeader(HttpHeader.DNT, value_);
1065 }
1066 }
1067
1068 private static final class SecFetchModeHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1069 private final String value_;
1070
1071 SecFetchModeHeaderHttpRequestInterceptor(final String value) {
1072 value_ = value;
1073 }
1074
1075 @Override
1076 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1077 request.setHeader(HttpHeader.SEC_FETCH_MODE, value_);
1078 }
1079 }
1080
1081 private static final class SecFetchSiteHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1082 private final String value_;
1083
1084 SecFetchSiteHeaderHttpRequestInterceptor(final String value) {
1085 value_ = value;
1086 }
1087
1088 @Override
1089 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1090 request.setHeader(HttpHeader.SEC_FETCH_SITE, value_);
1091 }
1092 }
1093
1094 private static final class SecFetchUserHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1095 private final String value_;
1096
1097 SecFetchUserHeaderHttpRequestInterceptor(final String value) {
1098 value_ = value;
1099 }
1100
1101 @Override
1102 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1103 request.setHeader(HttpHeader.SEC_FETCH_USER, value_);
1104 }
1105 }
1106
1107 private static final class SecFetchDestHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1108 private final String value_;
1109
1110 SecFetchDestHeaderHttpRequestInterceptor(final String value) {
1111 value_ = value;
1112 }
1113
1114 @Override
1115 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1116 request.setHeader(HttpHeader.SEC_FETCH_DEST, value_);
1117 }
1118 }
1119
1120 private static final class SecClientHintUserAgentHeaderHttpRequestInterceptor implements HttpRequestInterceptor {
1121 private final String value_;
1122
1123 SecClientHintUserAgentHeaderHttpRequestInterceptor(final String value) {
1124 value_ = value;
1125 }
1126
1127 @Override
1128 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1129 request.setHeader(HttpHeader.SEC_CH_UA, value_);
1130 }
1131 }
1132
1133 private static final class SecClientHintUserAgentMobileHeaderHttpRequestInterceptor
1134 implements HttpRequestInterceptor {
1135 private final String value_;
1136
1137 SecClientHintUserAgentMobileHeaderHttpRequestInterceptor(final String value) {
1138 value_ = value;
1139 }
1140
1141 @Override
1142 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1143 request.setHeader(HttpHeader.SEC_CH_UA_MOBILE, value_);
1144 }
1145 }
1146
1147 private static final class SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor
1148 implements HttpRequestInterceptor {
1149 private final String value_;
1150
1151 SecClientHintUserAgentPlatformHeaderHttpRequestInterceptor(final String value) {
1152 value_ = value;
1153 }
1154
1155 @Override
1156 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1157 request.setHeader(HttpHeader.SEC_CH_UA_PLATFORM, value_);
1158 }
1159 }
1160
1161 private static final class PriorityHeaderHttpRequestInterceptor
1162 implements HttpRequestInterceptor {
1163 private final String value_;
1164
1165 PriorityHeaderHttpRequestInterceptor(final String value) {
1166 value_ = value;
1167 }
1168
1169 @Override
1170 public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
1171 request.setHeader(HttpHeader.PRIORITY, value_);
1172 }
1173 }
1174
1175 private static class MultiHttpRequestInterceptor implements HttpRequestInterceptor {
1176 private final Map<String, String> map_;
1177
1178 MultiHttpRequestInterceptor(final Map<String, String> map) {
1179 map_ = map;
1180 }
1181
1182 @Override
1183 public void process(final HttpRequest request, final HttpContext context)
1184 throws HttpException, IOException {
1185 for (final Map.Entry<String, String> entry : map_.entrySet()) {
1186 request.setHeader(entry.getKey(), entry.getValue());
1187 }
1188 }
1189 }
1190
1191 private static class RequestClientConnControl implements HttpRequestInterceptor {
1192
1193 private static final String PROXY_CONN_DIRECTIVE = "Proxy-Connection";
1194 private static final String CONN_DIRECTIVE = "Connection";
1195 private static final String CONN_KEEP_ALIVE = "keep-alive";
1196
1197
1198
1199
1200 RequestClientConnControl() {
1201 super();
1202 }
1203
1204 @Override
1205 public void process(final HttpRequest request, final HttpContext context)
1206 throws HttpException, IOException {
1207 final String method = request.getRequestLine().getMethod();
1208 if ("CONNECT".equalsIgnoreCase(method)) {
1209 request.setHeader(PROXY_CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1210 return;
1211 }
1212
1213 final HttpClientContext clientContext = HttpClientContext.adapt(context);
1214
1215
1216 final RouteInfo route = clientContext.getHttpRoute();
1217 if (route == null) {
1218 return;
1219 }
1220
1221 if ((route.getHopCount() == 1 || route.isTunnelled())
1222 && !request.containsHeader(CONN_DIRECTIVE)) {
1223 request.addHeader(CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1224 }
1225 if (route.getHopCount() == 2
1226 && !route.isTunnelled()
1227 && !request.containsHeader(PROXY_CONN_DIRECTIVE)) {
1228 request.addHeader(PROXY_CONN_DIRECTIVE, CONN_KEEP_ALIVE);
1229 }
1230 }
1231 }
1232
1233
1234
1235
1236 private static final class SynchronizedAuthCache extends BasicAuthCache {
1237
1238
1239
1240
1241 SynchronizedAuthCache() {
1242 super();
1243 }
1244
1245
1246
1247
1248 @Override
1249 public synchronized void put(final HttpHost host, final AuthScheme authScheme) {
1250 super.put(host, authScheme);
1251 }
1252
1253
1254
1255
1256 @Override
1257 public synchronized AuthScheme get(final HttpHost host) {
1258 return super.get(host);
1259 }
1260
1261
1262
1263
1264 @Override
1265 public synchronized void remove(final HttpHost host) {
1266 super.remove(host);
1267 }
1268
1269
1270
1271
1272 @Override
1273 public synchronized void clear() {
1274 super.clear();
1275 }
1276
1277
1278
1279
1280 @Override
1281 public synchronized String toString() {
1282 return super.toString();
1283 }
1284 }
1285
1286
1287
1288
1289 @Override
1290 public void close() {
1291 httpClientBuilder_.clear();
1292
1293 if (connectionManager_ != null) {
1294 connectionManager_.shutdown();
1295 connectionManager_ = null;
1296 }
1297 }
1298
1299
1300
1301
1302
1303 private static PoolingHttpClientConnectionManager createConnectionManager(final HttpClientBuilder builder) {
1304 try {
1305 PublicSuffixMatcher publicSuffixMatcher = getField(builder, "publicSuffixMatcher");
1306 if (publicSuffixMatcher == null) {
1307 publicSuffixMatcher = PublicSuffixMatcherLoader.getDefault();
1308 }
1309
1310 LayeredConnectionSocketFactory sslSocketFactory = getField(builder, "sslSocketFactory");
1311 final SocketConfig defaultSocketConfig = getField(builder, "defaultSocketConfig");
1312 final ConnectionConfig defaultConnectionConfig = getField(builder, "defaultConnectionConfig");
1313 final boolean systemProperties = getField(builder, "systemProperties");
1314 final int maxConnTotal = getField(builder, "maxConnTotal");
1315 final int maxConnPerRoute = getField(builder, "maxConnPerRoute");
1316 HostnameVerifier hostnameVerifier = getField(builder, "hostnameVerifier");
1317 final SSLContext sslcontext = getField(builder, "sslContext");
1318 final DnsResolver dnsResolver = getField(builder, "dnsResolver");
1319 final long connTimeToLive = getField(builder, "connTimeToLive");
1320 final TimeUnit connTimeToLiveTimeUnit = getField(builder, "connTimeToLiveTimeUnit");
1321
1322 if (sslSocketFactory == null) {
1323 final String[] supportedProtocols = systemProperties
1324 ? split(System.getProperty("https.protocols")) : null;
1325 final String[] supportedCipherSuites = systemProperties
1326 ? split(System.getProperty("https.cipherSuites")) : null;
1327 if (hostnameVerifier == null) {
1328 hostnameVerifier = new DefaultHostnameVerifier(publicSuffixMatcher);
1329 }
1330 if (sslcontext == null) {
1331 if (systemProperties) {
1332 sslSocketFactory = new SSLConnectionSocketFactory(
1333 (SSLSocketFactory) SSLSocketFactory.getDefault(),
1334 supportedProtocols, supportedCipherSuites, hostnameVerifier);
1335 }
1336 else {
1337 sslSocketFactory = new SSLConnectionSocketFactory(
1338 SSLContexts.createDefault(),
1339 hostnameVerifier);
1340 }
1341 }
1342 else {
1343 sslSocketFactory = new SSLConnectionSocketFactory(
1344 sslcontext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
1345 }
1346 }
1347
1348 final PoolingHttpClientConnectionManager poolingmgr = new PoolingHttpClientConnectionManager(
1349 RegistryBuilder.<ConnectionSocketFactory>create()
1350 .register("http", new SocksConnectionSocketFactory())
1351 .register("https", sslSocketFactory)
1352 .build(),
1353 null,
1354 null,
1355 dnsResolver,
1356 connTimeToLive,
1357 connTimeToLiveTimeUnit != null ? connTimeToLiveTimeUnit : TimeUnit.MILLISECONDS);
1358 if (defaultSocketConfig != null) {
1359 poolingmgr.setDefaultSocketConfig(defaultSocketConfig);
1360 }
1361 if (defaultConnectionConfig != null) {
1362 poolingmgr.setDefaultConnectionConfig(defaultConnectionConfig);
1363 }
1364 if (systemProperties) {
1365 String s = System.getProperty("http.keepAlive", "true");
1366 if ("true".equalsIgnoreCase(s)) {
1367 s = System.getProperty("http.maxConnections", "5");
1368 final int max = Integer.parseInt(s);
1369 poolingmgr.setDefaultMaxPerRoute(max);
1370 poolingmgr.setMaxTotal(2 * max);
1371 }
1372 }
1373 if (maxConnTotal > 0) {
1374 poolingmgr.setMaxTotal(maxConnTotal);
1375 }
1376 if (maxConnPerRoute > 0) {
1377 poolingmgr.setDefaultMaxPerRoute(maxConnPerRoute);
1378 }
1379 return poolingmgr;
1380 }
1381 catch (final IllegalAccessException e) {
1382 throw new RuntimeException(e);
1383 }
1384 }
1385
1386 private static String[] split(final String s) {
1387 if (TextUtils.isBlank(s)) {
1388 return null;
1389 }
1390 return s.split(" *, *");
1391 }
1392
1393 @SuppressWarnings("unchecked")
1394 private static <T> T getField(final Object target, final String fieldName) throws IllegalAccessException {
1395 return (T) FieldUtils.readDeclaredField(target, fieldName, true);
1396 }
1397 }