test/jdk/java/net/httpclient/ProxyTest.java
changeset 49765 ee6f7a61f3a5
parent 48083 b1c1b4ef4be2
child 52121 934969c63223
child 56451 9585061fdb04
--- a/test/jdk/java/net/httpclient/ProxyTest.java	Tue Apr 17 15:39:20 2018 +0200
+++ b/test/jdk/java/net/httpclient/ProxyTest.java	Tue Apr 17 08:54:17 2018 -0700
@@ -47,13 +47,15 @@
 import java.nio.charset.StandardCharsets;
 import java.security.NoSuchAlgorithmException;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CopyOnWriteArrayList;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
 import javax.net.ssl.SSLContext;
 import javax.net.ssl.SSLSession;
-import jdk.incubator.http.HttpClient;
-import jdk.incubator.http.HttpRequest;
-import jdk.incubator.http.HttpResponse;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
 import jdk.testlibrary.SimpleSSLContext;
 
 /**
@@ -64,7 +66,7 @@
  *          Verifies that downgrading from HTTP/2 to HTTP/1.1 works through
  *          an SSL Tunnel connection when the client is HTTP/2 and the server
  *          and proxy are HTTP/1.1
- * @modules jdk.incubator.httpclient
+ * @modules java.net.http
  * @library /lib/testlibrary/
  * @build jdk.testlibrary.SimpleSSLContext ProxyTest
  * @run main/othervm ProxyTest
@@ -102,7 +104,8 @@
         });
 
         server.setHttpsConfigurator(new Configurator(SSLContext.getDefault()));
-        server.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0), 0);
+        InetSocketAddress addr = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
+        server.bind(addr, 0);
         return server;
     }
 
@@ -162,7 +165,8 @@
     {
         System.out.println("Server is: " + server.getAddress().toString());
         System.out.println("Verifying communication with server");
-        URI uri = new URI("https:/" + server.getAddress().toString() + PATH + "x");
+        URI uri = new URI("https://localhost:"
+                          + server.getAddress().getPort() + PATH + "x");
         try (InputStream is = uri.toURL().openConnection().getInputStream()) {
             String resp = new String(is.readAllBytes(), StandardCharsets.UTF_8);
             System.out.println(resp);
@@ -177,7 +181,8 @@
         try {
             System.out.println("Proxy started");
             Proxy p = new Proxy(Proxy.Type.HTTP,
-                    InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort()));
+                    InetSocketAddress.createUnresolved("localhost",
+                            proxy.getAddress().getPort()));
             System.out.println("Verifying communication with proxy");
             HttpURLConnection conn = (HttpURLConnection)uri.toURL().openConnection(p);
             try (InputStream is = conn.getInputStream()) {
@@ -192,7 +197,8 @@
             System.out.println("Setting up request with HttpClient for version: "
                     + version.name());
             CountingProxySelector ps = CountingProxySelector.of(
-                    InetSocketAddress.createUnresolved("localhost", proxy.getAddress().getPort()));
+                    InetSocketAddress.createUnresolved("localhost",
+                            proxy.getAddress().getPort()));
             HttpClient client = HttpClient.newBuilder()
                 .version(version)
                 .proxy(ps)
@@ -204,7 +210,7 @@
 
             System.out.println("Sending request with HttpClient");
             HttpResponse<String> response
-                = client.send(request, HttpResponse.BodyHandler.asString());
+                = client.send(request, HttpResponse.BodyHandlers.ofString());
             System.out.println("Got response");
             String resp = response.body();
             System.out.println("Received: " + resp);
@@ -226,10 +232,14 @@
         final ServerSocket ss;
         final boolean DEBUG = false;
         final HttpServer serverImpl;
+        final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs
+                = new CopyOnWriteArrayList<>();
+        private volatile boolean stopped;
         TunnelingProxy(HttpServer serverImpl) throws IOException {
             this.serverImpl = serverImpl;
             ss = new ServerSocket();
             accept = new Thread(this::accept);
+            accept.setDaemon(true);
         }
 
         void start() throws IOException {
@@ -238,7 +248,8 @@
         }
 
         // Pipe the input stream to the output stream.
-        private synchronized Thread pipe(InputStream is, OutputStream os, char tag) {
+        private synchronized Thread pipe(InputStream is, OutputStream os,
+                                         char tag, CompletableFuture<Void> end) {
             return new Thread("TunnelPipe("+tag+")") {
                 @Override
                 public void run() {
@@ -258,13 +269,16 @@
                         }
                     } catch (IOException ex) {
                         if (DEBUG) ex.printStackTrace(System.out);
+                    } finally {
+                        end.complete(null);
                     }
                 }
             };
         }
 
         public InetSocketAddress getAddress() {
-            return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort());
+            return new InetSocketAddress(InetAddress.getLoopbackAddress(),
+                                         ss.getLocalPort());
         }
 
         // This is a bit shaky. It doesn't handle continuation
@@ -289,18 +303,14 @@
         public void accept() {
             Socket clientConnection = null;
             try {
-                while (true) {
+                while (!stopped) {
                     System.out.println("Tunnel: Waiting for client");
-                    Socket previous = clientConnection;
+                    Socket toClose;
                     try {
-                        clientConnection = ss.accept();
+                        toClose = clientConnection = ss.accept();
                     } catch (IOException io) {
                         if (DEBUG) io.printStackTrace(System.out);
                         break;
-                    } finally {
-                        // we have only 1 client at a time, so it is safe
-                        // to close the previous connection here
-                        if (previous != null) previous.close();
                     }
                     System.out.println("Tunnel: Client accepted");
                     Socket targetConnection = null;
@@ -325,7 +335,7 @@
 
                         // Open target connection
                         targetConnection = new Socket(
-                                serverImpl.getAddress().getAddress(),
+                                InetAddress.getLoopbackAddress(),
                                 serverImpl.getAddress().getPort());
 
                         // Then send the 200 OK response to the client
@@ -334,26 +344,45 @@
                         pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
                         pw.flush();
                     } else {
-                        // This should not happen.
-                        throw new IOException("Tunnel: Unexpected status line: "
-                                           + requestLine);
+                        // This should not happen. If it does then just print an
+                        // error - both on out and err, and close the accepted
+                        // socket
+                        System.out.println("WARNING: Tunnel: Unexpected status line: "
+                                + requestLine + " received by "
+                                + ss.getLocalSocketAddress()
+                                + " from "
+                                + toClose.getRemoteSocketAddress()
+                                + " - closing accepted socket");
+                        // Print on err
+                        System.err.println("WARNING: Tunnel: Unexpected status line: "
+                                + requestLine + " received by "
+                                + ss.getLocalSocketAddress()
+                                + " from "
+                                + toClose.getRemoteSocketAddress());
+                        // close accepted socket.
+                        toClose.close();
+                        System.err.println("Tunnel: accepted socket closed.");
+                        continue;
                     }
 
                     // Pipe the input stream of the client connection to the
                     // output stream of the target connection and conversely.
                     // Now the client and target will just talk to each other.
                     System.out.println("Tunnel: Starting tunnel pipes");
-                    Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+');
-                    Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-');
+                    CompletableFuture<Void> end, end1, end2;
+                    Thread t1 = pipe(ccis, targetConnection.getOutputStream(), '+',
+                            end1 = new CompletableFuture<>());
+                    Thread t2 = pipe(targetConnection.getInputStream(), ccos, '-',
+                            end2 = new CompletableFuture<>());
+                    end = CompletableFuture.allOf(end1, end2);
+                    end.whenComplete(
+                            (r,t) -> {
+                                try { toClose.close(); } catch (IOException x) { }
+                                finally {connectionCFs.remove(end);}
+                            });
+                    connectionCFs.add(end);
                     t1.start();
                     t2.start();
-
-                    // We have only 1 client... wait until it has finished before
-                    // accepting a new connection request.
-                    // System.out.println("Tunnel: Waiting for pipes to close");
-                    // t1.join();
-                    // t2.join();
-                    System.out.println("Tunnel: Done - waiting for next client");
                 }
             } catch (Throwable ex) {
                 try {
@@ -362,10 +391,14 @@
                     ex.addSuppressed(ex1);
                 }
                 ex.printStackTrace(System.err);
+            } finally {
+                System.out.println("Tunnel: exiting (stopped=" + stopped + ")");
+                connectionCFs.forEach(cf -> cf.complete(null));
             }
         }
 
-        void stop() throws IOException {
+        public void stop() throws IOException {
+            stopped = true;
             ss.close();
         }