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