test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java
branchdatagramsocketimpl-branch
changeset 58678 9cf78a70fa4f
parent 54086 ccb4a50bee06
child 58679 9c3209ff7550
--- a/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java	Thu Oct 17 20:27:44 2019 +0100
+++ b/test/jdk/java/net/HttpURLConnection/SetAuthenticator/HTTPTestServer.java	Thu Oct 17 20:53:35 2019 +0100
@@ -165,7 +165,7 @@
                 for (int i = 1; i <= max; i++) {
                     B bindable = createBindable();
                     SocketAddress address = getAddress(bindable);
-                    String key = address.toString();
+                    String key = toString(address);
                     if (addresses.addIfAbsent(key)) {
                        System.out.println("Socket bound to: " + key
                                           + " after " + i + " attempt(s)");
@@ -188,6 +188,16 @@
                                   + "addresses used before: " + addresses);
         }
 
+        private static String toString(SocketAddress address) {
+            // We don't rely on address.toString(): sometimes it can be
+            // "/127.0.0.1:port", sometimes it can be "localhost/127.0.0.1:port"
+            // Instead we compose our own string representation:
+            InetSocketAddress candidate = (InetSocketAddress) address;
+            String hostAddr = candidate.getAddress().getHostAddress();
+            if (hostAddr.contains(":")) hostAddr = "[" + hostAddr + "]";
+            return hostAddr + ":" + candidate.getPort();
+        }
+
         protected abstract B createBindable() throws IOException;
 
         protected abstract SocketAddress getAddress(B bindable);
@@ -391,6 +401,10 @@
         return serverImpl.getAddress();
     }
 
