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
--- 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.
+ *
+ * <P> 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.
+ *
+ * <P> 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;
--- 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;
}
}