jdk/test/java/net/URLConnection/DisconnectAfterEOF.java
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/net/URLConnection/DisconnectAfterEOF.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2002 Sun Microsystems, Inc.  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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/**
+ * @test
+ * @bug 4774503
+ * @summary Calling HttpURLConnection's disconnect method after the
+ *          response has been received causes havoc with persistent
+ *          connections.
+ */
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+public class DisconnectAfterEOF {
+
+    /*
+     * Worker thread to service single connection - can service
+     * multiple http requests on same connection.
+     */
+    static class Worker extends Thread {
+        Socket s;
+
+        Worker(Socket s) {
+            this.s = s;
+        }
+
+        public void run() {
+            try {
+                InputStream in = s.getInputStream();
+                PrintStream out = new PrintStream(
+                                        new BufferedOutputStream(
+                                                s.getOutputStream() ));
+                byte b[] = new byte[1024];
+                int n = -1;
+                int cl = -1;
+                int remaining = -1;
+                StringBuffer sb = new StringBuffer();
+                Random r = new Random();
+                boolean close = false;
+
+                boolean inBody = false;
+                for (;;) {
+                    boolean sendResponse = false;
+
+                    try {
+                        n = in.read(b);
+                    } catch (IOException ioe) {
+                        n = -1;
+                    }
+                    if (n <= 0) {
+                        if (inBody) {
+                            System.err.println("ERROR: Client closed before before " +
+                                "entire request received.");
+                        }
+                        return;
+                    }
+
+                    // reading entity-body
+                    if (inBody) {
+                        if (n > remaining) {
+                            System.err.println("Receiving more than expected!!!");
+                            return;
+                        }
+                        remaining -= n;
+
+                        if (remaining == 0) {
+                            sendResponse = true;
+                            n = 0;
+                        } else {
+                            continue;
+                        }
+                    }
+
+                    // reading headers
+                    for (int i=0; i<n; i++) {
+                        char c = (char)b[i];
+
+                        if (c != '\n') {
+                            sb.append(c);
+                            continue;
+                        }
+
+
+                        // Got end-of-line
+                        int len = sb.length();
+                        if (len > 0) {
+                            if (sb.charAt(len-1) != '\r') {
+                                System.err.println("Unexpected CR in header!!");
+                                return;
+                            }
+                        }
+                        sb.setLength(len-1);
+
+                        // empty line
+                        if (sb.length() == 0) {
+                            if (cl < 0) {
+                                System.err.println("Content-Length not found!!!");
+                                return;
+                            }
+
+                            // the surplus is body data
+                            int dataRead = n - (i+1);
+                            remaining = cl - dataRead;
+                            if (remaining > 0) {
+                                inBody = true;
+                                break;
+                            } else {
+                                // entire body has been read
+                                sendResponse = true;
+                            }
+                        } else {
+                            // non-empty line - check for Content-Length
+                            String line = sb.toString().toLowerCase();
+                            if (line.startsWith("content-length")) {
+                                StringTokenizer st = new StringTokenizer(line, ":");
+                                st.nextToken();
+                                cl = Integer.parseInt(st.nextToken().trim());
+                            }
+                            if (line.startsWith("connection")) {
+                                StringTokenizer st = new StringTokenizer(line, ":");
+                                st.nextToken();
+                                if (st.nextToken().trim().equals("close")) {
+                                    close =true;
+                                }
+                            }
+                        }
+                        sb = new StringBuffer();
+                    }
+
+
+                   if (sendResponse) {
+                        // send a large response
+                        int rspLen = 32000;
+
+                        out.print("HTTP/1.1 200 OK\r\n");
+                        out.print("Content-Length: " + rspLen + "\r\n");
+                        out.print("\r\n");
+
+                        if (rspLen > 0)
+                            out.write(new byte[rspLen]);
+
+                        out.flush();
+
+                        if (close)
+                            return;
+
+                        sendResponse = false;
+                        inBody = false;
+                        cl = -1;
+                   }
+                }
+
+            } catch (IOException ioe) {
+            } finally {
+                try {
+                    s.close();
+                } catch (Exception e) { }
+                System.out.println("+ Worker thread shutdown.");
+            }
+        }
+    }
+
+    /*
+     * Server thread to accept connection and create worker threads
+     * to service each connection.
+     */
+    static class Server extends Thread {
+        ServerSocket ss;
+
+        Server(ServerSocket ss) {
+            this.ss = ss;
+        }
+
+        public void run() {
+            try {
+                for (;;) {
+                    Socket s = ss.accept();
+                    Worker w = new Worker(s);
+                    w.start();
+                }
+
+            } catch (IOException ioe) {
+            }
+
+            System.out.println("+ Server shutdown.");
+        }
+
+        public void shutdown() {
+            try {
+                ss.close();
+            } catch (IOException ioe) { }
+        }
+    }
+
+    static URLConnection doRequest(String uri) throws IOException {
+        URLConnection uc = (new URL(uri)).openConnection();
+        uc.setDoOutput(true);
+        OutputStream out = uc.getOutputStream();
+        out.write(new byte[16000]);
+
+        // force the request to be sent
+        uc.getInputStream();
+        return uc;
+    }
+
+    static URLConnection doResponse(URLConnection uc) throws IOException {
+        int cl = ((HttpURLConnection)uc).getContentLength();
+        byte b[] = new byte[4096];
+        int n;
+        do {
+            n = uc.getInputStream().read(b);
+            if (n > 0) cl -= n;
+        } while (n > 0);
+        if (cl != 0) {
+            throw new RuntimeException("ERROR: content-length mismatch");
+        }
+        return uc;
+    }
+
+    public static void main(String args[]) throws Exception {
+        Random r = new Random();
+
+        // start server
+        ServerSocket ss = new ServerSocket(0);
+        Server svr = new Server(ss);
+        svr.start();
+
+        String uri = "http://localhost:" +
+                     Integer.toString(ss.getLocalPort()) +
+                     "/foo.html";
+
+        /*
+         * The following is the test scenario we create here :-
+         *
+         * 1. We do a http request/response and read the response
+         *    to EOF. As it's a persistent connection the idle
+         *    connection should go into the keep-alive cache for a
+         *    few seconds.
+         *
+         * 2. We start a second request but don't read the response.
+         *    As the request is to the same server we can assume it
+         *    (for our implementation anyway) that it will use the
+         *    same TCP connection.
+         *
+         * 3. We "disconnect" the first HttpURLConnection. This
+         *    should be no-op because the connection is in use
+         *    but another request. However with 1.3.1 and 1.4/1.4.1
+         *    this causes the TCP connection for the second request
+         *    to be closed.
+         *
+         */
+        URLConnection uc1 = doRequest(uri);
+        doResponse(uc1);
+
+        Thread.currentThread().sleep(2000);
+
+        URLConnection uc2 = doRequest(uri);
+
+        ((HttpURLConnection)uc1).disconnect();
+
+        IOException ioe = null;
+        try {
+            doResponse(uc2);
+        } catch (IOException x) {
+            ioe = x;
+        }
+
+        ((HttpURLConnection)uc2).disconnect();
+
+        /*
+         * Shutdown server as we are done. Worker threads created
+         * by the server will shutdown automatically when the
+         * client connection closes.
+         */
+        svr.shutdown();
+
+        if (ioe != null) {
+            throw ioe;
+        }
+    }
+}