test/jdk/java/net/httpclient/http2/ProxyTest2.java
changeset 49765 ee6f7a61f3a5
parent 48083 b1c1b4ef4be2
child 52121 934969c63223
child 56451 9585061fdb04
--- a/test/jdk/java/net/httpclient/http2/ProxyTest2.java	Tue Apr 17 15:39:20 2018 +0200
+++ b/test/jdk/java/net/httpclient/http2/ProxyTest2.java	Tue Apr 17 08:54:17 2018 -0700
@@ -49,9 +49,9 @@
 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;
 import java.util.concurrent.*;
 
@@ -60,12 +60,12 @@
  * @bug 8181422
  * @summary  Verifies that you can access an HTTP/2 server over HTTPS by
  *           tunnelling through an HTTP/1.1 proxy.
- * @modules jdk.incubator.httpclient
+ * @modules java.net.http
  * @library /lib/testlibrary server
  * @modules java.base/sun.net.www.http
- *          jdk.incubator.httpclient/jdk.incubator.http.internal.common
- *          jdk.incubator.httpclient/jdk.incubator.http.internal.frame
- *          jdk.incubator.httpclient/jdk.incubator.http.internal.hpack
+ *          java.net.http/jdk.internal.net.http.common
+ *          java.net.http/jdk.internal.net.http.frame
+ *          java.net.http/jdk.internal.net.http.hpack
  * @build jdk.testlibrary.SimpleSSLContext ProxyTest2
  * @run main/othervm ProxyTest2
  * @author danielfuchs
@@ -146,7 +146,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);
@@ -165,19 +165,25 @@
         final ServerSocket ss;
         final boolean DEBUG = false;
         final Http2TestServer serverImpl;
+        final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs
+                = new CopyOnWriteArrayList<>();
+        private volatile boolean stopped;
         TunnelingProxy(Http2TestServer serverImpl) throws IOException {
             this.serverImpl = serverImpl;
             ss = new ServerSocket();
             accept = new Thread(this::accept);
+            accept.setDaemon(true);
         }
 
         void start() throws IOException {
+            ss.setReuseAddress(false);
             ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));
             accept.start();
         }
 
         // 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() {
@@ -197,13 +203,15 @@
                         }
                     } 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
@@ -228,18 +236,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;
@@ -259,40 +263,59 @@
                         // signals the end of all headers.
                         while(!requestLine.equals("")) {
                             System.out.println("Tunnel: Reading header: "
-                                               + (requestLine = readLine(ccis)));
+                                    + (requestLine = readLine(ccis)));
                         }
 
                         // Open target connection
                         targetConnection = new Socket(
-                                serverImpl.getAddress().getAddress(),
+                                InetAddress.getLoopbackAddress(),
                                 serverImpl.getAddress().getPort());
 
                         // Then send the 200 OK response to the client
                         System.out.println("Tunnel: Sending "
-                                           + "HTTP/1.1 200 OK\r\n\r\n");
+                                + "HTTP/1.1 200 OK\r\n\r\n");
                         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 {
@@ -301,10 +324,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();
         }