blob: 60346044c3d59b7e2ab402d614d0fe1452f5fe0d [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 4636628
27 * @summary HttpURLConnection duplicates HTTP GET requests when used with multiple threads
28*/
29
30/*
31 * This tests keep-alive behavior using chunkedinputstreams
32 * It checks that keep-alive connections are used and also
33 * that requests are not being repeated (due to errors)
34 *
35 * It also checks that the keepalive connections are closed eventually
36 * because the test will not terminate if the connections
37 * are not closed by the keep-alive timer.
38 */
39
40import java.net.*;
41import java.io.*;
42
43public class MultiThreadTest extends Thread {
44
45 /*
46 * Is debugging enabled - start with -d to enable.
47 */
48 static boolean debug = false;
49
50 static Object threadlock = new Object ();
51 static int threadCounter = 0;
52
53 static Object getLock() { return threadlock; }
54
55 static void debug(String msg) {
56 if (debug)
57 System.out.println(msg);
58 }
59
60 static int reqnum = 0;
61
62 void doRequest(String uri) throws Exception {
63 URL url = new URL(uri + "?foo="+reqnum);
64 reqnum ++;
65 HttpURLConnection http = (HttpURLConnection)url.openConnection();
66
67 InputStream in = http.getInputStream();
68 byte b[] = new byte[100];
69 int total = 0;
70 int n;
71 do {
72 n = in.read(b);
73 if (n > 0) total += n;
74 } while (n > 0);
75 debug ("client: read " + total + " bytes");
76 in.close();
77 http.disconnect();
78 }
79
80 String uri;
81 byte[] b;
82 int requests;
83
84 MultiThreadTest(int port, int requests) throws Exception {
85 uri = "http://localhost:" +
86 port + "/foo.html";
87
88 b = new byte [256];
89 this.requests = requests;
90
91 synchronized (threadlock) {
92 threadCounter ++;
93 }
94 }
95
96 public void run () {
97 try {
98 for (int i=0; i<requests; i++) {
99 doRequest (uri);
100 }
101 } catch (Exception e) {
102 throw new RuntimeException (e.getMessage());
103 }
104 synchronized (threadlock) {
105 threadCounter --;
106 if (threadCounter == 0) {
107 threadlock.notifyAll();
108 }
109 }
110 }
111
112 static int threads=5;
113
114 public static void main(String args[]) throws Exception {
115
116 int x = 0, arg_len = args.length;
117 int requests = 20;
118
119 if (arg_len > 0 && args[0].equals("-d")) {
120 debug = true;
121 x = 1;
122 arg_len --;
123 }
124 if (arg_len > 0) {
125 threads = Integer.parseInt (args[x]);
126 requests = Integer.parseInt (args[x+1]);
127 }
128
129 /* start the server */
130 ServerSocket ss = new ServerSocket(0);
131 Server svr = new Server(ss);
132 svr.start();
133
134 Object lock = MultiThreadTest.getLock();
135 synchronized (lock) {
136 for (int i=0; i<threads; i++) {
137 MultiThreadTest t = new MultiThreadTest(ss.getLocalPort(), requests);
138 t.start ();
139 }
140 try {
141 lock.wait();
142 } catch (InterruptedException e) {}
143 }
144
145 // shutdown server - we're done.
146 svr.shutdown();
147
148 int cnt = svr.connectionCount();
149 MultiThreadTest.debug("Connections = " + cnt);
150 int reqs = Worker.getRequests ();
151 MultiThreadTest.debug("Requests = " + reqs);
152 System.out.println ("Connection count = " + cnt + " Request count = " + reqs);
153 if (cnt > threads) { // could be less
154 throw new RuntimeException ("Expected "+threads + " connections: used " +cnt);
155 }
156 if (reqs != threads*requests) {
157 throw new RuntimeException ("Expected "+ threads*requests+ " requests: got " +reqs);
158 }
159 }
160}
161
162 /*
163 * Server thread to accept connection and create worker threads
164 * to service each connection.
165 */
166 class Server extends Thread {
167 ServerSocket ss;
168 int connectionCount;
169 boolean shutdown = false;
170
171 Server(ServerSocket ss) {
172 this.ss = ss;
173 }
174
175 public synchronized int connectionCount() {
176 return connectionCount;
177 }
178
179 public synchronized void shutdown() {
180 shutdown = true;
181 }
182
183 public void run() {
184 try {
185 ss.setSoTimeout(2000);
186
187 for (;;) {
188 Socket s;
189 try {
190 MultiThreadTest.debug("server: calling accept.");
191 s = ss.accept();
192 MultiThreadTest.debug("server: return accept.");
193 } catch (SocketTimeoutException te) {
194 MultiThreadTest.debug("server: STE");
195 synchronized (this) {
196 if (shutdown) {
197 MultiThreadTest.debug("server: Shuting down.");
198 return;
199 }
200 }
201 continue;
202 }
203
204 int id;
205 synchronized (this) {
206 id = connectionCount++;
207 }
208
209 Worker w = new Worker(s, id);
210 w.start();
211 MultiThreadTest.debug("server: Started worker " + id);
212 }
213
214 } catch (Exception e) {
215 e.printStackTrace();
216 } finally {
217 try {
218 ss.close();
219 } catch (Exception e) { }
220 }
221 }
222 }
223
224 /*
225 * Worker thread to service single connection - can service
226 * multiple http requests on same connection.
227 */
228 class Worker extends Thread {
229 Socket s;
230 int id;
231
232 Worker(Socket s, int id) {
233 this.s = s;
234 this.id = id;
235 }
236
237 static int requests = 0;
238 static Object rlock = new Object();
239
240 public static int getRequests () {
241 synchronized (rlock) {
242 return requests;
243 }
244 }
245 public static void incRequests () {
246 synchronized (rlock) {
247 requests++;
248 }
249 }
250
251 int readUntil (InputStream in, char[] seq) throws IOException {
252 int i=0, count=0;
253 while (true) {
254 int c = in.read();
255 if (c == -1)
256 return -1;
257 count++;
258 if (c == seq[i]) {
259 i++;
260 if (i == seq.length)
261 return count;
262 continue;
263 } else {
264 i = 0;
265 }
266 }
267 }
268
269 public void run() {
270 try {
271 int max = 400;
272 byte b[] = new byte[1000];
273 InputStream in = new BufferedInputStream (s.getInputStream());
274 // response to client
275 PrintStream out = new PrintStream(
276 new BufferedOutputStream(
277 s.getOutputStream() ));
278
279 for (;;) {
280
281 // read entire request from client
282 int n=0;
283
284 n = readUntil (in, new char[] {'\r','\n', '\r','\n'});
285
286 if (n <= 0) {
287 MultiThreadTest.debug("worker: " + id + ": Shutdown");
288 s.close();
289 return;
290 }
291
292 MultiThreadTest.debug("worker " + id +
293 ": Read request from client " +
294 "(" + n + " bytes).");
295
296 incRequests();
297 out.print("HTTP/1.1 200 OK\r\n");
298 out.print("Transfer-Encoding: chunked\r\n");
299 out.print("Content-Type: text/html\r\n");
300 out.print("Connection: Keep-Alive\r\n");
301 out.print ("Keep-Alive: timeout=15, max="+max+"\r\n");
302 out.print("\r\n");
303 out.print ("6\r\nHello \r\n");
304 out.print ("5\r\nWorld\r\n");
305 out.print ("0\r\n\r\n");
306 out.flush();
307
308 if (--max == 0) {
309 s.close();
310 return;
311 }
312 }
313
314 } catch (Exception e) {
315 e.printStackTrace();
316 } finally {
317 try {
318 s.close();
319 } catch (Exception e) { }
320 }
321 }
322 }