test/jdk/java/net/httpclient/DigestEchoServer.java
branchhttp-client-branch
changeset 56128 249a863b0aca
parent 56104 3420c1bdd254
child 56136 3b58e5bacad6
--- a/test/jdk/java/net/httpclient/DigestEchoServer.java	Wed Feb 14 17:02:56 2018 +0000
+++ b/test/jdk/java/net/httpclient/DigestEchoServer.java	Wed Feb 14 17:35:42 2018 +0000
@@ -24,11 +24,6 @@
  */
 
 import com.sun.net.httpserver.BasicAuthenticator;
-import com.sun.net.httpserver.Filter;
-import com.sun.net.httpserver.Headers;
-import com.sun.net.httpserver.HttpContext;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
 import com.sun.net.httpserver.HttpServer;
 import com.sun.net.httpserver.HttpsConfigurator;
 import com.sun.net.httpserver.HttpsParameters;
@@ -42,13 +37,11 @@
 import java.math.BigInteger;
 import java.net.Authenticator;
 import java.net.HttpURLConnection;
-import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.MalformedURLException;
 import java.net.PasswordAuthentication;
 import java.net.ServerSocket;
 import java.net.Socket;
-import java.net.SocketAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
@@ -64,6 +57,7 @@
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Random;
+import java.util.StringTokenizer;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -80,7 +74,7 @@
  * a test implementation implemented only for tests purposes.
  * @author danielfuchs
  */