+    public InetSocketAddress getProxyAddress() {
+        return serverImpl.getAddress();
+    }
+
     public void stop() {
         serverImpl.stop(0);
         if (redirect != null) {
@@ -966,6 +980,8 @@
             implements Runnable {
 
         final ServerSocket ss;
+        private volatile boolean stop;
+
         public HttpsProxyTunnel(HttpServer server, HTTPTestServer target,
                                HttpHandler delegate)
                 throws IOException {
@@ -984,9 +1000,10 @@
 
         @Override
         public void stop() {
-            super.stop();
-            try {
-                ss.close();
+            try (var toClose = ss) {
+                stop = true;
+                System.out.println("Server " + ss + " stop requested");
+                super.stop();
             } catch (IOException ex) {
                 if (DEBUG) ex.printStackTrace(System.out);
             }
@@ -1019,7 +1036,7 @@
         }
 
         @Override
-        public InetSocketAddress getAddress() {
+        public InetSocketAddress getProxyAddress() {
             return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort());
         }
 
@@ -1036,6 +1053,9 @@
                 if (c == '\n') break;
                 b.appendCodePoint(c);
             }
+            if (b.length() == 0) {
+                return "";
+            }
             if (b.codePointAt(b.length() -1) == '\r') {
                 b.delete(b.length() -1, b.length());
             }
@@ -1045,80 +1065,121 @@
         @Override
         public void run() {
             Socket clientConnection = null;
-            try {
-                while (true) {
-                    System.out.println("Tunnel: Waiting for client");
-                    Socket previous = clientConnection;
+            while (!stop) {
+                System.out.println("Tunnel: Waiting for client at: " + ss);
+                final Socket previous = clientConnection;
+                try {
+                    clientConnection = ss.accept();
+                } catch (IOException io) {
                     try {
-                        clientConnection = ss.accept();
-                    } catch (IOException io) {
-                        if (DEBUG) io.printStackTrace(System.out);
-                        break;
-                    } finally {
-                        // close the previous connection
-                        if (previous != null) previous.close();
-                    }
-                    System.out.println("Tunnel: Client accepted");
-                    Socket targetConnection = null;
-                    InputStream  ccis = clientConnection.getInputStream();
-                    OutputStream ccos = clientConnection.getOutputStream();
-                    Writer w = new OutputStreamWriter(
-                                   clientConnection.getOutputStream(), "UTF-8");
-                    PrintWriter pw = new PrintWriter(w);
-                    System.out.println("Tunnel: Reading request line");
-                    String requestLine = readLine(ccis);
-                    System.out.println("Tunnel: Request line: " + requestLine);
-                    if (requestLine.startsWith("CONNECT ")) {
-                        // We should probably check that the next word following
-                        // CONNECT is the host:port of our HTTPS serverImpl.
-                        // Some improvement for a followup!
-
-                        // Read all headers until we find the empty line that
-                        // signals the end of all headers.
-                        while(!requestLine.equals("")) {
-                            System.out.println("Tunnel: Reading header: "
-                                               + (requestLine = readLine(ccis)));
+                        ss.close();
+                    } catch (IOException ex) {
+                        if (DEBUG) {
+                            ex.printStackTrace(System.out);
                         }
-
-                        targetConnection = new Socket(
-                                serverImpl.getAddress().getAddress(),
-                                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");
-                        pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
-                        pw.flush();
-                    } else {
-                        // This should not happen. If it does let our serverImpl
-                        // deal with it.
-                        throw new IOException("Tunnel: Unexpected status line: "
-                                             + requestLine);
+                    }
+                    // log the reason that caused the server to stop accepting connections
+                    if (!stop) {
+                        System.err.println("Server will stop accepting connections due to an exception:");
+                        io.printStackTrace();
+                    }
+                    break;
+                } finally {
+                    // close the previous connection
+                    if (previous != null) {
+                        try {
+                            previous.close();
+                        } catch (IOException e) {
+                            // ignore
+                            if (DEBUG) {
+                                System.out.println("Ignoring exception that happened while closing " +
+                                        "an older connection:");
+                                e.printStackTrace(System.out);
+                            }
+                        }
                     }
-
-                    // 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, '-');
-                    t1.start();
-                    t2.start();
-
-                    // We have only 1 client... wait until it has finished before
+                }
+                System.out.println("Tunnel: Client accepted");
+                try {
+                    // We have only 1 client... process the current client
+                    // request and wait until it has finished before
                     // accepting a new connection request.
-                    t1.join();
-                    t2.join();
+                    processRequestAndWaitToComplete(clientConnection);
+                } catch (IOException ioe) {
+                    // close the client connection
+                    try {
+                        clientConnection.close();
+                    } catch (IOException io) {
+                        // ignore
+                        if (DEBUG) {
+                            System.out.println("Ignoring exception that happened during client" +
+                                    " connection close:");
+                            io.printStackTrace(System.out);
+                        }
+                    } finally {
+                        clientConnection = null;
+                    }
+                } catch (Throwable t) {
+                    // don't close the client connection for non-IOExceptions, instead
+                    // just log it and move on to accept next connection
+                    if (!stop) {
+                        t.printStackTrace();
+                    }
                 }
-            } catch (Throwable ex) {
-                try {
-                    ss.close();
-                } catch (IOException ex1) {
-                    ex.addSuppressed(ex1);
-                }
-                ex.printStackTrace(System.err);
             }
         }
 
+        private void processRequestAndWaitToComplete(final Socket clientConnection)
+                throws IOException, InterruptedException {
+            final Socket targetConnection;
+            InputStream  ccis = clientConnection.getInputStream();
+            OutputStream ccos = clientConnection.getOutputStream();
+            Writer w = new OutputStreamWriter(
+                    clientConnection.getOutputStream(), "UTF-8");
+            PrintWriter pw = new PrintWriter(w);
+            System.out.println("Tunnel: Reading request line");
+            String requestLine = readLine(ccis);
+            System.out.println("Tunnel: Request line: " + requestLine);
+            if (requestLine.startsWith("CONNECT ")) {
+                // We should probably check that the next word following
+                // CONNECT is the host:port of our HTTPS serverImpl.
+                // Some improvement for a followup!
+
+                // Read all headers until we find the empty line that
+                // signals the end of all headers.
+                while(!requestLine.equals("")) {
+                    System.out.println("Tunnel: Reading header: "
+                            + (requestLine = readLine(ccis)));
+                }
+
+                targetConnection = new Socket(
+                        serverImpl.getAddress().getAddress(),
+                        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");
+                pw.print("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
+                pw.flush();
+            } else {
+                // This should not happen. If it does then consider it a
+                // client error and throw an IOException
+                System.out.println("Tunnel: Throwing an IOException due to unexpected" +
+                        " request line: " + requestLine);
+                throw new IOException("Client request error - Unexpected request line");
+            }
+
+            // 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, '-');
+            t1.start();
+            t2.start();
+            // wait for the request to complete
+            t1.join();
+            t2.join();
+        }
     }
 }