From 351801ee0b1187a5468fbb85e9e27463fbb0e746 Mon Sep 17 00:00:00 2001 From: Chris Hegarty Date: Wed, 16 Apr 2008 14:17:54 +0100 Subject: [PATCH] 6687282: URLConnection for HTTPS connection through Proxy w/ Digest Authentication gives 400 Bad Request Change http/digest implementation to use host:port from CONNECT request Reviewed-by: michaelm --- .../protocol/http/DigestAuthentication.java | 44 ++++++++++- .../www/protocol/http/HttpURLConnection.java | 78 ++++++++++++++++--- 2 files changed, 110 insertions(+), 12 deletions(-) diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java b/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java index 26a852eb87b..472bf9a9fbe 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java @@ -36,6 +36,7 @@ import java.util.Random; import sun.net.www.HeaderParser; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import static sun.net.www.protocol.http.HttpURLConnection.HTTP_CONNECT; /** @@ -210,10 +211,38 @@ class DigestAuthentication extends AuthenticationInfo { /** * Reclaculates the request-digest and returns it. + * + *

Used in the common case where the requestURI is simply the + * abs_path. + * + * @param url + * the URL + * + * @param method + * the HTTP method + * * @return the value of the HTTP header this authentication wants set */ String getHeaderValue(URL url, String method) { - return getHeaderValueImpl (url.getFile(), method); + return getHeaderValueImpl(url.getFile(), method); + } + + /** + * Reclaculates the request-digest and returns it. + * + *

Used when the requestURI is not the abs_path. The exact + * requestURI can be passed as a String. + * + * @param requestURI + * the Request-URI from the HTTP request line + * + * @param method + * the HTTP method + * + * @return the value of the HTTP header this authentication wants set + */ + String getHeaderValue(String requestURI, String method) { + return getHeaderValueImpl(requestURI, method); } /** @@ -249,7 +278,16 @@ class DigestAuthentication extends AuthenticationInfo { params.setOpaque (p.findValue("opaque")); params.setQop (p.findValue("qop")); - String uri = conn.getURL().getFile(); + String uri; + String method; + if (type == PROXY_AUTHENTICATION && + conn.tunnelState() == HttpURLConnection.TunnelState.SETUP) { + uri = HttpURLConnection.connectRequestURI(conn.getURL()); + method = HTTP_CONNECT; + } else { + uri = conn.getURL().getFile(); + method = conn.getMethod(); + } if (params.nonce == null || authMethod == null || pw == null || realm == null) { return false; @@ -275,7 +313,7 @@ class DigestAuthentication extends AuthenticationInfo { params.setNewCnonce(); } - String value = getHeaderValueImpl (uri, conn.getMethod()); + String value = getHeaderValueImpl (uri, method); if (value != null) { conn.setAuthenticationProperty(getHeaderName(), value); return true; diff --git a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java index e9e9e762b32..0f7ce9a939c 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java +++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java @@ -75,6 +75,8 @@ public class HttpURLConnection extends java.net.HttpURLConnection { private static Logger logger = Logger.getLogger("sun.net.www.protocol.http.HttpURLConnection"); + static String HTTP_CONNECT = "CONNECT"; + static final String version; public static final String userAgent; @@ -266,6 +268,20 @@ public class HttpURLConnection extends java.net.HttpURLConnection { /* If we decide we want to reuse a client, we put it here */ private HttpClient reuseClient = null; + /* Tunnel states */ + enum TunnelState { + /* No tunnel */ + NONE, + + /* Setting up a tunnel */ + SETUP, + + /* Tunnel has been successfully setup */ + TUNNELING + } + + private TunnelState tunnelState = TunnelState.NONE; + /* Redefine timeouts from java.net.URLConnection as we nee -1 to mean * not set. This is to ensure backward compatibility. */ @@ -338,7 +354,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * others that have been set */ // send any pre-emptive authentication - if (http.usingProxy) { + if (http.usingProxy && tunnelState() != TunnelState.TUNNELING) { setPreemptiveProxyAuthentication(requests); } if (!setRequests) { @@ -1404,11 +1420,17 @@ public class HttpURLConnection extends java.net.HttpURLConnection { String raw = auth.raw(); if (proxyAuthentication.isAuthorizationStale (raw)) { /* we can retry with the current credentials */ - requests.set (proxyAuthentication.getHeaderName(), - proxyAuthentication.getHeaderValue( - url, method)); + String value; + if (tunnelState() == TunnelState.SETUP && + proxyAuthentication instanceof DigestAuthentication) { + value = ((DigestAuthentication)proxyAuthentication) + .getHeaderValue(connectRequestURI(url), HTTP_CONNECT); + } else { + value = proxyAuthentication.getHeaderValue(url, method); + } + requests.set(proxyAuthentication.getHeaderName(), value); currentProxyCredentials = proxyAuthentication; - return proxyAuthentication; + return proxyAuthentication; } else { proxyAuthentication.removeFromCache(); } @@ -1418,6 +1440,24 @@ public class HttpURLConnection extends java.net.HttpURLConnection { return proxyAuthentication; } + /** + * Returns the tunnel state. + * + * @return the state + */ + TunnelState tunnelState() { + return tunnelState; + } + + /** + * Set the tunneling status. + * + * @param the state + */ + void setTunnelState(TunnelState tunnelState) { + this.tunnelState = tunnelState; + } + /** * establish a tunnel through proxy server */ @@ -1437,6 +1477,9 @@ public class HttpURLConnection extends java.net.HttpURLConnection { boolean inNegotiateProxy = false; try { + /* Actively setting up a tunnel */ + setTunnelState(TunnelState.SETUP); + do { if (!checkReuseConnection()) { proxiedConnect(url, proxyHost, proxyPort, false); @@ -1512,11 +1555,13 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } if (respCode == HTTP_OK) { + setTunnelState(TunnelState.TUNNELING); break; } // we don't know how to deal with other response code // so disconnect and report error disconnectInternal(); + setTunnelState(TunnelState.NONE); break; } while (retryTunnel < maxRedirects); @@ -1538,6 +1583,14 @@ public class HttpURLConnection extends java.net.HttpURLConnection { responses.reset(); } + static String connectRequestURI(URL url) { + String host = url.getHost(); + int port = url.getPort(); + port = port != -1 ? port : url.getDefaultPort(); + + return host + ":" + port; + } + /** * send a CONNECT request for establishing a tunnel to proxy server */ @@ -1551,8 +1604,7 @@ public class HttpURLConnection extends java.net.HttpURLConnection { // otherwise, there may have 2 http methods in headers if (setRequests) requests.set(0, null, null); - requests.prepend("CONNECT " + url.getHost() + ":" - + (port != -1 ? port : url.getDefaultPort()) + requests.prepend(HTTP_CONNECT + " " + connectRequestURI(url) + " " + httpVersion, null); requests.setIfNotSet("User-Agent", userAgent); @@ -1583,9 +1635,17 @@ public class HttpURLConnection extends java.net.HttpURLConnection { = AuthenticationInfo.getProxyAuth(http.getProxyHostUsed(), http.getProxyPortUsed()); if (pauth != null && pauth.supportsPreemptiveAuthorization()) { + String value; + if (tunnelState() == TunnelState.SETUP && + pauth instanceof DigestAuthentication) { + value = ((DigestAuthentication)pauth) + .getHeaderValue(connectRequestURI(url), HTTP_CONNECT); + } else { + value = pauth.getHeaderValue(url, method); + } + // Sets "Proxy-authorization" - requests.set(pauth.getHeaderName(), - pauth.getHeaderValue(url,method)); + requests.set(pauth.getHeaderName(), value); currentProxyCredentials = pauth; } }