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