--- /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;
+ }
+ }
+}