changeset 4364:85d7fc20baa5

6670868: StackOverFlow with authenticated Proxy tunnels Reviewed-by: chegar, coffeys
author robm
date Thu, 01 Sep 2011 00:09:34 +0100
parents eabbc04b83bc
children 791e94e7b64f
files src/share/classes/sun/net/www/http/HttpClient.java src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsProxyStackOverflow.java
diffstat 3 files changed, 132 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/net/www/http/HttpClient.java	Fri Aug 26 18:38:19 2011 -0700
+++ b/src/share/classes/sun/net/www/http/HttpClient.java	Thu Sep 01 00:09:34 2011 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -599,7 +599,9 @@
             cachedHttpClient = false;
             if (!failedOnce && requests != null) {
                 failedOnce = true;
-                if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
+                if (getRequestMethod().equals("CONNECT") ||
+                    (httpuc.getRequestMethod().equals("POST") &&
+                    (!retryPostProp || streaming))) {
                     // do not retry the request
                 }  else {
                     // try once more
@@ -706,7 +708,9 @@
             } else if (nread != 8) {
                 if (!failedOnce && requests != null) {
                     failedOnce = true;
-                    if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
+                    if (getRequestMethod().equals("CONNECT") ||
+                        (httpuc.getRequestMethod().equals("POST") &&
+                        (!retryPostProp || streaming))) {
                         // do not retry the request
                     } else {
                         closeServer();
@@ -891,6 +895,16 @@
         return cacheRequest;
     }
 
+    String getRequestMethod() {
+        if (requests != null) {
+            String requestLine = requests.getKey(0);
+            if (requestLine != null) {
+                return requestLine.split("\\s+")[0];
+            }
+        }
+        return "";
+    }
+
     @Override
     protected void finalize() throws Throwable {
         // This should do nothing.  The stream finalizer will
--- a/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Fri Aug 26 18:38:19 2011 -0700
+++ b/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Thu Sep 01 00:09:34 2011 +0100
@@ -1880,14 +1880,7 @@
     private void sendCONNECTRequest() throws IOException {
         int port = url.getPort();
 
-        // setRequests == true indicates the std. request headers
-        // have been set in (previous) requests.
-        // so the first one must be the http method (GET, etc.).
-        // we need to set it to CONNECT soon, remove this one first.
-        // otherwise, there may have 2 http methods in headers
-        if (setRequests) requests.set(0, null, null);
-
-        requests.prepend(HTTP_CONNECT + " " + connectRequestURI(url)
+        requests.set(0, HTTP_CONNECT + " " + connectRequestURI(url)
                          + " " + httpVersion, null);
         requests.setIfNotSet("User-Agent", userAgent);
 
@@ -1912,8 +1905,6 @@
         }
 
         http.writeRequests(requests, null);
-        // remove CONNECT header
-        requests.set(0, null, null);
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/HttpsProxyStackOverflow.java	Thu Sep 01 00:09:34 2011 +0100
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2011 Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 6670868
+ * @summary StackOverFlow with bad authenticated Proxy tunnels
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Authenticator;
+import java.net.Proxy;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+import javax.net.ssl.HttpsURLConnection;
+
+public class HttpsProxyStackOverflow {
+
+    public static void main(String[] args) throws IOException {
+        BadAuthProxyServer server = startServer();
+        doClient(server);
+    }
+
+    static void doClient(BadAuthProxyServer server) throws IOException {
+        // url doesn't matter since we will never make the connection
+        URL url = new URL("https://anythingwilldo/");
+        HttpsURLConnection conn = (HttpsURLConnection) url.openConnection(
+                      new Proxy(Proxy.Type.HTTP,
+                      new InetSocketAddress("localhost", server.getPort())));
+        try {
+            InputStream is = conn.getInputStream();
+        } catch(IOException unused) {
+            // no real server, IOException is expected.
+            // failure if StackOverflowError
+        } finally {
+            server.done();
+        }
+    }
+
+    static BadAuthProxyServer startServer() throws IOException {
+        Authenticator.setDefault(new Authenticator() {
+            @Override
+            protected PasswordAuthentication getPasswordAuthentication() {
+                return new PasswordAuthentication("xyz", "xyz".toCharArray());
+            }
+            });
+
+        BadAuthProxyServer server = new BadAuthProxyServer(new ServerSocket(0));
+        Thread serverThread = new Thread(server);
+        serverThread.start();
+        return server;
+    }
+
+    static class BadAuthProxyServer implements Runnable {
+        private ServerSocket ss;
+        private boolean done;
+
+        BadAuthProxyServer(ServerSocket ss) { this.ss = ss; }
+
+        public void run() {
+            try {
+               while (!done) {
+                    Socket s = ss.accept();
+                    s.getOutputStream().write(
+                            ("HTTP/1.1 407\nProxy-Authenticate:Basic " +
+                            "realm=\"WallyWorld\"\n\n").getBytes("US-ASCII"));
+
+                    s.close();
+
+                    s = ss.accept();
+                    s.close();
+                }
+            } catch (IOException e) {
+                // Ignore IOException when the main thread calls done
+            } finally {
+                try { ss.close(); } catch (IOException e) {}
+            }
+        }
+
+        int getPort() {
+            return ss.getLocalPort();
+        }
+
+        void done() {
+            try { ss.close(); } catch (IOException e) {}
+            done = true;
+        }
+    }
+}
+