1 /*
   2  * Copyright (c) 2004, 2019, 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 5026745
  27  * @modules java.base/sun.net.www
  28  * @build TestHttpsServer HttpCallback
  29  * @run main/othervm ChunkedOutputStream
  30  * @run main/othervm -Djava.net.preferIPv6Addresses=true ChunkedOutputStream
  31  *
  32  *     SunJSSE does not support dynamic system properties, no way to re-use
  33  *     system properties in samevm/agentvm mode.
  34  * @summary Cannot flush output stream when writing to an HttpUrlConnection
  35  */
  36 
  37 import java.io.*;
  38 import java.net.*;
  39 import javax.net.ssl.*;
  40 
  41 public class ChunkedOutputStream implements HttpCallback {
  42     /*
  43      * Where do we find the keystores for ssl?
  44      */
  45     static String pathToStores = "../../../../../javax/net/ssl/etc";
  46     static String keyStoreFile = "keystore";
  47     static String trustStoreFile = "truststore";
  48     static String passwd = "passphrase";
  49     static int count = 0;
  50 
  51     static final String str1 = "Helloworld1234567890abcdefghijklmnopqrstuvwxyz"+
  52                                 "1234567890abcdefkjsdlkjflkjsldkfjlsdkjflkj"+
  53                                 "1434567890abcdefkjsdlkjflkjsldkfjlsdkjflkj";
  54 
  55     static final String str2 = "Helloworld1234567890abcdefghijklmnopqrstuvwxyz"+
  56                                 "1234567890";
  57 
  58     public void request(HttpTransaction req) {
  59         try {
  60             // this is needed (count++ doesn't work), 'cause we
  61             // are doing concurrent tests
  62             String path = req.getRequestURI().getPath();
  63             if (path.equals("/d0")) {
  64                 count = 0;
  65             } else if (path.equals("/d01")) {
  66                 count = 1;
  67             } else if (path.equals("/d3")) {
  68                 count = 2;
  69             } else if (path.equals("/d4") || path.equals("/d5")) {
  70                 count = 3;
  71             } else if (path.equals("/d6")) {
  72                 count = 3;
  73             }  else if (path.equals("/d7")) {
  74                 count = 4;
  75             }  else if (path.equals("/d8")) {
  76                 count = 5;
  77             }
  78 
  79             switch (count) {
  80             case 0: /* test1 -- keeps conn alive */
  81             case 1: /* test2 -- closes conn */
  82                 String reqbody = req.getRequestEntityBody();
  83                 if (!reqbody.equals(str1)) {
  84                     req.sendResponse(500, "Internal server error");
  85                     req.orderlyClose();
  86                 }
  87                 String chunk = req.getRequestHeader("Transfer-encoding");
  88                 if (!"chunked".equals(chunk)) {
  89                     req.sendResponse(501, "Internal server error");
  90                     req.orderlyClose();
  91                 }
  92                 req.setResponseEntityBody(reqbody);
  93                 if (count == 1) {
  94                     req.setResponseHeader("Connection", "close");
  95                 }
  96                 req.sendResponse(200, "OK");
  97                 if (count == 1) {
  98                     req.orderlyClose();
  99                 }
 100                 break;
 101             case 2: /* test 3 */
 102                 reqbody = req.getRequestEntityBody();
 103                 if (!reqbody.equals(str2)) {
 104                     req.sendResponse(500, "Internal server error");
 105                     req.orderlyClose();
 106                 }
 107                 int clen = Integer.parseInt (
 108                         req.getRequestHeader("Content-length"));
 109                 if (clen != str2.length()) {
 110                     req.sendResponse(501, "Internal server error");
 111                     req.orderlyClose();
 112                 }
 113                 req.setResponseEntityBody (reqbody);
 114                 req.setResponseHeader("Connection", "close");
 115                 req.sendResponse(200, "OK");
 116                 req.orderlyClose();
 117                 break;
 118             case 3: /* test 6 */
 119                 req.setResponseHeader("Location", "https://foo.bar/");
 120                 req.setResponseHeader("Connection", "close");
 121                 req.sendResponse(307, "Temporary Redirect");
 122                 req.orderlyClose();
 123                 break;
 124             case 4: /* test 7 */
 125             case 5: /* test 8 */
 126                 reqbody = req.getRequestEntityBody();
 127                 if (reqbody != null && !"".equals(reqbody)) {
 128                     req.sendResponse(501, "Internal server error");
 129                     req.orderlyClose();
 130                 }
 131                 req.setResponseHeader("Connection", "close");
 132                 req.sendResponse(200, "OK");
 133                 req.orderlyClose();
 134                 break;
 135             }
 136         } catch (IOException e) {
 137             e.printStackTrace();
 138         }
 139     }
 140 
 141     public boolean closeOnException(Exception ex) {
 142         if (ex instanceof SSLException) {
 143             if (ex.toString().contains("Unrecognized SSL message, plaintext connection?")) {
 144                 System.out.println("TestHttpsServer receveived rogue connection: " + ex);
 145                 System.out.println("Ignoring rogue connection...");
 146                 return true;
 147             }
 148         }
 149         return false;
 150     }
 151 
 152     static void readAndCompare(InputStream is, String cmp) throws IOException {
 153         int c;
 154         byte buf[] = new byte[1024];
 155         int off = 0;
 156         int len = 1024;
 157         while ((c=is.read(buf, off, len)) != -1) {
 158             off += c;
 159             len -= c;
 160         }
 161         String s1 = new String(buf, 0, off, "ISO8859_1");
 162         if (!cmp.equals(s1)) {
 163             throw new IOException("strings not same");
 164         }
 165     }
 166 
 167     /* basic chunked test (runs twice) */
 168 
 169     static void test1(String u) throws Exception {
 170         URL url = new URL(u);
 171         System.out.println("client opening connection to: " + u);
 172         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 173         urlc.setChunkedStreamingMode(20);
 174         urlc.setDoOutput(true);
 175         urlc.setRequestMethod("POST");
 176         OutputStream os = urlc.getOutputStream();
 177         os.write(str1.getBytes());
 178         os.close();
 179         InputStream is = urlc.getInputStream();
 180         readAndCompare(is, str1);
 181         is.close();
 182     }
 183 
 184     /* basic fixed length test */
 185 
 186     static void test3(String u) throws Exception {
 187         URL url = new URL(u);
 188         System.out.println("client opening connection to: " + u);
 189         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 190         urlc.setFixedLengthStreamingMode(str2.length());
 191         urlc.setDoOutput(true);
 192         urlc.setRequestMethod("POST");
 193         OutputStream os = urlc.getOutputStream();
 194         os.write (str2.getBytes());
 195         os.close();
 196         InputStream is = urlc.getInputStream();
 197         readAndCompare(is, str2);
 198         is.close();
 199     }
 200 
 201     /* write too few bytes */
 202 
 203     static void test4(String u) throws Exception {
 204         URL url = new URL(u);
 205         System.out.println("client opening connection to: " + u);
 206         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 207         urlc.setFixedLengthStreamingMode(str2.length()+1);
 208         urlc.setDoOutput(true);
 209         urlc.setRequestMethod("POST");
 210         OutputStream os = urlc.getOutputStream();
 211         os.write(str2.getBytes());
 212         try {
 213             os.close();
 214             throw new Exception("should have thrown IOException");
 215         } catch (IOException e) {}
 216     }
 217 
 218     /* write too many bytes */
 219 
 220     static void test5(String u) throws Exception {
 221         URL url = new URL(u);
 222         System.out.println("client opening connection to: " + u);
 223         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 224         urlc.setFixedLengthStreamingMode(str2.length()-1);
 225         urlc.setDoOutput(true);
 226         urlc.setRequestMethod("POST");
 227         OutputStream os = urlc.getOutputStream();
 228         try {
 229             os.write(str2.getBytes());
 230             throw new Exception("should have thrown IOException");
 231         } catch (IOException e) {}
 232     }
 233 
 234     /* check for HttpRetryException on redirection */
 235 
 236     static void test6(String u) throws Exception {
 237         URL url = new URL(u);
 238         System.out.println("client opening connection to: " + u);
 239         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 240         urlc.setChunkedStreamingMode(20);
 241         urlc.setDoOutput(true);
 242         urlc.setRequestMethod("POST");
 243         OutputStream os = urlc.getOutputStream();
 244         os.write(str1.getBytes());
 245         os.close();
 246         try {
 247             InputStream is = urlc.getInputStream();
 248             throw new Exception("should have gotten HttpRetryException");
 249         } catch (HttpRetryException e) {
 250             if (e.responseCode() != 307) {
 251                 throw new Exception("Wrong response code " + e.responseCode());
 252             }
 253             if (!e.getLocation().equals("https://foo.bar/")) {
 254                 throw new Exception("Wrong location " + e.getLocation());
 255             }
 256         }
 257     }
 258 
 259     /* next two tests send zero length posts */
 260 
 261     static void test7(String u) throws Exception {
 262         URL url = new URL(u);
 263         System.out.println("client opening connection to: " + u);
 264         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 265         urlc.setChunkedStreamingMode(20);
 266         urlc.setDoOutput(true);
 267         urlc.setRequestMethod("POST");
 268         OutputStream os = urlc.getOutputStream();
 269         os.close();
 270         int ret = urlc.getResponseCode();
 271         if (ret != 200) {
 272             throw new Exception("Expected 200: got " + ret);
 273         }
 274     }
 275 
 276     static void test8(String u) throws Exception {
 277         URL url = new URL(u);
 278         System.out.println("client opening connection to: " + u);
 279         HttpURLConnection urlc = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
 280         urlc.setFixedLengthStreamingMode(0);
 281         urlc.setDoOutput(true);
 282         urlc.setRequestMethod("POST");
 283         OutputStream os = urlc.getOutputStream();
 284         os.close();
 285         int ret = urlc.getResponseCode();
 286         if (ret != 200) {
 287             throw new Exception("Expected 200: got " + ret);
 288         }
 289     }
 290 
 291     static TestHttpsServer server;
 292 
 293     public static void main(String[] args) throws Exception {
 294         // setup properties to do ssl
 295         String keyFilename =
 296             System.getProperty("test.src", "./") + "/" + pathToStores +
 297                 "/" + keyStoreFile;
 298         String trustFilename =
 299             System.getProperty("test.src", "./") + "/" + pathToStores +
 300                 "/" + trustStoreFile;
 301 
 302         InetAddress loopback = InetAddress.getLoopbackAddress();
 303 
 304         HostnameVerifier reservedHV =
 305             HttpsURLConnection.getDefaultHostnameVerifier();
 306         try {
 307             System.setProperty("javax.net.ssl.keyStore", keyFilename);
 308             System.setProperty("javax.net.ssl.keyStorePassword", passwd);
 309             System.setProperty("javax.net.ssl.trustStore", trustFilename);
 310             System.setProperty("javax.net.ssl.trustStorePassword", passwd);
 311             HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
 312 
 313             try {
 314                 server = new TestHttpsServer(
 315                         new ChunkedOutputStream(), 1, 10, loopback, 0);
 316                 System.out.println("Server started: listening on: " + server.getAuthority());
 317                 // the test server doesn't support keep-alive yet
 318                 // test1("http://" + server.getAuthority() + "/d0");
 319                 test1("https://" + server.getAuthority() + "/d01");
 320                 test3("https://" + server.getAuthority() + "/d3");
 321                 test4("https://" + server.getAuthority() + "/d4");
 322                 test5("https://" + server.getAuthority() + "/d5");
 323                 test6("https://" + server.getAuthority() + "/d6");
 324                 test7("https://" + server.getAuthority() + "/d7");
 325                 test8("https://" + server.getAuthority() + "/d8");
 326             } catch (Exception e) {
 327                 if (server != null) {
 328                     server.terminate();
 329                 }
 330                 throw e;
 331             }
 332             server.terminate();
 333         } finally {
 334             HttpsURLConnection.setDefaultHostnameVerifier(reservedHV);
 335         }
 336     }
 337 
 338     static class NameVerifier implements HostnameVerifier {
 339         public boolean verify(String hostname, SSLSession session) {
 340             return true;
 341         }
 342     }
 343 
 344     public static void except(String s) {
 345         server.terminate();
 346         throw new RuntimeException(s);
 347     }
 348 }