# HG changeset patch # User chegar # Date 1208351874 -3600 # Node ID c6ddcfc7ff4dfd73d9e10d3f508a5cc6f600f9da # Parent 5adf3e3f1ff315ae72a4a8b8756bf62ad1abf25d 6687282: URLConnection for HTTPS connection through Proxy w/ Digest Authentication gives 400 Bad Request Summary: Change http/digest implementation to use host:port from CONNECT request Reviewed-by: michaelm diff -r 5adf3e3f1ff3 -r c6ddcfc7ff4d jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java --- a/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java Tue Apr 15 14:22:36 2008 +0100 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/DigestAuthentication.java Wed Apr 16 14:17:54 2008 +0100 @@ -36,6 +36,7 @@ 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 @@ /** * 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 @@ 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 @@ params.setNewCnonce(); } - String value = getHeaderValueImpl (uri, conn.getMethod()); + String value = getHeaderValueImpl (uri, method); if (value != null) { conn.setAuthenticationProperty(getHeaderName(), value); return true; diff -r 5adf3e3f1ff3 -r c6ddcfc7ff4d jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java --- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Tue Apr 15 14:22:36 2008 +0100 +++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java Wed Apr 16 14:17:54 2008 +0100 @@ -75,6 +75,8 @@ 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 @@ /* 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 @@ * 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 @@ 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(); } @@ -1419,6 +1441,24 @@ } /** + * 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 */ public synchronized void doTunneling() throws IOException { @@ -1437,6 +1477,9 @@ boolean inNegotiateProxy = false; try { + /* Actively setting up a tunnel */ + setTunnelState(TunnelState.SETUP); + do { if (!checkReuseConnection()) { proxiedConnect(url, proxyHost, proxyPort, false); @@ -1512,11 +1555,13 @@ } 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 @@ 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 @@ // 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 @@ = 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; } }