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