8011719: Properties.loadFromXML fails with a chunked HTTP connection
authorchegar
Wed, 05 Jun 2013 16:06:28 +0100
changeset 17952 a5f2fb0e7457
parent 17951 2734e5252a38
child 17953 9a56976d1ab2
8011719: Properties.loadFromXML fails with a chunked HTTP connection Reviewed-by: michaelm
jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java
jdk/test/sun/net/www/protocol/http/HttpStreams.java
--- a/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Wed Jun 05 18:20:34 2013 +0400
+++ b/jdk/src/share/classes/sun/net/www/protocol/http/HttpURLConnection.java	Wed Jun 05 16:06:28 2013 +0100
@@ -3158,6 +3158,7 @@
         private boolean marked = false;
         private int inCache = 0;
         private int markCount = 0;
+        private boolean closed;  // false
 
         public HttpInputStream (InputStream is) {
             super (is);
@@ -3233,8 +3234,14 @@
             }
         }
 
+        private void ensureOpen() throws IOException {
+            if (closed)
+                throw new IOException("stream is closed");
+        }
+
         @Override
         public int read() throws IOException {
+            ensureOpen();
             try {
                 byte[] b = new byte[1];
                 int ret = read(b);
@@ -3254,6 +3261,7 @@
 
         @Override
         public int read(byte[] b, int off, int len) throws IOException {
+            ensureOpen();
             try {
                 int newLen = super.read(b, off, len);
                 int nWrite;
@@ -3291,7 +3299,7 @@
 
         @Override
         public long skip (long n) throws IOException {
-
+            ensureOpen();
             long remaining = n;
             int nr;
             if (skipBuffer == null)
@@ -3317,6 +3325,9 @@
 
         @Override
         public void close () throws IOException {
+            if (closed)
+                return;
+
             try {
                 if (outputStream != null) {
                     if (read() != -1) {
@@ -3332,6 +3343,7 @@
                 }
                 throw ioex;
             } finally {
+                closed = true;
                 HttpURLConnection.this.http = null;
                 checkResponseCredentials (true);
             }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/net/www/protocol/http/HttpStreams.java	Wed Jun 05 16:06:28 2013 +0100
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2013, 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 8011719
+ * @summary Basic checks to verify behavior of returned input streams
+ */
+
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+import com.sun.net.httpserver.HttpServer;
+import java.io.*;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+public class HttpStreams {
+
+    void client(String u) throws Exception {
+        byte[] ba = new byte[5];
+        HttpURLConnection urlc = (HttpURLConnection)(new URL(u)).openConnection();
+        int resp = urlc.getResponseCode();
+        InputStream is;
+        if (resp == 200)
+            is = urlc.getInputStream();
+        else
+            is = urlc.getErrorStream();
+
+        expectNoThrow(() -> { is.read(); }, "read on open stream should not throw :" + u);
+        expectNoThrow(() -> { is.close(); }, "close should never throw: " + u);
+        expectNoThrow(() -> { is.close(); }, "close should never throw: " + u);
+        expectThrow(() -> { is.read(); }, "read on closed stream should throw: " + u);
+        expectThrow(() -> { is.read(ba); }, "read on closed stream should throw: " + u);
+        expectThrow(() -> { is.read(ba, 0, 2); }, "read on closed stream should throw: " + u);
+    }
+
+    void test() throws Exception {
+        HttpServer server = null;
+        try {
+            server = startHttpServer();
+            String baseUrl = "http://localhost:" + server.getAddress().getPort() + "/";
+            client(baseUrl +  "chunked/");
+            client(baseUrl +  "fixed/");
+            client(baseUrl +  "error/");
+            client(baseUrl +  "chunkedError/");
+
+            // Test with a response cache
+            ResponseCache ch = ResponseCache.getDefault();
+            ResponseCache.setDefault(new TrivialCacheHandler());
+            try {
+                client(baseUrl +  "chunked/");
+                client(baseUrl +  "fixed/");
+                client(baseUrl +  "error/");
+                client(baseUrl +  "chunkedError/");
+            } finally {
+                ResponseCache.setDefault(ch);
+            }
+        } finally {
+            if (server != null)
+                server.stop(0);
+        }
+
+        System.out.println("passed: " + pass + ", failed: " + fail);
+        if (fail > 0)
+            throw new RuntimeException("some tests failed check output");
+    }
+
+    public static void main(String[] args) throws Exception {
+        (new HttpStreams()).test();
+    }
+
+    // HTTP Server
+    HttpServer startHttpServer() throws IOException {
+        HttpServer httpServer = HttpServer.create(new InetSocketAddress(0), 0);
+        httpServer.createContext("/chunked/", new ChunkedHandler());
+        httpServer.createContext("/fixed/", new FixedHandler());
+        httpServer.createContext("/error/", new ErrorHandler());
+        httpServer.createContext("/chunkedError/", new ChunkedErrorHandler());
+        httpServer.start();
+        return httpServer;
+    }
+
+    static abstract class AbstractHandler implements HttpHandler {
+        @Override
+        public void handle(HttpExchange t) throws IOException {
+            try (InputStream is = t.getRequestBody()) {
+                while (is.read() != -1);
+            }
+            t.sendResponseHeaders(respCode(), length());
+            try (OutputStream os = t.getResponseBody()) {
+                os.write(message());
+            }
+            t.close();
+        }
+
+        abstract int respCode();
+        abstract int length();
+        abstract byte[] message();
+    }
+
+    static class ChunkedHandler extends AbstractHandler {
+        static final byte[] ba =
+                "Hello there from chunked handler!".getBytes(StandardCharsets.US_ASCII);
+        int respCode() { return 200; }
+        int length() { return 0; }
+        byte[] message() { return ba; }
+    }
+
+    static class FixedHandler extends AbstractHandler {
+        static final byte[] ba =
+                "Hello there from fixed handler!".getBytes(StandardCharsets.US_ASCII);
+        int respCode() { return 200; }
+        int length() { return ba.length; }
+        byte[] message() { return ba; }
+    }
+
+    static class ErrorHandler extends AbstractHandler {
+        static final byte[] ba =
+                "This is an error mesg from the server!".getBytes(StandardCharsets.US_ASCII);
+        int respCode() { return 400; }
+        int length() { return ba.length; }
+        byte[] message() { return ba; }
+    }
+
+    static class ChunkedErrorHandler extends ErrorHandler {
+        int length() { return 0; }
+    }
+
+    static class TrivialCacheHandler extends ResponseCache
+    {
+       public CacheResponse get(URI uri, String rqstMethod, Map rqstHeaders) {
+          return null;
+       }
+
+       public CacheRequest put(URI uri, URLConnection conn) {
+          return new TrivialCacheRequest();
+       }
+    }
+
+    static class TrivialCacheRequest extends CacheRequest
+    {
+       ByteArrayOutputStream baos = new ByteArrayOutputStream();
+       public void abort() {}
+       public OutputStream getBody() throws IOException { return baos; }
+    }
+
+    static interface ThrowableRunnable {
+        void run() throws IOException;
+    }
+
+    void expectThrow(ThrowableRunnable r, String msg) {
+        try { r.run(); fail(msg); } catch (IOException x) { pass(); }
+    }
+
+    void expectNoThrow(ThrowableRunnable r, String msg) {
+        try { r.run(); pass(); } catch (IOException x) { fail(msg, x); }
+    }
+
+    private int pass;
+    private int fail;
+    void pass() { pass++; }
+    void fail(String msg, Exception x) { System.out.println(msg); x.printStackTrace(); fail++; }
+    void fail(String msg) { System.out.println(msg); Thread.dumpStack(); fail++; }
+}