From: <asa...@us...> - 2014-01-23 01:19:51
|
Revision: 9037 http://sourceforge.net/p/htmlunit/code/9037 Author: asashour Date: 2014-01-23 01:19:49 +0000 (Thu, 23 Jan 2014) Log Message: ----------- Test new HttpWebConnection Modified Paths: -------------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HttpWebConnection.java trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/SocksSocketFactory.java trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/HttpWebConnectionTest.java Added Paths: ----------- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HtmlUnitSSLConnectionSocketFactory.java Added: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HtmlUnitSSLConnectionSocketFactory.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HtmlUnitSSLConnectionSocketFactory.java (rev 0) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HtmlUnitSSLConnectionSocketFactory.java 2014-01-23 01:19:49 UTC (rev 9037) @@ -0,0 +1,239 @@ +/* + * Copyright (c) 2002-2014 Gargoyle Software Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.gargoylesoftware.htmlunit; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.security.GeneralSecurityException; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.http.HttpHost; +import org.apache.http.conn.ConnectTimeoutException; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.conn.ssl.X509HostnameVerifier; +import org.apache.http.protocol.HttpContext; + +/** + * Socket factory offering facilities for insecure SSL and for SOCKS proxy support. + * This looks rather like a hack than like clean code but at the time of the writing it seems to + * be the easiest way to provide SOCKS proxy support for HTTPS. + * + * @version $Revision$ + * @author Nicolas Belisle + * @author Ahmed Ashour + * @author Martin Huber + * @author Marc Guillemot + * @author Ronald Brill + */ +final class HtmlUnitSSLConnectionSocketFactory extends SSLConnectionSocketFactory { + private static final String SSL3ONLY = "htmlunit.SSL3Only"; + + static boolean isUseSSL3Only(final HttpContext context) { + return "TRUE".equalsIgnoreCase((String) context.getAttribute(SSL3ONLY)); + } + + public static SSLConnectionSocketFactory buildSSLSocketFactory(final WebClientOptions options) { + try { + if (!options.isUseInsecureSSL()) { + if (options.getSSLClientCertificateUrl() == null) { + return new HtmlUnitSSLConnectionSocketFactory((KeyStore) null, null); // only SOCKS awareness + } + // SOCKS + keystore + return new HtmlUnitSSLConnectionSocketFactory(getKeyStore(options), options.getSSLClientCertificatePassword()); + } + + // we need insecure SSL + SOCKS awareness + final SSLContext sslContext = SSLContext.getInstance("SSL"); + sslContext.init(getKeyManagers(options), new TrustManager[]{new InsecureTrustManager2()}, null); + + final SSLConnectionSocketFactory factory = new HtmlUnitSSLConnectionSocketFactory(sslContext, + SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); + return factory; + } + catch (final GeneralSecurityException e) { + throw new RuntimeException(e); + } + } + + private HtmlUnitSSLConnectionSocketFactory(final SSLContext sslContext, final X509HostnameVerifier hostnameVerifier) { + super(sslContext, hostnameVerifier); + } + + private HtmlUnitSSLConnectionSocketFactory(final KeyStore keystore, final String keystorePassword) + throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException { + super(SSLContexts.custom() + .loadKeyMaterial(keystore, keystorePassword != null ? keystorePassword.toCharArray() : null) + .build(), + BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); + } + + /** + * {@inheritDoc} + */ + @Override + public Socket createSocket(final HttpContext context) throws IOException { + if (SocksSocketFactory.getSocksProxy(context) != null) { + // we create the socket in connectSocket has we need to know the destination to open the underlying request + return null; + } + final Socket socket = super.createSocket(context); + configureSocket((SSLSocket) socket, context); + return socket; + } + + private void configureSocket(final SSLSocket sslSocket, final HttpContext context) { + if (isUseSSL3Only(context)) { + sslSocket.setEnabledProtocols(new String[]{"SSLv3"}); + } + } + + public Socket connectSocket( + final int connectTimeout, + final Socket socket, + final HttpHost host, + final InetSocketAddress remoteAddress, + final InetSocketAddress localAddress, + final HttpContext context) throws IOException { + final HttpHost socksProxy = SocksSocketFactory.getSocksProxy(context); + if (socksProxy != null) { + final Socket underlying = SocksSocketFactory.createSocketWithSocksProxy(socksProxy); + underlying.setReuseAddress(true); + + // TODO: commented out for HttpClient 4.3 + // final int soTimeout = HttpConnectionParams.getSoTimeout(params); + + final SocketAddress socksProxyAddress = new InetSocketAddress(socksProxy.getHostName(), + socksProxy.getPort()); + try { + //underlying.setSoTimeout(soTimeout); + underlying.connect(remoteAddress, connectTimeout); + } + catch (final SocketTimeoutException ex) { + throw new ConnectTimeoutException("Connect to " + socksProxyAddress + " timed out"); + } + + final Socket sslSocket = getSSLSocketFactory().createSocket(underlying, socksProxy.getHostName(), + socksProxy.getPort(), true); + configureSocket((SSLSocket) sslSocket, context); + return sslSocket; + } + return super.connectSocket(connectTimeout, socket, host, remoteAddress, localAddress, context); + } + + private javax.net.ssl.SSLSocketFactory getSSLSocketFactory() { + try { + final Field field = SSLConnectionSocketFactory.class.getDeclaredField("socketfactory"); + field.setAccessible(true); + return (javax.net.ssl.SSLSocketFactory) field.get(this); + } + catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private static KeyManager[] getKeyManagers(final WebClientOptions options) { + if (options.getSSLClientCertificateUrl() == null) { + return null; + } + try { + final String password = options.getSSLClientCertificatePassword(); + final char[] passwordChars = password != null ? password.toCharArray() : null; + + final KeyStore keyStore = getKeyStore(options); + final KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); + keyManagerFactory.init(keyStore, passwordChars); + return keyManagerFactory.getKeyManagers(); + } + catch (final Exception e) { + throw new RuntimeException(e); + } + } + + private static KeyStore getKeyStore(final WebClientOptions options) { + try { + final KeyStore keyStore = KeyStore.getInstance(options.getSSLClientCertificateType()); + final String password = options.getSSLClientCertificatePassword(); + final char[] passwordChars = password != null ? password.toCharArray() : null; + keyStore.load(options.getSSLClientCertificateUrl().openStream(), passwordChars); + return keyStore; + } + catch (final Exception e) { + throw new RuntimeException(e); + } + } +} + +/** + * A completely insecure (yet very easy to use) x509 trust manager. This manager trusts all servers + * and all clients, regardless of credentials or lack thereof. + * + * @version $Revision$ + * @author Daniel Gredler + * @author Marc Guillemot + */ +class InsecureTrustManager2 implements X509TrustManager { + private final Set<X509Certificate> acceptedIssuers_ = new HashSet<X509Certificate>(); + + /** + * {@inheritDoc} + */ + public void checkClientTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { + // Everyone is trusted! + acceptedIssuers_.addAll(Arrays.asList(chain)); + } + + /** + * {@inheritDoc} + */ + public void checkServerTrusted(final X509Certificate[] chain, final String authType) throws CertificateException { + // Everyone is trusted! + acceptedIssuers_.addAll(Arrays.asList(chain)); + } + + /** + * {@inheritDoc} + */ + public X509Certificate[] getAcceptedIssuers() { + // it seems to be OK for Java <= 6 to return an empty array but not for Java 7 (at least 1.7.0_04-b20): + // requesting an URL with a valid certificate (working without WebClient.setUseInsecureSSL(true)) throws a + // javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated + // when the array returned here is empty + if (acceptedIssuers_.isEmpty()) { + return new X509Certificate[0]; + } + return acceptedIssuers_.toArray(new X509Certificate[acceptedIssuers_.size()]); + } +} Property changes on: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HtmlUnitSSLConnectionSocketFactory.java ___________________________________________________________________ Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +Author Date Id Revision \ No newline at end of property Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HttpWebConnection.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HttpWebConnection.java 2014-01-22 20:18:44 UTC (rev 9036) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/HttpWebConnection.java 2014-01-23 01:19:49 UTC (rev 9037) @@ -53,6 +53,8 @@ import org.apache.http.auth.Credentials; import org.apache.http.client.CookieStore; import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.HttpDelete; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; @@ -62,41 +64,37 @@ import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.methods.HttpTrace; import org.apache.http.client.methods.HttpUriRequest; -import org.apache.http.client.params.ClientPNames; -import org.apache.http.client.params.HttpClientParams; +import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.client.utils.DateUtils; import org.apache.http.client.utils.URIUtils; import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.conn.params.ConnRoutePNames; -import org.apache.http.conn.scheme.Scheme; -import org.apache.http.conn.scheme.SchemeRegistry; -import org.apache.http.conn.ssl.SSLSocketFactory; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.cookie.ClientCookie; import org.apache.http.cookie.Cookie; import org.apache.http.cookie.CookieAttributeHandler; import org.apache.http.cookie.CookieOrigin; import org.apache.http.cookie.CookiePathComparator; import org.apache.http.cookie.CookieSpec; -import org.apache.http.cookie.CookieSpecFactory; +import org.apache.http.cookie.CookieSpecProvider; import org.apache.http.cookie.MalformedCookieException; import org.apache.http.cookie.SetCookie; -import org.apache.http.cookie.params.CookieSpecPNames; import org.apache.http.entity.ContentType; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.entity.mime.content.InputStreamBody; -import org.apache.http.impl.client.AbstractHttpClient; -import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.impl.client.DefaultRedirectStrategy; -import org.apache.http.impl.conn.PoolingClientConnectionManager; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.cookie.BasicClientCookie; import org.apache.http.impl.cookie.BasicPathHandler; +import org.apache.http.impl.cookie.BestMatchSpecFactory; import org.apache.http.impl.cookie.BrowserCompatSpec; +import org.apache.http.impl.cookie.BrowserCompatSpecFactory; +import org.apache.http.impl.cookie.IgnoreSpecFactory; +import org.apache.http.impl.cookie.NetscapeDraftSpecFactory; +import org.apache.http.impl.cookie.RFC2109SpecFactory; +import org.apache.http.impl.cookie.RFC2965SpecFactory; import org.apache.http.message.BasicHeader; -import org.apache.http.params.BasicHttpParams; -import org.apache.http.params.CoreConnectionPNames; -import org.apache.http.params.HttpParams; -import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; import com.gargoylesoftware.htmlunit.util.KeyDataPair; @@ -105,6 +103,8 @@ /** * Default implementation of {@link WebConnection}, using the HttpClient library to perform HTTP requests. + * This is meant to use the new API in HttpClient 4.3, and should be renamed to HttpWebConnection, + * after the old code is removed. * * @version $Revision$ * @author <a href="mailto:mb...@Ga...">Mike Bowler</a> @@ -120,13 +120,13 @@ public class HttpWebConnection implements WebConnection { private static final String HACKED_COOKIE_POLICY = "mine"; - private AbstractHttpClient httpClient_; + private HttpClientBuilder httpClientBuilder_; private final WebClient webClient_; /** Use single HttpContext, so there is no need to re-send authentication for each and every request. */ - private HttpContext httpContext_ = new BasicHttpContext(); + private HttpContext httpContext_ = new HttpClientContext(); private String virtualHost_; - private final CookieSpecFactory htmlUnitCookieSpecFactory_; + private final CookieSpecProvider htmlUnitCookieSpecProvider_; private final WebClientOptions usedOptions_ = new WebClientOptions(); /** @@ -135,8 +135,9 @@ */ public HttpWebConnection(final WebClient webClient) { webClient_ = webClient; - htmlUnitCookieSpecFactory_ = new CookieSpecFactory() { - public CookieSpec newInstance(final HttpParams params) { + htmlUnitCookieSpecProvider_ = new CookieSpecProvider() { + @Override + public CookieSpec create(final HttpContext context) { return new HtmlUnitBrowserCompatCookieSpec(webClient_.getIncorrectnessListener()); } }; @@ -147,7 +148,7 @@ */ public WebResponse getResponse(final WebRequest request) throws IOException { final URL url = request.getUrl(); - final AbstractHttpClient httpClient = getHttpClient(); + final HttpClientBuilder builder = reconfigureHttpClientIfNeeded(getHttpClientBuilder()); HttpUriRequest httpMethod = null; try { @@ -159,18 +160,19 @@ + " (reason: " + e.getMessage() + ")", e); } final HttpHost hostConfiguration = getHostConfiguration(request); - setProxy(httpMethod, request); +// setProxy(httpMethod, request); final long startTime = System.currentTimeMillis(); HttpResponse httpResponse = null; try { - httpResponse = httpClient.execute(hostConfiguration, httpMethod, httpContext_); + httpResponse = builder.build().execute(hostConfiguration, httpMethod, httpContext_); } catch (final SSLPeerUnverifiedException s) { // Try to use only SSLv3 instead if (webClient_.getOptions().isUseInsecureSSL()) { - HtmlUnitSSLSocketFactory.setUseSSL3Only(getHttpClient().getParams(), true); - httpResponse = httpClient.execute(hostConfiguration, httpMethod); + // TODO: asashour + // HtmlUnitSSLSocketFactory.setUseSSL3Only(getHttpClient().getParams(), true); + httpResponse = builder.build().execute(hostConfiguration, httpMethod); } else { throw s; @@ -182,7 +184,7 @@ // come out of connections and throw a ConnectionPoolTimeoutException. // => best solution, discard the HttpClient instance. synchronized (this) { - httpClient_ = null; + httpClientBuilder_ = null; } throw e; } @@ -218,17 +220,39 @@ return hostConfiguration; } - private static void setProxy(final HttpUriRequest httpUriRequest, final WebRequest webRequest) { +// private static void setProxy(final HttpUriRequest httpUriRequest, final WebRequest webRequest) { +// if (webRequest.getProxyHost() != null) { +// final HttpHost proxy = new HttpHost(webRequest.getProxyHost(), webRequest.getProxyPort()); +// final HttpParams httpRequestParams = httpUriRequest.getParams(); +// if (webRequest.isSocksProxy()) { +// SocksSocketFactory.setSocksProxy(httpRequestParams, proxy); +// } +// else { +// httpRequestParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); +// } +// } +// } + + private void setProxy(final HttpRequestBase httpRequest, final WebRequest webRequest) { + RequestConfig.Builder requestBuilder = createRequestConfig(); + configureTimeout(requestBuilder, webClient_.getOptions().getTimeout()); + if (webRequest.getProxyHost() != null) { final HttpHost proxy = new HttpHost(webRequest.getProxyHost(), webRequest.getProxyPort()); - final HttpParams httpRequestParams = httpUriRequest.getParams(); - if (webRequest.isSocksProxy()) { - SocksSocketFactory.setSocksProxy(httpRequestParams, proxy); - } - else { - httpRequestParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); - } + requestBuilder.setProxy(proxy); + httpRequest.setConfig(requestBuilder.build()); + +// if (webRequest.isSocksProxy()) { +// SocksSocketFactory.setSocksProxy(httpRequestParams, proxy); +// } +// else { +// httpRequestParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy); +// } } + else { + requestBuilder.setProxy(null); + httpRequest.setConfig(requestBuilder.build()); + } } /** @@ -254,6 +278,7 @@ URI uri = URIUtils.createURI(url.getProtocol(), url.getHost(), url.getPort(), url.getPath(), escapeQuery(url.getQuery()), null); final HttpRequestBase httpMethod = buildHttpMethod(webRequest.getHttpMethod(), uri); + setProxy(httpMethod, webRequest); if (!(httpMethod instanceof HttpEntityEnclosingRequest)) { // this is the case for GET as well as TRACE, DELETE, OPTIONS and HEAD if (!webRequest.getRequestParameters().isEmpty()) { @@ -294,7 +319,8 @@ buildFilePart((KeyDataPair) pair, builder); } else { - builder.addTextBody(pair.getName(), pair.getValue()); + builder.addTextBody(pair.getName(), pair.getValue(), + ContentType.create("text/plain", webRequest.getCharset())); } } method.setEntity(builder.build()); @@ -325,10 +351,8 @@ writeRequestHeadersToHttpMethod(httpMethod, webRequest.getAdditionalHeaders()); // getHttpClient().getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, true); - final AbstractHttpClient httpClient = getHttpClient(); + final HttpClientBuilder httpClient = getHttpClientBuilder(); - reconfigureHttpClientIfNeeded(httpClient); - // Tell the client where to get its credentials from // (it may have changed on the webClient since last call to getHttpClientFor(...)) final CredentialsProvider credentialsProvider = webClient_.getCredentialsProvider(); @@ -341,6 +365,7 @@ final AuthScope authScope = new AuthScope(requestUrl.getHost(), requestUrl.getPort()); // updating our client to keep the credentials for the next request credentialsProvider.setCredentials(authScope, requestUrlCredentials); + httpContext_.removeAttribute(HttpClientContext.TARGET_AUTH_STATE); } // if someone has set credentials to this request, we have to add this @@ -350,19 +375,22 @@ final AuthScope authScope = new AuthScope(requestUrl.getHost(), requestUrl.getPort()); // updating our client to keep the credentials for the next request credentialsProvider.setCredentials(authScope, requestCredentials); + httpContext_.removeAttribute(HttpClientContext.TARGET_AUTH_STATE); } - httpClient.setCredentialsProvider(credentialsProvider); + httpContext_.removeAttribute(HttpClientContext.CREDS_PROVIDER); + httpClient.setDefaultCredentialsProvider(credentialsProvider); + httpContext_.removeAttribute(HttpClientContext.COOKIE_STORE); if (webClient_.getCookieManager().isCookiesEnabled()) { // Cookies are enabled. Note that it's important that we enable single cookie headers, // for compatibility purposes. - httpClient.getParams().setParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, Boolean.TRUE); - httpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, HACKED_COOKIE_POLICY); - httpClient.setCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager())); + // TODO: asashour +// httpClient.getParams().setParameter(CookieSpecPNames.SINGLE_COOKIE_HEADER, Boolean.TRUE); + httpClient.setDefaultCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager())); } else { // Cookies are disabled. - httpClient.setCookieStore(new CookieStore() { + httpClient.setDefaultCookieStore(new CookieStore() { public void addCookie(final Cookie cookie) { /* empty */ } public void clear() { /* empty */ } public boolean clearExpired(final Date date) { @@ -501,16 +529,27 @@ * * @return the initialized HTTP client */ - protected synchronized AbstractHttpClient getHttpClient() { - if (httpClient_ == null) { - httpClient_ = createHttpClient(); + protected synchronized HttpClientBuilder getHttpClientBuilder() { + if (httpClientBuilder_ == null) { + httpClientBuilder_ = createHttpClient(); // this factory is required later // to be sure this is done, we do it outside the createHttpClient() call - httpClient_.getCookieSpecs().register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecFactory_); + final RegistryBuilder<CookieSpecProvider> registeryBuilder + = RegistryBuilder.<CookieSpecProvider>create() + .register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory()) + .register(CookieSpecs.STANDARD, new RFC2965SpecFactory()) + .register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory()) + .register(CookieSpecs.NETSCAPE, new NetscapeDraftSpecFactory()) + .register(CookieSpecs.IGNORE_COOKIES, new IgnoreSpecFactory()) + .register("rfc2109", new RFC2109SpecFactory()) + .register("rfc2965", new RFC2965SpecFactory()); + + registeryBuilder.register(HACKED_COOKIE_POLICY, htmlUnitCookieSpecProvider_); + httpClientBuilder_.setDefaultCookieSpecRegistry(registeryBuilder.build()); } - return httpClient_; + return httpClientBuilder_; } /** @@ -531,25 +570,40 @@ * some tracking; see feature request 1438216). * @return the <tt>HttpClient</tt> that will be used by this WebConnection */ - protected AbstractHttpClient createHttpClient() { - final HttpParams httpParams = new BasicHttpParams(); + protected HttpClientBuilder createHttpClient() { + // TODO: asashour +// final HttpParams httpParams = new BasicHttpParams(); +// +// HttpClientParams.setRedirecting(httpParams, false); +// // Set timeouts +// configureTimeout(httpParams, webClient_.getOptions().getTimeout()); +// +// final SchemeRegistry schemeRegistry = new SchemeRegistry(); +// schemeRegistry.register(new Scheme("http", 80, new SocksSocketFactory())); +// configureHttpsScheme(schemeRegistry); +// +// final PoolingClientConnectionManager connectionManager = +// new PoolingClientConnectionManager(schemeRegistry); +// connectionManager.setDefaultMaxPerRoute(6); +// +// final DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager, httpParams); +// httpClient.setCookieStore(new HtmlUnitCookieStore2(webClient_.getCookieManager())); +// +// httpClient.setRedirectStrategy(new DefaultRedirectStrategy() { +// @Override +// public boolean isRedirected(final HttpRequest request, final HttpResponse response, +// final HttpContext context) throws ProtocolException { +// return super.isRedirected(request, response, context) +// && response.getFirstHeader("location") != null; +// } +// }); +// +// if (getVirtualHost() != null) { +// httpClient.getParams().setParameter(ClientPNames.VIRTUAL_HOST, virtualHost_); +// } - HttpClientParams.setRedirecting(httpParams, false); - // Set timeouts - configureTimeout(httpParams, webClient_.getOptions().getTimeout()); - - final SchemeRegistry schemeRegistry = new SchemeRegistry(); - schemeRegistry.register(new Scheme("http", 80, new SocksSocketFactory())); - configureHttpsScheme(schemeRegistry); - - final PoolingClientConnectionManager connectionManager = - new PoolingClientConnectionManager(schemeRegistry); - connectionManager.setDefaultMaxPerRoute(6); - - final DefaultHttpClient httpClient = new DefaultHttpClient(connectionManager, httpParams); - httpClient.setCookieStore(new HtmlUnitCookieStore(webClient_.getCookieManager())); - - httpClient.setRedirectStrategy(new DefaultRedirectStrategy() { + final HttpClientBuilder builder = HttpClientBuilder.create(); + builder.setRedirectStrategy(new DefaultRedirectStrategy() { @Override public boolean isRedirected(final HttpRequest request, final HttpResponse response, final HttpContext context) throws ProtocolException { @@ -557,45 +611,57 @@ && response.getFirstHeader("location") != null; } }); + configureTimeout(builder, webClient_.getOptions().getTimeout()); + return builder; + } - if (getVirtualHost() != null) { - httpClient.getParams().setParameter(ClientPNames.VIRTUAL_HOST, virtualHost_); - } + private void configureTimeout(final HttpClientBuilder builder, final int timeout) { + RequestConfig.Builder requestBuilder = createRequestConfig(); + configureTimeout(requestBuilder, timeout); - return httpClient; + builder.setDefaultRequestConfig(requestBuilder.build()); + httpContext_.removeAttribute(HttpClientContext.REQUEST_CONFIG); + usedOptions_.setTimeout(timeout); } - private void configureTimeout(final HttpParams httpParams, final int timeout) { - httpParams.setParameter(CoreConnectionPNames.SO_TIMEOUT, timeout); - httpParams.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, timeout); + private RequestConfig.Builder createRequestConfig() { + RequestConfig.Builder requestBuilder = RequestConfig.custom() + .setCookieSpec(HACKED_COOKIE_POLICY) + .setRedirectsEnabled(false); + return requestBuilder; + } - usedOptions_.setTimeout(timeout); + private void configureTimeout(final RequestConfig.Builder builder, final int timeout) { + builder.setConnectTimeout(timeout) + .setConnectionRequestTimeout(timeout) + .setSocketTimeout(timeout); } /** * React on changes that may have occurred on the WebClient settings. * Registering as a listener would be probably better. */ - private void reconfigureHttpClientIfNeeded(final AbstractHttpClient httpClient) { + private HttpClientBuilder reconfigureHttpClientIfNeeded(final HttpClientBuilder httpClientBuilder) { final WebClientOptions options = webClient_.getOptions(); // register new SSL factory only if settings have changed if (options.isUseInsecureSSL() != usedOptions_.isUseInsecureSSL() || options.getSSLClientCertificateUrl() != usedOptions_.getSSLClientCertificateUrl()) { - configureHttpsScheme(httpClient.getConnectionManager().getSchemeRegistry()); + configureHttpsScheme(httpClientBuilder); } if (options.getTimeout() != usedOptions_.getTimeout()) { - configureTimeout(httpClient.getParams(), options.getTimeout()); + configureTimeout(httpClientBuilder, options.getTimeout()); } + return httpClientBuilder; } - private void configureHttpsScheme(final SchemeRegistry schemeRegistry) { + private void configureHttpsScheme(final HttpClientBuilder builder) { final WebClientOptions options = webClient_.getOptions(); - final SSLSocketFactory socketFactory = HtmlUnitSSLSocketFactory.buildSSLSocketFactory(options); + final SSLConnectionSocketFactory socketFactory = HtmlUnitSSLConnectionSocketFactory.buildSSLSocketFactory(options); - schemeRegistry.register(new Scheme("https", 443, socketFactory)); + builder.setSSLSocketFactory(socketFactory); usedOptions_.setUseInsecureSSL(options.isUseInsecureSSL()); usedOptions_.setSSLClientCertificate(options.getSSLClientCertificateUrl(), @@ -609,7 +675,8 @@ public void setVirtualHost(final String virtualHost) { virtualHost_ = virtualHost; if (virtualHost_ != null) { - getHttpClient().getParams().setParameter(ClientPNames.VIRTUAL_HOST, virtualHost_); + // TODO: asashour + //getHttpClient().getParams().setParameter(ClientPNames.VIRTUAL_HOST, virtualHost_); } } @@ -625,7 +692,7 @@ * Converts an HttpMethod into a WebResponse. */ private WebResponse makeWebResponse(final HttpResponse httpResponse, - final WebRequest request, final DownloadedContent downloadedContent, final long loadTime) { + final WebRequest request, final DownloadedContent responseBody, final long loadTime) { String statusMessage = httpResponse.getStatusLine().getReasonPhrase(); if (statusMessage == null) { @@ -636,7 +703,7 @@ for (final Header header : httpResponse.getAllHeaders()) { headers.add(new NameValuePair(header.getName(), header.getValue())); } - final WebResponseData responseData = new WebResponseData(downloadedContent, statusCode, statusMessage, headers); + final WebResponseData responseData = new WebResponseData(responseBody, statusCode, statusMessage, headers); return newWebResponseInstance(responseData, loadTime, request); } @@ -721,11 +788,17 @@ * Shutdown the connection manager. */ public synchronized void shutdown() { - if (httpClient_ != null) { - httpClient_.getConnectionManager().shutdown(); - httpClient_ = null; + if (httpClientBuilder_ != null) { + // TODO: asashour + //httpClientBuilder_.getConnectionManager().shutdown(); + httpClientBuilder_ = null; } } + + //TODO: should we really do this? +// public void clearCredentials() { +// httpContext_.removeAttribute(HttpClientContext.TARGET_AUTH_STATE); +// } } /** @@ -876,4 +949,5 @@ public synchronized void clear() { manager_.clearCookies(); } + } Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/SocksSocketFactory.java =================================================================== --- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/SocksSocketFactory.java 2014-01-22 20:18:44 UTC (rev 9036) +++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/SocksSocketFactory.java 2014-01-23 01:19:49 UTC (rev 9037) @@ -21,6 +21,7 @@ import org.apache.http.HttpHost; import org.apache.http.conn.scheme.PlainSocketFactory; import org.apache.http.params.HttpParams; +import org.apache.http.protocol.HttpContext; /** * SOCKS aware {@link org.apache.http.conn.scheme.SchemeSocketFactory}. @@ -41,6 +42,10 @@ return (HttpHost) parameters.getParameter(SOCKS_PROXY); } + static HttpHost getSocksProxy(final HttpContext context) { + return (HttpHost) context.getAttribute(SOCKS_PROXY); + } + static Socket createSocketWithSocksProxy(final HttpHost socksProxy) { final InetSocketAddress address = new InetSocketAddress(socksProxy.getHostName(), socksProxy.getPort()); final Proxy proxy = new Proxy(Proxy.Type.SOCKS, address); Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/HttpWebConnectionTest.java =================================================================== --- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/HttpWebConnectionTest.java 2014-01-22 20:18:44 UTC (rev 9036) +++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/HttpWebConnectionTest.java 2014-01-23 01:19:49 UTC (rev 9037) @@ -44,6 +44,7 @@ import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.AbstractHttpClient; import org.apache.http.impl.client.DefaultHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; import org.apache.log4j.Level; @@ -244,9 +245,9 @@ final boolean[] tabCalled = {false}; final WebConnection myWebConnection = new HttpWebConnection(webClient) { @Override - protected AbstractHttpClient createHttpClient() { + protected HttpClientBuilder createHttpClient() { tabCalled[0] = true; - return new DefaultHttpClient(); + return HttpClientBuilder.create(); } }; |