blob: 44a65f6ea24193936c5148a55baf61a12d5e1b79 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any questions.
22 */
23
24/**
25 * @test
26 * @bug 4774503
27 * @summary Calling HttpURLConnection's disconnect method after the
28 * response has been received causes havoc with persistent
29 * connections.
30 */
31import java.net.*;
32import java.io.*;
33import java.util.*;
34
35public class DisconnectAfterEOF {
36
37 /*
38 * Worker thread to service single connection - can service
39 * multiple http requests on same connection.
40 */
41 static class Worker extends Thread {
42 Socket s;
43
44 Worker(Socket s) {
45 this.s = s;
46 }
47
48 public void run() {
49 try {
50 InputStream in = s.getInputStream();
51 PrintStream out = new PrintStream(
52 new BufferedOutputStream(
53 s.getOutputStream() ));
54 byte b[] = new byte[1024];
55 int n = -1;
56 int cl = -1;
57 int remaining = -1;
58 StringBuffer sb = new StringBuffer();
59 Random r = new Random();
60 boolean close = false;
61
62 boolean inBody = false;
63 for (;;) {
64 boolean sendResponse = false;
65
66 try {
67 n = in.read(b);
68 } catch (IOException ioe) {
69 n = -1;
70 }
71 if (n <= 0) {
72 if (inBody) {
73 System.err.println("ERROR: Client closed before before " +
74 "entire request received.");
75 }
76 return;
77 }
78
79 // reading entity-body
80 if (inBody) {
81 if (n > remaining) {
82 System.err.println("Receiving more than expected!!!");
83 return;
84 }
85 remaining -= n;
86
87 if (remaining == 0) {
88 sendResponse = true;
89 n = 0;
90 } else {
91 continue;
92 }
93 }
94
95 // reading headers
96 for (int i=0; i<n; i++) {
97 char c = (char)b[i];
98
99 if (c != '\n') {
100 sb.append(c);
101 continue;
102 }
103
104
105 // Got end-of-line
106 int len = sb.length();
107 if (len > 0) {
108 if (sb.charAt(len-1) != '\r') {
109 System.err.println("Unexpected CR in header!!");
110 return;
111 }
112 }
113 sb.setLength(len-1);
114
115 // empty line
116 if (sb.length() == 0) {
117 if (cl < 0) {
118 System.err.println("Content-Length not found!!!");
119 return;
120 }
121
122 // the surplus is body data
123 int dataRead = n - (i+1);
124 remaining = cl - dataRead;
125 if (remaining > 0) {
126 inBody = true;
127 break;
128 } else {
129 // entire body has been read
130 sendResponse = true;
131 }
132 } else {
133 // non-empty line - check for Content-Length
134 String line = sb.toString().toLowerCase();
135 if (line.startsWith("content-length")) {
136 StringTokenizer st = new StringTokenizer(line, ":");
137 st.nextToken();
138 cl = Integer.parseInt(st.nextToken().trim());
139 }
140 if (line.startsWith("connection")) {
141 StringTokenizer st = new StringTokenizer(line, ":");
142 st.nextToken();
143 if (st.nextToken().trim().equals("close")) {
144 close =true;
145 }
146 }
147 }
148 sb = new StringBuffer();
149 }
150
151
152 if (sendResponse) {
153 // send a large response
154 int rspLen = 32000;
155
156 out.print("HTTP/1.1 200 OK\r\n");
157 out.print("Content-Length: " + rspLen + "\r\n");
158 out.print("\r\n");
159
160 if (rspLen > 0)
161 out.write(new byte[rspLen]);
162
163 out.flush();
164
165 if (close)
166 return;
167
168 sendResponse = false;
169 inBody = false;
170 cl = -1;
171 }
172 }
173
174 } catch (IOException ioe) {
175 } finally {
176 try {
177 s.close();
178 } catch (Exception e) { }
179 System.out.println("+ Worker thread shutdown.");
180 }
181 }
182 }
183
184 /*
185 * Server thread to accept connection and create worker threads
186 * to service each connection.
187 */
188 static class Server extends Thread {
189 ServerSocket ss;
190
191 Server(ServerSocket ss) {
192 this.ss = ss;
193 }
194
195 public void run() {
196 try {
197 for (;;) {
198 Socket s = ss.accept();
199 Worker w = new Worker(s);
200 w.start();
201 }
202
203 } catch (IOException ioe) {
204 }
205
206 System.out.println("+ Server shutdown.");
207 }
208
209 public void shutdown() {
210 try {
211 ss.close();
212 } catch (IOException ioe) { }
213 }
214 }
215
216 static URLConnection doRequest(String uri) throws IOException {
217 URLConnection uc = (new URL(uri)).openConnection();
218 uc.setDoOutput(true);
219 OutputStream out = uc.getOutputStream();
220 out.write(new byte[16000]);
221
222 // force the request to be sent
223 uc.getInputStream();
224 return uc;
225 }
226
227 static URLConnection doResponse(URLConnection uc) throws IOException {
228 int cl = ((HttpURLConnection)uc).getContentLength();
229 byte b[] = new byte[4096];
230 int n;
231 do {
232 n = uc.getInputStream().read(b);
233 if (n > 0) cl -= n;
234 } while (n > 0);
235 if (cl != 0) {
236 throw new RuntimeException("ERROR: content-length mismatch");
237 }
238 return uc;
239 }
240
241 public static void main(String args[]) throws Exception {
242 Random r = new Random();
243
244 // start server
245 ServerSocket ss = new ServerSocket(0);
246 Server svr = new Server(ss);
247 svr.start();
248
249 String uri = "http://localhost:" +
250 Integer.toString(ss.getLocalPort()) +
251 "/foo.html";
252
253 /*
254 * The following is the test scenario we create here :-
255 *
256 * 1. We do a http request/response and read the response
257 * to EOF. As it's a persistent connection the idle
258 * connection should go into the keep-alive cache for a
259 * few seconds.
260 *
261 * 2. We start a second request but don't read the response.
262 * As the request is to the same server we can assume it
263 * (for our implementation anyway) that it will use the
264 * same TCP connection.
265 *
266 * 3. We "disconnect" the first HttpURLConnection. This
267 * should be no-op because the connection is in use
268 * but another request. However with 1.3.1 and 1.4/1.4.1
269 * this causes the TCP connection for the second request
270 * to be closed.
271 *
272 */
273 URLConnection uc1 = doRequest(uri);
274 doResponse(uc1);
275
276 Thread.currentThread().sleep(2000);
277
278 URLConnection uc2 = doRequest(uri);
279
280 ((HttpURLConnection)uc1).disconnect();
281
282 IOException ioe = null;
283 try {
284 doResponse(uc2);
285 } catch (IOException x) {
286 ioe = x;
287 }
288
289 ((HttpURLConnection)uc2).disconnect();
290
291 /*
292 * Shutdown server as we are done. Worker threads created
293 * by the server will shutdown automatically when the
294 * client connection closes.
295 */
296 svr.shutdown();
297
298 if (ioe != null) {
299 throw ioe;
300 }
301 }
302}