jdk/test/java/net/URLConnection/ZeroContentLength.java
author ohair
Tue, 25 May 2010 15:58:33 -0700
changeset 5506 202f599c92aa
parent 2 90ce3da70b43
child 6115 7c523cf2bc8a
permissions -rw-r--r--
6943119: Rebrand source copyright notices Reviewed-by: darcy, weijun

/*
 * 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();
    }

}