8166530: sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java fails intermittently
authorasmotrak
Thu, 20 Oct 2016 11:23:41 -0700
changeset 41595 f1213215e135
parent 41594 e54e81e9be7b
child 41596 a6382b6ba29d
8166530: sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java fails intermittently Reviewed-by: chegar
jdk/test/javax/net/ssl/templates/SSLTest.java
jdk/test/sun/net/www/protocol/https/HttpsClient/OriginServer.java
jdk/test/sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java
--- a/jdk/test/javax/net/ssl/templates/SSLTest.java	Thu Oct 20 18:35:45 2016 +0100
+++ b/jdk/test/javax/net/ssl/templates/SSLTest.java	Thu Oct 20 11:23:41 2016 -0700
@@ -94,12 +94,22 @@
     /*
      * Is the server ready to serve?
      */
-    private final CountDownLatch serverCondition = new CountDownLatch(1);
+    private final CountDownLatch serverReadyCondition = new CountDownLatch(1);
 
     /*
      * Is the client ready to handshake?
      */
-    private final CountDownLatch clientCondition = new CountDownLatch(1);
+    private final CountDownLatch clientReadyCondition = new CountDownLatch(1);
+
+    /*
+     * Is the server done?
+     */
+    private final CountDownLatch serverDoneCondition = new CountDownLatch(1);
+
+    /*
+     * Is the client done?
+     */
+    private final CountDownLatch clientDoneCondition = new CountDownLatch(1);
 
     /*
      * Public API.
@@ -162,6 +172,25 @@
         return keystore;
     }
 
+    // Try to accept a connection in 30 seconds.
+    public static SSLSocket accept(SSLServerSocket sslServerSocket)
+            throws IOException {
+
+        return accept(sslServerSocket, SERVER_TIMEOUT);
+    }
+
+    public static SSLSocket accept(SSLServerSocket sslServerSocket, int timeout)
+            throws IOException {
+
+        try {
+            sslServerSocket.setSoTimeout(timeout);
+            return (SSLSocket) sslServerSocket.accept();
+        } catch (SocketTimeoutException ste) {
+            sslServerSocket.close();
+            return null;
+        }
+    }
+
     public SSLTest setSeparateServerThread(boolean separateServerThread) {
         this.separateServerThread = separateServerThread;
         return this;
@@ -202,33 +231,61 @@
     }
 
     public void signalServerReady() {
-        serverCondition.countDown();
+        serverReadyCondition.countDown();
+    }
+
+    public void signalServerDone() {
+        serverDoneCondition.countDown();
     }
 
     public boolean waitForClientSignal(long timeout, TimeUnit unit)
             throws InterruptedException {
 
-        return clientCondition.await(timeout, unit);
+        return clientReadyCondition.await(timeout, unit);
     }
 
     public boolean waitForClientSignal() throws InterruptedException {
         return waitForClientSignal(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
     }
 
+    public boolean waitForClientDone(long timeout, TimeUnit unit)
+            throws InterruptedException {
+
+        return clientDoneCondition.await(timeout, unit);
+    }
+
+    public boolean waitForClientDone() throws InterruptedException {
+        return waitForClientDone(CLIENT_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
+    }
+
     public void signalClientReady() {
-        clientCondition.countDown();
+        clientReadyCondition.countDown();
+    }
+
+    public void signalClientDone() {
+        clientDoneCondition.countDown();
     }
 
     public boolean waitForServerSignal(long timeout, TimeUnit unit)
             throws InterruptedException {
 
-        return serverCondition.await(timeout, unit);
+        return serverReadyCondition.await(timeout, unit);
     }
 
     public boolean waitForServerSignal() throws InterruptedException {
         return waitForServerSignal(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
     }
 
+    public boolean waitForServerDone(long timeout, TimeUnit unit)
+            throws InterruptedException {
+
+        return serverDoneCondition.await(timeout, unit);
+    }
+
+    public boolean waitForServerDone() throws InterruptedException {
+        return waitForServerDone(SERVER_SIGNAL_TIMEOUT, TimeUnit.SECONDS);
+    }
+
     public SSLTest setServerPeer(Peer serverPeer) {
         this.serverPeer = serverPeer;
         return this;
@@ -310,19 +367,14 @@
         test.signalServerReady();
 
         // Try to accept a connection in 30 seconds.
-        SSLSocket sslSocket;
-        try {
-            sslServerSocket.setSoTimeout(SERVER_TIMEOUT);
-            sslSocket = (SSLSocket) sslServerSocket.accept();
-            print("Server accepted connection");
-        } catch (SocketTimeoutException ste) {
-            sslServerSocket.close();
-
+        SSLSocket sslSocket = accept(sslServerSocket);
+        if (sslSocket == null) {
             // Ignore the test case if no connection within 30 seconds.
             print("No incoming client connection in 30 seconds. "
-                    + "Ignore in server side.", ste);
+                    + "Ignore in server side.");
             return;
         }
+        print("Server accepted connection");
 
         // handle the connection
         try {
@@ -353,6 +405,8 @@
             sslSocket.close();
             sslServerSocket.close();
         }
+
+        test.signalServerDone();
     }
 
     /*
@@ -419,6 +473,8 @@
             print("Run client application");
             test.getClientApplication().run(sslSocket, test);
         }
+
+        test.signalClientDone();
     }
 
     /*
--- a/jdk/test/sun/net/www/protocol/https/HttpsClient/OriginServer.java	Thu Oct 20 18:35:45 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,160 +0,0 @@
-/*
- * 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.
- */
-
-/*
- *
- * This is a HTTP test server used by the regression test
- * for the bug fixes: 4323990, 4413069
- */
-
-import java.io.*;
-import java.net.*;
-import javax.net.*;
-
-/*
- * OriginServer.java -- a simple server that can serve
- * Http get request in both clear and secure channel
- */
-
-public abstract class OriginServer implements Runnable, Closeable {
-
-    private ServerSocket server = null;
-    Exception serverException = null;
-    private volatile boolean closed;
-
-    /**
-     * Constructs a OriginServer based on ss and
-     * obtains a response data's bytecodes using the method
-     * getBytes.
-     */
-    protected OriginServer(ServerSocket ss) throws Exception
-    {
-        server = ss;
-        newListener();
-        if (serverException != null)
-            throw serverException;
-    }
-
-    @Override
-    public void close() throws IOException {
-        if (closed)
-            return;
-        closed = true;
-        server.close();
-    }
-
-    /**
-     * Returns an array of bytes containing the bytes for
-     * the data sent in the response.
-     *
-     * @return the bytes for the information that is being sent
-     */
-    public abstract byte[] getBytes();
-
-    /**
-     * The "listen" thread that accepts a connection to the
-     * server, parses the header and sends back the response
-     */
-    public void run()
-    {
-        Socket socket;
-
-        // accept a connection
-        try {
-            socket = server.accept();
-        } catch (IOException e) {
-            if (!closed) {
-                System.out.println("Class Server died: " + e.getMessage());
-                serverException = e;
-            }
-            return;
-        }
-        try {
-            DataOutputStream out =
-                new DataOutputStream(socket.getOutputStream());
-            try {
-                BufferedReader in =
-                    new BufferedReader(new InputStreamReader(
-                                socket.getInputStream()));
-                // read the request
-                readRequest(in);
-                // retrieve bytecodes
-                byte[] bytecodes = getBytes();
-                // send bytecodes in response (assumes HTTP/1.0 or later)
-                try {
-                    out.writeBytes("HTTP/1.0 200 OK\r\n");
-                    out.writeBytes("Content-Length: " + bytecodes.length +
-                                   "\r\n");
-                    out.writeBytes("Content-Type: text/html\r\n\r\n");
-                    out.write(bytecodes);
-                    out.flush();
-                } catch (IOException ie) {
-                    serverException = ie;
-                    return;
-                }
-
-            } catch (Exception e) {
-                // write out error response
-                out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
-                out.writeBytes("Content-Type: text/html\r\n\r\n");
-                out.flush();
-            }
-
-        } catch (IOException ex) {
-            System.out.println("error writing response: " + ex.getMessage());
-            serverException = ex;
-
-        } finally {
-            try {
-                socket.close();
-            } catch (IOException e) {
-                serverException = e;
-            }
-        }
-    }
-
-    /**
-     * Create a new thread to listen.
-     */
-    private void newListener()
-    {
-        (new Thread(this)).start();
-    }
-
-    /**
-     * read the response, don't care for the syntax of the request-line
-     * for this testing
-     */
-    private static void readRequest(BufferedReader in)
-        throws IOException
-    {
-        String line = null;
-        System.out.println("Server received: ");
-        do {
-            if (line != null)
-                System.out.println(line);
-            line = in.readLine();
-        } while ((line.length() != 0) &&
-                (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
-    }
-}
--- a/jdk/test/sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java	Thu Oct 20 18:35:45 2016 +0100
+++ b/jdk/test/sun/net/www/protocol/https/HttpsClient/ProxyAuthTest.java	Thu Oct 20 11:23:41 2016 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2001, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2016, 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
@@ -27,6 +27,7 @@
  * @summary HttpsURLConnection doesn't send Proxy-Authorization on CONNECT
  *     Incorrect checking of proxy server response
  * @modules java.base/sun.net.www
+ * @library /javax/net/ssl/templates
  * @run main/othervm ProxyAuthTest fail
  * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes=Basic ProxyAuthTest fail
  * @run main/othervm -Djdk.http.auth.tunneling.disabledSchemes=Basic, ProxyAuthTest fail
@@ -41,12 +42,18 @@
 // No way to reserve and restore java.lang.Authenticator, as well as read-once
 // system properties, so this tests needs to run in othervm mode.
 
-import java.io.*;
-import java.net.*;
-import java.security.KeyStore;
-import javax.net.*;
-import javax.net.ssl.*;
-import java.security.cert.*;
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.Authenticator;
+import java.net.InetSocketAddress;
+import java.net.PasswordAuthentication;
+import java.net.Proxy;
+import java.net.URL;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSession;
 import static java.nio.charset.StandardCharsets.US_ASCII;
 
 /*
@@ -65,27 +72,20 @@
     static String trustStoreFile = "truststore";
     static String passwd = "passphrase";
 
-    volatile private static int serverPort = 0;
-
-    /*
-     * The TestServer implements a OriginServer that
-     * processes HTTP requests and responses.
+    /**
+     * read the response, don't care for the syntax of the request-line
+     * for this testing
      */
-    static class TestServer extends OriginServer {
-        public TestServer(ServerSocket ss) throws Exception {
-            super(ss);
-        }
-
-        /*
-         * Returns an array of bytes containing the bytes for
-         * the data sent in the response.
-         *
-         * @return bytes for the data in the response
-         */
-        public byte[] getBytes() {
-            return "Proxy authentication for tunneling succeeded ..".
-                        getBytes(US_ASCII);
-        }
+    private static void readRequest(BufferedReader in) throws IOException {
+        String line = null;
+        System.out.println("Server received: ");
+        do {
+            if (line != null) {
+                System.out.println(line);
+            }
+            line = in.readLine();
+        } while ((line.length() != 0) &&
+                (line.charAt(0) != '\r') && (line.charAt(0) != '\n'));
     }
 
     /*
@@ -93,94 +93,96 @@
      */
     public static void main(String args[]) throws Exception {
         boolean expectSuccess;
-        if (args[0].equals("succeed")) {
-            expectSuccess = true;
-        } else {
-            expectSuccess = false;
-        }
+        expectSuccess = args[0].equals("succeed");
 
         String keyFilename =
-            System.getProperty("test.src", "./") + "/" + pathToStores +
-                "/" + keyStoreFile;
+            SSLTest.TEST_SRC + "/" + pathToStores + "/" + keyStoreFile;
         String trustFilename =
-            System.getProperty("test.src", "./") + "/" + pathToStores +
-                "/" + trustStoreFile;
+            SSLTest.TEST_SRC + "/" + pathToStores + "/" + trustStoreFile;
+
+        SSLTest.setup(keyFilename, trustFilename, passwd);
+
+        new SSLTest()
+            .setServerApplication((socket, test) -> {
+                DataOutputStream out = new DataOutputStream(
+                        socket.getOutputStream());
 
-        System.setProperty("javax.net.ssl.keyStore", keyFilename);
-        System.setProperty("javax.net.ssl.keyStorePassword", passwd);
-        System.setProperty("javax.net.ssl.trustStore", trustFilename);
-        System.setProperty("javax.net.ssl.trustStorePassword", passwd);
+                try {
+                    BufferedReader in = new BufferedReader(
+                            new InputStreamReader(socket.getInputStream()));
+
+                    // read the request
+                    readRequest(in);
+
+                    // retrieve bytecodes
+                    byte[] bytecodes =
+                            "Proxy authentication for tunneling succeeded .."
+                                    .getBytes(US_ASCII);
 
-        boolean useSSL = true;
-        /*
-         * setup the server
-         */
-        Closeable server;
-        try {
-            ServerSocketFactory ssf =
-                ProxyAuthTest.getServerSocketFactory(useSSL);
-            ServerSocket ss = ssf.createServerSocket(serverPort);
-            serverPort = ss.getLocalPort();
-            server = new TestServer(ss);
-        } catch (Exception e) {
-            System.out.println("Server side failed:" +
-                                e.getMessage());
-            throw e;
-        }
-        // trigger the client
-        try {
-            doClientSide();
-            if (!expectSuccess) {
-                throw new RuntimeException(
-                        "Expected exception/failure to connect, but succeeded.");
-            }
-        } catch (IOException e) {
-            if (expectSuccess) {
-                System.out.println("Client side failed: " + e.getMessage());
-                throw e;
-            }
+                    // send bytecodes in response (assumes HTTP/1.0 or later)
+                    out.writeBytes("HTTP/1.0 200 OK\r\n");
+                    out.writeBytes("Content-Length: " + bytecodes.length +
+                                   "\r\n");
+                    out.writeBytes("Content-Type: text/html\r\n\r\n");
+                    out.write(bytecodes);
+                    out.flush();
+                } catch (IOException e) {
+                    // write out error response
+                    out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n");
+                    out.writeBytes("Content-Type: text/html\r\n\r\n");
+                    out.flush();
+                }
+            })
+            .setClientPeer(test -> {
+                try {
+                    doClientSide(test);
+                    if (!expectSuccess) {
+                        throw new RuntimeException("Expected exception/failure "
+                                + "to connect, but succeeded.");
+                    }
+                } catch (IOException e) {
+                    if (expectSuccess) {
+                        System.out.println("Client side failed: "
+                                + e.getMessage());
+                        throw e;
+                    }
 
-            if (! (e.getMessage().contains("Unable to tunnel through proxy") &&
-                   e.getMessage().contains("407")) ) {
-                throw new RuntimeException(
-                        "Expected exception about cannot tunnel, 407, etc, but got", e);
-            } else {
-                // Informative
-                System.out.println("Caught expected exception: " + e.getMessage());
-            }
-        } finally {
-            if (server != null)
-                server.close();
-        }
+                    if (! (e.getMessage().contains(
+                                "Unable to tunnel through proxy") &&
+                           e.getMessage().contains("407")) ) {
+
+                        throw new RuntimeException(
+                                "Expected exception about cannot tunnel, "
+                                        + "407, etc, but got", e);
+                    } else {
+                        // Informative
+                        System.out.println("Caught expected exception: "
+                                + e.getMessage());
+                    }
+                }
+            })
+            .runTest();
     }
 
-    private static ServerSocketFactory getServerSocketFactory
-                   (boolean useSSL) throws Exception {
-        if (useSSL) {
-            SSLServerSocketFactory ssf = null;
-            // set up key manager to do server authentication
-            SSLContext ctx;
-            KeyManagerFactory kmf;
-            KeyStore ks;
-            char[] passphrase = passwd.toCharArray();
-
-            ctx = SSLContext.getInstance("TLS");
-            kmf = KeyManagerFactory.getInstance("SunX509");
-            ks = KeyStore.getInstance("JKS");
+    static void doClientSide(SSLTest test) throws IOException {
 
-            ks.load(new FileInputStream(System.getProperty(
-                        "javax.net.ssl.keyStore")), passphrase);
-            kmf.init(ks, passphrase);
-            ctx.init(kmf.getKeyManagers(), null, null);
+        // Wait for server to get started.
+        //
+        // The server side takes care of the issue if the server cannot
+        // get started in 90 seconds.  The client side would just ignore
+        // the test case if the serer is not ready.
+        try {
+            if (!test.waitForServerSignal()) {
+                System.out.print("The server is not ready yet in 90 seconds. "
+                        + "Ignore in client side.");
+                return;
+            }
+        } catch (InterruptedException e) {
+            System.out.print("InterruptedException occured. "
+                    + "Ignore in client side.");
+            return;
+        }
 
-            ssf = ctx.getServerSocketFactory();
-            return ssf;
-        } else {
-            return ServerSocketFactory.getDefault();
-        }
-    }
-
-    static void doClientSide() throws IOException {
         /*
          * setup up a proxy with authentication information
          */
@@ -190,23 +192,27 @@
          * we want to avoid URLspoofCheck failures in cases where the cert
          * DN name does not match the hostname in the URL.
          */
-        HttpsURLConnection.setDefaultHostnameVerifier(
-                                      new NameVerifier());
+        HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
 
-        InetSocketAddress paddr = new InetSocketAddress("localhost", ps.getPort());
+        InetSocketAddress paddr = new InetSocketAddress(
+                "localhost", ps.getPort());
         Proxy proxy = new Proxy(Proxy.Type.HTTP, paddr);
 
-        URL url = new URL("https://" + "localhost:" + serverPort
-                                + "/index.html");
-        BufferedReader in = null;
+        URL url = new URL("https://" + "localhost:" + test.getServerPort()
+                + "/index.html");
+
+        // Signal the server, the client is ready to communicate.
+        test.signalClientReady();
+
         HttpsURLConnection uc = (HttpsURLConnection) url.openConnection(proxy);
-        try {
-            in = new BufferedReader(new InputStreamReader(uc.getInputStream()));
+        try (BufferedReader in = new BufferedReader(
+                new InputStreamReader(uc.getInputStream()))) {
+
             String inputLine;
             System.out.print("Client recieved from the server: ");
-            while ((inputLine = in.readLine()) != null)
+            while ((inputLine = in.readLine()) != null) {
                 System.out.println(inputLine);
-            in.close();
+            }
         } catch (IOException e) {
             // Assert that the error stream is not accessible from the failed
             // tunnel setup.
@@ -214,13 +220,13 @@
                 throw new RuntimeException("Unexpected error stream.");
             }
 
-            if (in != null)
-                in.close();
             throw e;
         }
     }
 
     static class NameVerifier implements HostnameVerifier {
+
+        @Override
         public boolean verify(String hostname, SSLSession session) {
             return true;
         }
@@ -244,9 +250,9 @@
 
     public static class TestAuthenticator extends Authenticator {
 
+        @Override
         public PasswordAuthentication getPasswordAuthentication() {
-            return new PasswordAuthentication("Test",
-                                         "test123".toCharArray());
+            return new PasswordAuthentication("Test", "test123".toCharArray());
         }
     }
 }