| /* |
| * Copyright (c) 2001, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| * or visit www.oracle.com if you need additional information or have any |
| * questions. |
| */ |
| |
| /** |
| * @test |
| * @bug 4507412 |
| * @bug 4506998 |
| * @summary Check that a 304 "Not-Modified" response from a server |
| * doesn't cause http client to close a keep-alive |
| * connection. |
| * Check that a content-length of 0 results in an |
| * empty input stream. |
| */ |
| import java.net.*; |
| import java.io.*; |
| |
| public class ZeroContentLength { |
| |
| /* |
| * Is debugging enabled - start with -d to enable. |
| */ |
| static boolean debug = false; |
| |
| static void debug(String msg) { |
| if (debug) |
| System.out.println(msg); |
| } |
| |
| /* |
| * The response string and content-length that |
| * the server should return; |
| */ |
| static String response; |
| static int contentLength; |
| |
| static synchronized void setResponse(String rsp, int cl) { |
| response = rsp; |
| contentLength = cl; |
| } |
| |
| /* |
| * Worker thread to service single connection - can service |
| * multiple http requests on same connection. |
| */ |
| class Worker extends Thread { |
| Socket s; |
| int id; |
| |
| Worker(Socket s, int id) { |
| this.s = s; |
| this.id = id; |
| } |
| |
| public void run() { |
| try { |
| |
| s.setSoTimeout(2000); |
| int max = 100; |
| |
| for (;;) { |
| |
| // read entire request from client |
| byte b[] = new byte[100]; |
| InputStream in = s.getInputStream(); |
| int n, total=0; |
| |
| try { |
| do { |
| n = in.read(b); |
| if (n > 0) total += n; |
| } while (n > 0); |
| } catch (SocketTimeoutException e) { } |
| |
| debug("worker " + id + |
| ": Read request from client " + |
| "(" + total + " bytes)."); |
| |
| if (total == 0) { |
| debug("worker: " + id + ": Shutdown"); |
| return; |
| } |
| |
| // response to client |
| PrintStream out = new PrintStream( |
| new BufferedOutputStream( |
| s.getOutputStream() )); |
| |
| out.print("HTTP/1.1 " + response + "\r\n"); |
| if (contentLength >= 0) { |
| out.print("Content-Length: " + contentLength + |
| "\r\n"); |
| } |
| out.print("\r\n"); |
| for (int i=0; i<contentLength; i++) { |
| out.write( (byte)'.' ); |
| } |
| out.flush(); |
| |
| debug("worked " + id + |
| ": Sent response to client, length: " + contentLength); |
| |
| if (--max == 0) { |
| s.close(); |
| return; |
| } |
| } |
| |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| s.close(); |
| } catch (Exception e) { } |
| } |
| } |
| } |
| |
| /* |
| * Server thread to accept connection and create worker threads |
| * to service each connection. |
| */ |
| class Server extends Thread { |
| ServerSocket ss; |
| int connectionCount; |
| boolean shutdown = false; |
| |
| Server(ServerSocket ss) { |
| this.ss = ss; |
| } |
| |
| public synchronized int connectionCount() { |
| return connectionCount; |
| } |
| |
| public synchronized void shutdown() { |
| shutdown = true; |
| } |
| |
| public void run() { |
| try { |
| ss.setSoTimeout(2000); |
| |
| for (;;) { |
| Socket s; |
| try { |
| debug("server: Waiting for connections"); |
| s = ss.accept(); |
| } catch (SocketTimeoutException te) { |
| synchronized (this) { |
| if (shutdown) { |
| debug("server: Shuting down."); |
| return; |
| } |
| } |
| continue; |
| } |
| |
| int id; |
| synchronized (this) { |
| id = connectionCount++; |
| } |
| |
| Worker w = new Worker(s, id); |
| w.start(); |
| debug("server: Started worker " + id); |
| } |
| |
| } catch (Exception e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| ss.close(); |
| } catch (Exception e) { } |
| } |
| } |
| } |
| |
| /* |
| * Make a single http request and return the content length |
| * received. Also do sanity check to ensure that the |
| * content-length header matches the total received on |
| * the input stream. |
| */ |
| int doRequest(String uri) throws Exception { |
| URL url = new URL(uri); |
| HttpURLConnection http = (HttpURLConnection)url.openConnection(); |
| |
| int cl = http.getContentLength(); |
| |
| InputStream in = http.getInputStream(); |
| byte b[] = new byte[100]; |
| int total = 0; |
| int n; |
| do { |
| n = in.read(b); |
| if (n > 0) total += n; |
| } while (n > 0); |
| in.close(); |
| |
| if (cl >= 0 && total != cl) { |
| System.err.println("content-length header indicated: " + cl); |
| System.err.println("Actual received: " + total); |
| throw new Exception("Content-length didn't match actual received"); |
| } |
| |
| return total; |
| } |
| |
| |
| /* |
| * Send http requests to "server" and check that they all |
| * use the same network connection and that the content |
| * length corresponds to the content length expected. |
| * stream. |
| */ |
| ZeroContentLength() throws Exception { |
| |
| /* start the server */ |
| ServerSocket ss = new ServerSocket(0); |
| Server svr = new Server(ss); |
| svr.start(); |
| |
| String uri = "http://localhost:" + |
| Integer.toString(ss.getLocalPort()) + |
| "/foo.html"; |
| |
| int expectedTotal = 0; |
| int actualTotal = 0; |
| |
| System.out.println("**********************************"); |
| System.out.println("200 OK, content-length:1024 ..."); |
| setResponse("200 OK", 1024); |
| for (int i=0; i<5; i++) { |
| actualTotal += doRequest(uri); |
| expectedTotal += 1024; |
| } |
| |
| System.out.println("**********************************"); |
| System.out.println("200 OK, content-length:0 ..."); |
| setResponse("200 OK", 0); |
| for (int i=0; i<5; i++) { |
| actualTotal += doRequest(uri); |
| } |
| |
| System.out.println("**********************************"); |
| System.out.println("304 Not-Modified, (no content-length) ..."); |
| setResponse("304 Not-Modifed", -1); |
| for (int i=0; i<5; i++) { |
| actualTotal += doRequest(uri); |
| } |
| |
| System.out.println("**********************************"); |
| System.out.println("204 No-Content, (no content-length) ..."); |
| setResponse("204 No-Content", -1); |
| for (int i=0; i<5; i++) { |
| actualTotal += doRequest(uri); |
| } |
| |
| // shutdown server - we're done. |
| svr.shutdown(); |
| |
| System.out.println("**********************************"); |
| |
| if (actualTotal == expectedTotal) { |
| System.out.println("Passed: Actual total equal to expected total"); |
| } else { |
| throw new Exception("Actual total != Expected total!!!"); |
| } |
| |
| int cnt = svr.connectionCount(); |
| if (cnt == 1) { |
| System.out.println("Passed: Only 1 connection established"); |
| } else { |
| throw new Exception("Test failed: Number of connections " + |
| "established: " + cnt + " - see log for details."); |
| } |
| } |
| |
| public static void main(String args[]) throws Exception { |
| |
| if (args.length > 0 && args[0].equals("-d")) { |
| debug = true; |
| } |
| |
| new ZeroContentLength(); |
| } |
| |
| } |