1/*
2 * Copyright (c) 2006, 2016, 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 6488669 6595324 6993490
27 * @modules jdk.httpserver
28 * @run main/othervm ChunkedErrorStream
29 * @summary Chunked ErrorStream tests
30 */
31
32import java.net.*;
33import java.io.*;
34import com.sun.net.httpserver.*;
35
36/**
37 * Part 1: 6488669
38 * 1) Http server that responds with an error code (>=400)
39 *    and a chunked response body. It also indicates that
40 *    the connection will be closed.
41 * 2) Client sends request to server and tries to
42 *    getErrorStream(). Some data must be able to be read
43 *    from the errorStream.
44 *
45 * Part 2: 6595324
46 * 1) Http server that responds with an error code (>=400)
47 *    and a chunked response body greater than
48 *    sun.net.http.errorstream.bufferSize, 4K + 10 bytes.
49 * 2) Client sends request to server and tries to
50 *    getErrorStream(). 4K + 10 bytes must be read from
51 *    the errorStream.
52 *
53 * Part 3: 6993490
54 *    Reuse persistent connection from part 2, the error stream
55 *    buffering will have set a reduced timeout on the socket and
56 *    tried to reset it to the default, infinity. Client must not
57 *    throw a timeout exception. If it does, it indicates that the
58 *    default timeout was not reset correctly.
59 *    If no timeout exception is thrown, it does not guarantee that
60 *    the timeout was reset correctly, as there is a potential race
61 *    between the sleeping server and the client thread. Typically,
62 *    1000 millis has been enought to reliable reproduce this problem
63 *    since the error stream buffering sets the timeout to 60 millis.
64 */
65
66public class ChunkedErrorStream
67{
68    com.sun.net.httpserver.HttpServer httpServer;
69
70    static {
71        // Enable ErrorStream buffering
72        System.getProperties().setProperty("sun.net.http.errorstream.enableBuffering", "true");
73
74        // No need to set this as 4K is the default
75        // System.getProperties().setProperty("sun.net.http.errorstream.bufferSize", "4096");
76    }
77
78    public static void main(String[] args) {
79        new ChunkedErrorStream();
80    }
81
82    public ChunkedErrorStream() {
83        try {
84            startHttpServer();
85            doClient();
86        } catch (IOException ioe) {
87            ioe.printStackTrace();
88        }  finally {
89            httpServer.stop(1);
90        }
91    }
92
93    void doClient() {
94        for (int times=0; times<3; times++) {
95            HttpURLConnection uc = null;
96            try {
97                InetSocketAddress address = httpServer.getAddress();
98                String URLStr = "http://localhost:" + address.getPort() + "/test/";
99                if (times == 0) {
100                    URLStr += "first";
101                } else {
102                    URLStr += "second";
103                }
104
105                System.out.println("Trying " + URLStr);
106                URL url = new URL(URLStr);
107                uc = (HttpURLConnection)url.openConnection();
108                uc.getInputStream();
109
110                throw new RuntimeException("Failed: getInputStream should throw and IOException");
111            }  catch (IOException e) {
112                if (e instanceof SocketTimeoutException) {
113                    e.printStackTrace();
114                    throw new RuntimeException("Failed: SocketTimeoutException should not happen");
115                }
116
117                // This is what we expect to happen.
118                InputStream es = uc.getErrorStream();
119                byte[] ba = new byte[1024];
120                int count = 0, ret;
121                try {
122                    while ((ret = es.read(ba)) != -1)
123                        count += ret;
124                    es.close();
125                } catch  (IOException ioe) {
126                    ioe.printStackTrace();
127                }
128
129                if (count == 0)
130                    throw new RuntimeException("Failed: ErrorStream returning 0 bytes");
131
132                if (times >= 1 && count != (4096+10))
133                    throw new RuntimeException("Failed: ErrorStream returning " + count +
134                                                 " bytes. Expecting " + (4096+10));
135
136                System.out.println("Read " + count + " bytes from the errorStream");
137            }
138        }
139    }
140
141    /**
142     * Http Server
143     */
144    void startHttpServer() throws IOException {
145        httpServer = com.sun.net.httpserver.HttpServer.create(new InetSocketAddress(0), 0);
146
147        // create HttpServer context
148        httpServer.createContext("/test/first", new FirstHandler());
149        httpServer.createContext("/test/second", new SecondHandler());
150
151        httpServer.start();
152    }
153
154    class FirstHandler implements HttpHandler {
155        public void handle(HttpExchange t) throws IOException {
156            InputStream is = t.getRequestBody();
157            byte[] ba = new byte[1024];
158            while (is.read(ba) != -1);
159            is.close();
160
161            Headers resHeaders = t.getResponseHeaders();
162            resHeaders.add("Connection", "close");
163            t.sendResponseHeaders(404, 0);
164            OutputStream os = t.getResponseBody();
165
166            // actual data doesn't matter. Just send 2K worth.
167            byte b = 'a';
168            for (int i=0; i<2048; i++)
169                os.write(b);
170
171            os.close();
172            t.close();
173        }
174    }
175
176    static class SecondHandler implements HttpHandler {
177        /* count greater than 0, slow response */
178        static int count = 0;
179
180        public void handle(HttpExchange t) throws IOException {
181            InputStream is = t.getRequestBody();
182            byte[] ba = new byte[1024];
183            while (is.read(ba) != -1);
184            is.close();
185
186            if (count > 0) {
187                System.out.println("server sleeping...");
188                try { Thread.sleep(1000); } catch(InterruptedException e) {}
189            }
190            count++;
191
192            t.sendResponseHeaders(404, 0);
193            OutputStream os = t.getResponseBody();
194
195            // actual data doesn't matter. Just send more than 4K worth
196            byte b = 'a';
197            for (int i=0; i<(4096+10); i++)
198                os.write(b);
199
200            os.close();
201            t.close();
202        }
203    }
204}
205