ZeroContentLength.java revision 2362:00cd9dc3c2b5
1/* 2 * Copyright (c) 2001, 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 4507412 27 * @bug 4506998 28 * @summary Check that a 304 "Not-Modified" response from a server 29 * doesn't cause http client to close a keep-alive 30 * connection. 31 * Check that a content-length of 0 results in an 32 * empty input stream. 33 */ 34import java.net.*; 35import java.io.*; 36 37public class ZeroContentLength { 38 39 /* 40 * Is debugging enabled - start with -d to enable. 41 */ 42 static boolean debug = false; 43 44 static void debug(String msg) { 45 if (debug) 46 System.out.println(msg); 47 } 48 49 /* 50 * The response string and content-length that 51 * the server should return; 52 */ 53 static String response; 54 static int contentLength; 55 56 static synchronized void setResponse(String rsp, int cl) { 57 response = rsp; 58 contentLength = cl; 59 } 60 61 /* 62 * Worker thread to service single connection - can service 63 * multiple http requests on same connection. 64 */ 65 class Worker extends Thread { 66 Socket s; 67 int id; 68 69 Worker(Socket s, int id) { 70 this.s = s; 71 this.id = id; 72 } 73 74 public void run() { 75 try { 76 77 s.setSoTimeout(2000); 78 int max = 100; 79 80 for (;;) { 81 82 // read entire request from client 83 byte b[] = new byte[100]; 84 InputStream in = s.getInputStream(); 85 int n, total=0; 86 87 try { 88 do { 89 n = in.read(b); 90 if (n > 0) total += n; 91 } while (n > 0); 92 } catch (SocketTimeoutException e) { } 93 94 debug("worker " + id + 95 ": Read request from client " + 96 "(" + total + " bytes)."); 97 98 if (total == 0) { 99 debug("worker: " + id + ": Shutdown"); 100 return; 101 } 102 103 // response to client 104 PrintStream out = new PrintStream( 105 new BufferedOutputStream( 106 s.getOutputStream() )); 107 108 out.print("HTTP/1.1 " + response + "\r\n"); 109 if (contentLength >= 0) { 110 out.print("Content-Length: " + contentLength + 111 "\r\n"); 112 } 113 out.print("\r\n"); 114 for (int i=0; i<contentLength; i++) { 115 out.write( (byte)'.' ); 116 } 117 out.flush(); 118 119 debug("worked " + id + 120 ": Sent response to client, length: " + contentLength); 121 122 if (--max == 0) { 123 s.close(); 124 return; 125 } 126 } 127 128 } catch (Exception e) { 129 e.printStackTrace(); 130 } finally { 131 try { 132 s.close(); 133 } catch (Exception e) { } 134 } 135 } 136 } 137 138 /* 139 * Server thread to accept connection and create worker threads 140 * to service each connection. 141 */ 142 class Server extends Thread { 143 ServerSocket ss; 144 int connectionCount; 145 boolean shutdown = false; 146 147 Server(ServerSocket ss) { 148 this.ss = ss; 149 } 150 151 public synchronized int connectionCount() { 152 return connectionCount; 153 } 154 155 public synchronized void shutdown() { 156 shutdown = true; 157 } 158 159 public void run() { 160 try { 161 ss.setSoTimeout(2000); 162 163 for (;;) { 164 Socket s; 165 try { 166 debug("server: Waiting for connections"); 167 s = ss.accept(); 168 } catch (SocketTimeoutException te) { 169 synchronized (this) { 170 if (shutdown) { 171 debug("server: Shuting down."); 172 return; 173 } 174 } 175 continue; 176 } 177 178 int id; 179 synchronized (this) { 180 id = connectionCount++; 181 } 182 183 Worker w = new Worker(s, id); 184 w.start(); 185 debug("server: Started worker " + id); 186 } 187 188 } catch (Exception e) { 189 e.printStackTrace(); 190 } finally { 191 try { 192 ss.close(); 193 } catch (Exception e) { } 194 } 195 } 196 } 197 198 /* 199 * Make a single http request and return the content length 200 * received. Also do sanity check to ensure that the 201 * content-length header matches the total received on 202 * the input stream. 203 */ 204 int doRequest(String uri) throws Exception { 205 URL url = new URL(uri); 206 HttpURLConnection http = (HttpURLConnection)url.openConnection(); 207 208 int cl = http.getContentLength(); 209 210 InputStream in = http.getInputStream(); 211 byte b[] = new byte[100]; 212 int total = 0; 213 int n; 214 do { 215 n = in.read(b); 216 if (n > 0) total += n; 217 } while (n > 0); 218 in.close(); 219 220 if (cl >= 0 && total != cl) { 221 System.err.println("content-length header indicated: " + cl); 222 System.err.println("Actual received: " + total); 223 throw new Exception("Content-length didn't match actual received"); 224 } 225 226 return total; 227 } 228 229 230 /* 231 * Send http requests to "server" and check that they all 232 * use the same network connection and that the content 233 * length corresponds to the content length expected. 234 * stream. 235 */ 236 ZeroContentLength() throws Exception { 237 238 /* start the server */ 239 ServerSocket ss = new ServerSocket(0); 240 Server svr = new Server(ss); 241 svr.start(); 242 243 String uri = "http://localhost:" + 244 Integer.toString(ss.getLocalPort()) + 245 "/foo.html"; 246 247 int expectedTotal = 0; 248 int actualTotal = 0; 249 250 System.out.println("**********************************"); 251 System.out.println("200 OK, content-length:1024 ..."); 252 setResponse("200 OK", 1024); 253 for (int i=0; i<5; i++) { 254 actualTotal += doRequest(uri); 255 expectedTotal += 1024; 256 } 257 258 System.out.println("**********************************"); 259 System.out.println("200 OK, content-length:0 ..."); 260 setResponse("200 OK", 0); 261 for (int i=0; i<5; i++) { 262 actualTotal += doRequest(uri); 263 } 264 265 System.out.println("**********************************"); 266 System.out.println("304 Not-Modified, (no content-length) ..."); 267 setResponse("304 Not-Modifed", -1); 268 for (int i=0; i<5; i++) { 269 actualTotal += doRequest(uri); 270 } 271 272 System.out.println("**********************************"); 273 System.out.println("204 No-Content, (no content-length) ..."); 274 setResponse("204 No-Content", -1); 275 for (int i=0; i<5; i++) { 276 actualTotal += doRequest(uri); 277 } 278 279 // shutdown server - we're done. 280 svr.shutdown(); 281 282 System.out.println("**********************************"); 283 284 if (actualTotal == expectedTotal) { 285 System.out.println("Passed: Actual total equal to expected total"); 286 } else { 287 throw new Exception("Actual total != Expected total!!!"); 288 } 289 290 int cnt = svr.connectionCount(); 291 if (cnt == 1) { 292 System.out.println("Passed: Only 1 connection established"); 293 } else { 294 throw new Exception("Test failed: Number of connections " + 295 "established: " + cnt + " - see log for details."); 296 } 297 } 298 299 public static void main(String args[]) throws Exception { 300 301 if (args.length > 0 && args[0].equals("-d")) { 302 debug = true; 303 } 304 305 new ZeroContentLength(); 306 } 307 308} 309