6672144: HttpURLConnection.getInputStream sends POST request after failed chunked
authorchegar
Tue, 21 Sep 2010 15:58:06 +0100
changeset 6673 3674dbc66612
parent 6672 f01ef94a63e7
child 6674 2b22e69fdb75
6672144: HttpURLConnection.getInputStream sends POST request after failed chunked Reviewed-by: michaelm
jdk/src/share/classes/sun/net/www/http/HttpClient.java
jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
jdk/test/sun/net/www/http/HttpClient/StreamingRetry.java
--- a/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Mon Sep 20 18:05:09 2010 -0700
+++ b/jdk/src/share/classes/sun/net/www/http/HttpClient.java	Tue Sep 21 15:58:06 2010 +0100
@@ -55,6 +55,9 @@
     // Http data we send with the headers
     PosterOutputStream poster = null;
 
+    // true if we are in streaming mode (fixed length or chunked)
+    boolean streaming;
+
     // if we've had one io error
     boolean failedOnce = false;
 
@@ -275,6 +278,10 @@
                         ret.cachedHttpClient = true;
                         assert ret.inCache;
                         ret.inCache = false;
+                        PlatformLogger logger = HttpURLConnection.getHttpLogger();
+                        if (logger.isLoggable(PlatformLogger.FINEST)) {
+                            logger.finest("KeepAlive stream retrieved from the cache, " + ret);
+                        }
                     }
                 } else {
                     // We cannot return this connection to the cache as it's
@@ -545,6 +552,13 @@
         serverOutput.flush();
     }
 
+    public void writeRequests(MessageHeader head,
+                              PosterOutputStream pos,
+                              boolean streaming) throws IOException {
+        this.streaming = streaming;
+        writeRequests(head, pos);
+    }
+
     /** Parse the first line of the HTTP request.  It usually looks
         something like: "HTTP/1.0 <number> comment\r\n". */
 
@@ -577,11 +591,11 @@
             closeServer();
             cachedHttpClient = false;
             if (!failedOnce && requests != null) {
-                if (httpuc.getRequestMethod().equals("POST") && !retryPostProp) {
+                failedOnce = true;
+                if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
                     // do not retry the request
                 }  else {
                     // try once more
-                    failedOnce = true;
                     openServer();
                     if (needsTunneling()) {
                         httpuc.doTunneling();
@@ -684,10 +698,10 @@
                 }
             } else if (nread != 8) {
                 if (!failedOnce && requests != null) {
-                    if (httpuc.getRequestMethod().equals("POST") && !retryPostProp) {
+                    failedOnce = true;
+                    if (httpuc.getRequestMethod().equals("POST") && (!retryPostProp || streaming)) {
                         // do not retry the request
                     } else {
-                        failedOnce = true;
                         closeServer();
                         cachedHttpClient = false;
                         openServer();
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Mon Sep 20 18:05:09 2010 -0700
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Tue Sep 21 15:58:06 2010 +0100
@@ -494,7 +494,7 @@
         if (logger.isLoggable(PlatformLogger.FINE)) {
             logger.fine(requests.toString());
         }
-        http.writeRequests(requests, poster);
+        http.writeRequests(requests, poster, streaming());
         if (ps.checkError()) {
             String proxyHost = http.getProxyHostUsed();
             int proxyPort = http.getProxyPortUsed();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/http/HttpClient/StreamingRetry.java	Tue Sep 21 15:58:06 2010 +0100
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2010, 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 6672144
+ * @summary HttpURLConnection.getInputStream sends POST request after failed chunked send
+ */
+
+import java.net.HttpURLConnection;
+import java.net.ServerSocket;
+import java.net.URL;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class StreamingRetry implements Runnable {
+    static final int ACCEPT_TIMEOUT = 20 * 1000; // 20 seconds
+    ServerSocket ss;
+
+    public static void main(String[] args) throws IOException {
+        (new StreamingRetry()).instanceMain();
+    }
+
+    void instanceMain() throws IOException {
+        test();
+        if (failed > 0) throw new RuntimeException("Some tests failed");
+    }
+
+    void test() throws IOException {
+        ss = new ServerSocket(0);
+        ss.setSoTimeout(ACCEPT_TIMEOUT);
+        int port = ss.getLocalPort();
+
+        (new Thread(this)).start();
+
+        try {
+            URL url = new URL("http://localhost:" + port + "/");
+            HttpURLConnection uc = (HttpURLConnection) url.openConnection();
+            uc.setDoOutput(true);
+            uc.setChunkedStreamingMode(4096);
+            OutputStream os = uc.getOutputStream();
+            os.write("Hello there".getBytes());
+
+            InputStream is = uc.getInputStream();
+            is.close();
+        } catch (IOException expected) {
+            //expected.printStackTrace();
+        } finally {
+            ss.close();
+        }
+    }
+
+    // Server
+    public void run() {
+        try {
+            (ss.accept()).close();
+            (ss.accept()).close();
+            ss.close();
+            fail("The server shouldn't accept a second connection");
+         } catch (IOException e) {
+            //OK, the clien will close the server socket if successfull
+        }
+    }
+
+    volatile int failed = 0;
+    void fail() {failed++; Thread.dumpStack();}
+    void fail(String msg) {System.err.println(msg); fail();}
+}