1 /* 2 * Copyright (c) 2002, 2010, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * 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 40 import java.net.*; 41 import java.io.*; 42 43 public 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]; 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 } finally { 104 synchronized (threadlock) { 105 threadCounter --; 106 if (threadCounter == 0) { 107 threadlock.notifyAll(); 108 } 109 } 110 } 111 } 112 113 static int threads=5; 114 115 public static void main(String args[]) throws Exception { 116 117 int x = 0, arg_len = args.length; 118 int requests = 20; 119 120 if (arg_len > 0 && args[0].equals("-d")) { 121 debug = true; 122 x = 1; 123 arg_len --; 124 } 125 if (arg_len > 0) { 126 threads = Integer.parseInt (args[x]); 127 requests = Integer.parseInt (args[x+1]); 128 } 129 130 /* start the server */ 131 ServerSocket ss = new ServerSocket(0); 132 Server svr = new Server(ss); 133 svr.start(); 134 135 Object lock = MultiThreadTest.getLock(); 140 } 141 try { 142 lock.wait(); 143 } catch (InterruptedException e) {} 144 } 145 146 // shutdown server - we're done. 147 svr.shutdown(); 148 149 int cnt = svr.connectionCount(); 150 MultiThreadTest.debug("Connections = " + cnt); 151 int reqs = Worker.getRequests (); 152 MultiThreadTest.debug("Requests = " + reqs); 153 System.out.println ("Connection count = " + cnt + " Request count = " + reqs); 154 if (cnt > threads) { // could be less 155 throw new RuntimeException ("Expected "+threads + " connections: used " +cnt); 156 } 157 if (reqs != threads*requests) { 158 throw new RuntimeException ("Expected "+ threads*requests+ " requests: got " +reqs); 159 } 160 } 161 } 162 163 /* 164 * Server thread to accept connection and create worker threads 165 * to service each connection. 166 */ 167 class Server extends Thread { 168 ServerSocket ss; 169 int connectionCount; 170 boolean shutdown = false; 171 172 Server(ServerSocket ss) { 173 this.ss = ss; 174 } 175 176 public synchronized int connectionCount() { 177 return connectionCount; 178 } 179 180 public synchronized void shutdown() { 181 shutdown = true; 182 } 183 184 public void run() { 185 try { 186 ss.setSoTimeout(2000); 187 188 for (;;) { 189 Socket s; 190 try { 191 MultiThreadTest.debug("server: calling accept."); 192 s = ss.accept(); 193 MultiThreadTest.debug("server: return accept."); 194 } catch (SocketTimeoutException te) { 195 MultiThreadTest.debug("server: STE"); 196 synchronized (this) { 197 if (shutdown) { 198 MultiThreadTest.debug("server: Shuting down."); 199 return; 200 } 201 } 202 continue; 203 } 204 205 int id; 206 synchronized (this) { 207 id = connectionCount++; 208 } 209 210 Worker w = new Worker(s, id); 211 w.start(); 212 MultiThreadTest.debug("server: Started worker " + id); 213 } 214 215 } catch (Exception e) { 216 e.printStackTrace(); 217 } finally { 218 try { 219 ss.close(); 220 } catch (Exception e) { } 221 } 222 } 223 } 224 225 /* 226 * Worker thread to service single connection - can service 227 * multiple http requests on same connection. 228 */ 229 class Worker extends Thread { 230 Socket s; 251 252 int readUntil (InputStream in, char[] seq) throws IOException { 253 int i=0, count=0; 254 while (true) { 255 int c = in.read(); 256 if (c == -1) 257 return -1; 258 count++; 259 if (c == seq[i]) { 260 i++; 261 if (i == seq.length) 262 return count; 263 continue; 264 } else { 265 i = 0; 266 } 267 } 268 } 269 270 public void run() { 271 try { 272 int max = 400; 273 byte b[] = new byte[1000]; 274 InputStream in = new BufferedInputStream (s.getInputStream()); 275 // response to client 276 PrintStream out = new PrintStream( 277 new BufferedOutputStream( 278 s.getOutputStream() )); 279 280 for (;;) { 281 282 // read entire request from client 283 int n=0; 284 285 n = readUntil (in, new char[] {'\r','\n', '\r','\n'}); 286 287 if (n <= 0) { 288 MultiThreadTest.debug("worker: " + id + ": Shutdown"); 289 s.close(); 290 return; 301 out.print("Connection: Keep-Alive\r\n"); 302 out.print ("Keep-Alive: timeout=15, max="+max+"\r\n"); 303 out.print("\r\n"); 304 out.print ("6\r\nHello \r\n"); 305 out.print ("5\r\nWorld\r\n"); 306 out.print ("0\r\n\r\n"); 307 out.flush(); 308 309 if (--max == 0) { 310 s.close(); 311 return; 312 } 313 } 314 315 } catch (Exception e) { 316 e.printStackTrace(); 317 } finally { 318 try { 319 s.close(); 320 } catch (Exception e) { } 321 } 322 } 323 } | 1 /* 2 * Copyright (c) 2002, 2018, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * 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 40 import java.net.*; 41 import java.io.*; 42 import java.time.Duration; 43 import java.util.ArrayList; 44 import java.util.List; 45 46 public class MultiThreadTest extends Thread { 47 48 /* 49 * Is debugging enabled - start with -d to enable. 50 */ 51 static boolean debug = true; // disable debug once stability proven 52 53 static Object threadlock = new Object (); 54 static int threadCounter = 0; 55 56 static Object getLock() { return threadlock; } 57 58 static void debug(String msg) { 59 if (debug) 60 System.out.println(msg); 61 } 62 63 static int reqnum = 0; 64 65 void doRequest(String uri) throws Exception { 66 URL url = new URL(uri + "?foo="+reqnum); 67 reqnum ++; 68 HttpURLConnection http = (HttpURLConnection)url.openConnection(); 69 70 InputStream in = http.getInputStream(); 71 byte b[] = new byte[100]; 80 http.disconnect(); 81 } 82 83 String uri; 84 byte[] b; 85 int requests; 86 87 MultiThreadTest(int port, int requests) throws Exception { 88 uri = "http://localhost:" + 89 port + "/foo.html"; 90 91 b = new byte [256]; 92 this.requests = requests; 93 94 synchronized (threadlock) { 95 threadCounter ++; 96 } 97 } 98 99 public void run () { 100 long start = System.nanoTime(); 101 102 try { 103 for (int i=0; i<requests; i++) { 104 doRequest (uri); 105 } 106 } catch (Exception e) { 107 throw new RuntimeException (e.getMessage()); 108 } finally { 109 synchronized (threadlock) { 110 threadCounter --; 111 if (threadCounter == 0) { 112 threadlock.notifyAll(); 113 } 114 } 115 } 116 debug("client: end - " + Duration.ofNanos(System.nanoTime() - start)); 117 } 118 119 static int threads=5; 120 121 public static void main(String args[]) throws Exception { 122 long start = System.nanoTime(); 123 124 int x = 0, arg_len = args.length; 125 int requests = 20; 126 127 if (arg_len > 0 && args[0].equals("-d")) { 128 debug = true; 129 x = 1; 130 arg_len --; 131 } 132 if (arg_len > 0) { 133 threads = Integer.parseInt (args[x]); 134 requests = Integer.parseInt (args[x+1]); 135 } 136 137 /* start the server */ 138 ServerSocket ss = new ServerSocket(0); 139 Server svr = new Server(ss); 140 svr.start(); 141 142 Object lock = MultiThreadTest.getLock(); 147 } 148 try { 149 lock.wait(); 150 } catch (InterruptedException e) {} 151 } 152 153 // shutdown server - we're done. 154 svr.shutdown(); 155 156 int cnt = svr.connectionCount(); 157 MultiThreadTest.debug("Connections = " + cnt); 158 int reqs = Worker.getRequests (); 159 MultiThreadTest.debug("Requests = " + reqs); 160 System.out.println ("Connection count = " + cnt + " Request count = " + reqs); 161 if (cnt > threads) { // could be less 162 throw new RuntimeException ("Expected "+threads + " connections: used " +cnt); 163 } 164 if (reqs != threads*requests) { 165 throw new RuntimeException ("Expected "+ threads*requests+ " requests: got " +reqs); 166 } 167 for (Thread worker : svr.workers()) { 168 worker.join(60_000); 169 } 170 171 debug("main thread end - " + Duration.ofNanos(System.nanoTime() - start)); 172 } 173 } 174 175 /* 176 * Server thread to accept connection and create worker threads 177 * to service each connection. 178 */ 179 class Server extends Thread { 180 ServerSocket ss; 181 int connectionCount; 182 boolean shutdown = false; 183 private List<Worker> workers = new ArrayList<>(); 184 185 Server(ServerSocket ss) { 186 this.ss = ss; 187 } 188 189 public synchronized List<Worker> workers() { 190 return workers; 191 } 192 193 public synchronized int connectionCount() { 194 return connectionCount; 195 } 196 197 public synchronized void shutdown() { 198 shutdown = true; 199 } 200 201 public void run() { 202 try { 203 ss.setSoTimeout(2000); 204 205 for (;;) { 206 Socket s; 207 try { 208 MultiThreadTest.debug("server: calling accept."); 209 s = ss.accept(); 210 MultiThreadTest.debug("server: return accept."); 211 } catch (SocketTimeoutException te) { 212 MultiThreadTest.debug("server: STE"); 213 synchronized (this) { 214 if (shutdown) { 215 MultiThreadTest.debug("server: Shuting down."); 216 return; 217 } 218 } 219 continue; 220 } 221 222 int id; 223 Worker w; 224 synchronized (this) { 225 id = connectionCount++; 226 w = new Worker(s, id); 227 workers.add(w); 228 } 229 w.start(); 230 MultiThreadTest.debug("server: Started worker " + id); 231 } 232 233 } catch (Exception e) { 234 e.printStackTrace(); 235 } finally { 236 try { 237 ss.close(); 238 } catch (Exception e) { } 239 } 240 } 241 } 242 243 /* 244 * Worker thread to service single connection - can service 245 * multiple http requests on same connection. 246 */ 247 class Worker extends Thread { 248 Socket s; 269 270 int readUntil (InputStream in, char[] seq) throws IOException { 271 int i=0, count=0; 272 while (true) { 273 int c = in.read(); 274 if (c == -1) 275 return -1; 276 count++; 277 if (c == seq[i]) { 278 i++; 279 if (i == seq.length) 280 return count; 281 continue; 282 } else { 283 i = 0; 284 } 285 } 286 } 287 288 public void run() { 289 long start = System.nanoTime(); 290 291 try { 292 int max = 400; 293 byte b[] = new byte[1000]; 294 InputStream in = new BufferedInputStream (s.getInputStream()); 295 // response to client 296 PrintStream out = new PrintStream( 297 new BufferedOutputStream( 298 s.getOutputStream() )); 299 300 for (;;) { 301 302 // read entire request from client 303 int n=0; 304 305 n = readUntil (in, new char[] {'\r','\n', '\r','\n'}); 306 307 if (n <= 0) { 308 MultiThreadTest.debug("worker: " + id + ": Shutdown"); 309 s.close(); 310 return; 321 out.print("Connection: Keep-Alive\r\n"); 322 out.print ("Keep-Alive: timeout=15, max="+max+"\r\n"); 323 out.print("\r\n"); 324 out.print ("6\r\nHello \r\n"); 325 out.print ("5\r\nWorld\r\n"); 326 out.print ("0\r\n\r\n"); 327 out.flush(); 328 329 if (--max == 0) { 330 s.close(); 331 return; 332 } 333 } 334 335 } catch (Exception e) { 336 e.printStackTrace(); 337 } finally { 338 try { 339 s.close(); 340 } catch (Exception e) { } 341 MultiThreadTest.debug("worker: " + id + " end - " + 342 Duration.ofNanos(System.nanoTime() - start)); 343 } 344 } 345 } |