-public class DigestEchoServer implements HttpServerAdapters {
+public abstract class DigestEchoServer implements HttpServerAdapters {
 
     public static final boolean DEBUG =
             Boolean.parseBoolean(System.getProperty("test.debug", "false"));
@@ -147,7 +141,7 @@
     final HttpTestHandler      delegate;   // unused
     final String               key;
 
-    private DigestEchoServer(String key,
+    DigestEchoServer(String key,
                              HttpTestServer server,
                              DigestEchoServer target,
                              HttpTestHandler delegate) {
@@ -505,7 +499,7 @@
                 ProcessHandle.current().pid(),
                 impl.getAddress().getPort(),
                 version, protocol, authType, schemeType);
-        final DigestEchoServer server = new DigestEchoServer(key, impl, null, delegate);
+        final DigestEchoServer server = new DigestEchoServerImpl(key, impl, null, delegate);
         final HttpTestHandler handler =
                 server.createHandler(schemeType, auth, authType, false);
         HttpTestContext context = impl.addHandler(handler, path);
@@ -536,7 +530,7 @@
                 version, protocol, authType, schemeType);
         final DigestEchoServer server = "https".equalsIgnoreCase(protocol)
                 ? new HttpsProxyTunnel(key, impl, null, delegate)
-                : new DigestEchoServer(key, impl, null, delegate);
+                : new DigestEchoServerImpl(key, impl, null, delegate);
 
         final HttpTestHandler hh = server.createHandler(HttpAuthSchemeType.NONE,
                                          null, HttpAuthType.SERVER,
@@ -580,7 +574,7 @@
                 HttpAuthType.SERVER, code300)
                 + "->" + redirectTarget.key;
         final DigestEchoServer redirectingServer =
-                 new DigestEchoServer(key, impl, redirectTarget, null);
+                 new DigestEchoServerImpl(key, impl, redirectTarget, null);
         InetSocketAddress redirectAddr = redirectTarget.getAddress();
         URL locationURL = url(targetProtocol, redirectAddr, "/");
         final HttpTestHandler hh = redirectingServer.create300Handler(key, locationURL,
@@ -590,27 +584,44 @@
         return redirectingServer;
     }
 
-    public InetSocketAddress getAddress() {
-        return new InetSocketAddress("127.0.0.1",
-                serverImpl.getAddress().getPort());
-    }
+    public abstract InetSocketAddress getServerAddress();
+    public abstract InetSocketAddress getProxyAddress();
+    public abstract InetSocketAddress getAddress();
+    public abstract void stop();
+    public abstract Version getServerVersion();
 
-    public InetSocketAddress getServerAddress() {
-        return new InetSocketAddress("127.0.0.1",
-                serverImpl.getAddress().getPort());
-    }
+    private static class DigestEchoServerImpl extends DigestEchoServer {
+        DigestEchoServerImpl(String key,
+                             HttpTestServer server,
+                             DigestEchoServer target,
+                             HttpTestHandler delegate) {
+            super(key, Objects.requireNonNull(server), target, delegate);
+        }
+
+        public InetSocketAddress getAddress() {
+            return new InetSocketAddress("127.0.0.1",
+                    serverImpl.getAddress().getPort());
+        }
 
-    public InetSocketAddress getProxyAddress() {
-        return new InetSocketAddress("127.0.0.1",
-                serverImpl.getAddress().getPort());
-    }
+        public InetSocketAddress getServerAddress() {
+            return new InetSocketAddress("127.0.0.1",
+                    serverImpl.getAddress().getPort());
+        }
+
+        public InetSocketAddress getProxyAddress() {
+            return new InetSocketAddress("127.0.0.1",
+                    serverImpl.getAddress().getPort());
+        }
 
-    public Version getServerVersion() { return serverImpl.getVersion(); }
+        public Version getServerVersion() {
+            return serverImpl.getVersion();
+        }
 
-    public void stop() {
-        serverImpl.stop();
-        if (redirect != null) {
-            redirect.stop();
+        public void stop() {
+            serverImpl.stop();
+            if (redirect != null) {
+                redirect.stop();
+            }
         }
     }
 
@@ -1420,12 +1431,17 @@
         }
     }
 
+    public interface TunnelingProxy {
+        InetSocketAddress getProxyAddress();
+        void stop();
+    }
+
     // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden
     // behind a fake proxy that only understands CONNECT requests.
     // The fake proxy is just a server socket that intercept the
     // CONNECT and then redirect streams to the real server.
     static class HttpsProxyTunnel extends DigestEchoServer
-            implements Runnable {
+            implements Runnable, TunnelingProxy {
 
         final ServerSocket ss;
         final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs
@@ -1455,9 +1471,23 @@
         }
 
         @Override
+        public Version getServerVersion() {
+            // serverImpl is not null when this proxy
+            // serves a single server. It will be null
+            // if this proxy can serve multiple servers.
+            if (serverImpl != null) return serverImpl.getVersion();
+            return null;
+        }
+
+        @Override
         public void stop() {
             stopped = true;
-            super.stop();
+            if (serverImpl != null) {
+                serverImpl.stop();
+            }
+            if (redirect != null) {
+                redirect.stop();
+            }
             try {
                 ss.close();
             } catch (IOException ex) {
@@ -1516,14 +1546,21 @@
 
         @Override
         public InetSocketAddress getAddress() {
-            return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort());
+            return new InetSocketAddress("127.0.0.1",
+                    ss.getLocalPort());
         }
+        @Override
         public InetSocketAddress getProxyAddress() {
             return getAddress();
         }
+        @Override
         public InetSocketAddress getServerAddress() {
-            return new InetSocketAddress("127.0.0.1",
-                    serverImpl.getAddress().getPort());
+            // serverImpl can be null if this proxy can serve
+            // multiple servers.
+            if (serverImpl != null) {
+                return serverImpl.getAddress();
+            }
+            return null;
         }
 
 
@@ -1574,6 +1611,27 @@
                         // We should probably check that the next word following
                         // CONNECT is the host:port of our HTTPS serverImpl.
                         // Some improvement for a followup!
+                        StringTokenizer tokenizer = new StringTokenizer(requestLine);
+                        String connect = tokenizer.nextToken();
+                        assert connect.equalsIgnoreCase("connect");
+                        String hostport = tokenizer.nextToken();
+                        InetSocketAddress targetAddress;
+                        try {
+                            URI uri = new URI("https", hostport, "/", null, null);
+                            int port = uri.getPort();
+                            port = port == -1 ? 443 : port;
+                            targetAddress = new InetSocketAddress(uri.getHost(), port);
+                            if (serverImpl != null) {
+                                assert targetAddress.getHostString()
+                                        .equalsIgnoreCase(serverImpl.getAddress().getHostString());
+                                assert targetAddress.getPort() == serverImpl.getAddress().getPort();
+                            }
+                        } catch (Throwable x) {
+                            System.err.printf("Bad target address: \"%s\" in \"%s\"%n",
+                                    hostport, requestLine);
+                            toClose.close();
+                            continue;
+                        }
 
                         // Read all headers until we find the empty line that
                         // signals the end of all headers.
@@ -1595,9 +1653,12 @@
                             toClose.close();
                             continue;
                         }
+                        System.out.println(now()
+                                + "Tunnel connecting to target server at "
+                                + targetAddress.getAddress() + ":" + targetAddress.getPort());
                         targetConnection = new Socket(
-                                serverImpl.getAddress().getAddress(),
-                                serverImpl.getAddress().getPort());
+                                targetAddress.getAddress(),
+                                targetAddress.getPort());
 
                         // Then send the 200 OK response to the client
                         System.out.println(now() + "Tunnel: Sending "
@@ -1659,6 +1720,26 @@
         }
     }
 
+    /**
+     * Creates a TunnelingProxy that can serve multiple servers.
+     * The server address is extracted from the CONNECT request line.
+     * @param authScheme The authentication scheme supported by the proxy.
+     *                   Typically one of DIGEST, BASIC, NONE.
+     * @return A new TunnelingProxy able to serve multiple servers.
+     * @throws IOException If the proxy could not be created.
+     */
+    public static TunnelingProxy createHttpsProxyTunnel(HttpAuthSchemeType authScheme)
+            throws IOException {
+        HttpsProxyTunnel result = new HttpsProxyTunnel("", null, null, null);
+        if (authScheme != HttpAuthSchemeType.NONE) {
+            result.configureAuthentication(null,
+                                           authScheme,
+                                           AUTHENTICATOR,
+                                           HttpAuthType.PROXY);
+        }
+        return result;
+    }
+
     private static String protocol(String protocol) {
         if ("http".equalsIgnoreCase(protocol)) return "http";
         else if ("https".equalsIgnoreCase(protocol)) return "https";