diff -r f7fd051519ac -r ee6f7a61f3a5 test/jdk/java/net/httpclient/ProxyTest.java --- 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 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> 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 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 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(); }