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