test/jdk/java/net/httpclient/DigestEchoServer.java
branchhttp-client-branch
changeset 56128 249a863b0aca
parent 56104 3420c1bdd254
child 56136 3b58e5bacad6
equal deleted inserted replaced
56127:e2a780d8c6f0 56128:249a863b0aca
    22  * or visit www.oracle.com if you need additional information or have any
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    23  * questions.
    24  */
    24  */
    25 
    25 
    26 import com.sun.net.httpserver.BasicAuthenticator;
    26 import com.sun.net.httpserver.BasicAuthenticator;
    27 import com.sun.net.httpserver.Filter;
       
    28 import com.sun.net.httpserver.Headers;
       
    29 import com.sun.net.httpserver.HttpContext;
       
    30 import com.sun.net.httpserver.HttpExchange;
       
    31 import com.sun.net.httpserver.HttpHandler;
       
    32 import com.sun.net.httpserver.HttpServer;
    27 import com.sun.net.httpserver.HttpServer;
    33 import com.sun.net.httpserver.HttpsConfigurator;
    28 import com.sun.net.httpserver.HttpsConfigurator;
    34 import com.sun.net.httpserver.HttpsParameters;
    29 import com.sun.net.httpserver.HttpsParameters;
    35 import com.sun.net.httpserver.HttpsServer;
    30 import com.sun.net.httpserver.HttpsServer;
    36 import java.io.IOException;
    31 import java.io.IOException;
    40 import java.io.PrintWriter;
    35 import java.io.PrintWriter;
    41 import java.io.Writer;
    36 import java.io.Writer;
    42 import java.math.BigInteger;
    37 import java.math.BigInteger;
    43 import java.net.Authenticator;
    38 import java.net.Authenticator;
    44 import java.net.HttpURLConnection;
    39 import java.net.HttpURLConnection;
    45 import java.net.InetAddress;
       
    46 import java.net.InetSocketAddress;
    40 import java.net.InetSocketAddress;
    47 import java.net.MalformedURLException;
    41 import java.net.MalformedURLException;
    48 import java.net.PasswordAuthentication;
    42 import java.net.PasswordAuthentication;
    49 import java.net.ServerSocket;
    43 import java.net.ServerSocket;
    50 import java.net.Socket;
    44 import java.net.Socket;
    51 import java.net.SocketAddress;
       
    52 import java.net.URI;
    45 import java.net.URI;
    53 import java.net.URISyntaxException;
    46 import java.net.URISyntaxException;
    54 import java.net.URL;
    47 import java.net.URL;
    55 import java.nio.charset.StandardCharsets;
    48 import java.nio.charset.StandardCharsets;
    56 import java.security.MessageDigest;
    49 import java.security.MessageDigest;
    62 import java.util.List;
    55 import java.util.List;
    63 import java.util.Locale;
    56 import java.util.Locale;
    64 import java.util.Objects;
    57 import java.util.Objects;
    65 import java.util.Optional;
    58 import java.util.Optional;
    66 import java.util.Random;
    59 import java.util.Random;
       
    60 import java.util.StringTokenizer;
    67 import java.util.concurrent.CompletableFuture;
    61 import java.util.concurrent.CompletableFuture;
    68 import java.util.concurrent.CopyOnWriteArrayList;
    62 import java.util.concurrent.CopyOnWriteArrayList;
    69 import java.util.concurrent.atomic.AtomicInteger;
    63 import java.util.concurrent.atomic.AtomicInteger;
    70 import java.util.stream.Collectors;
    64 import java.util.stream.Collectors;
    71 import java.util.stream.Stream;
    65 import java.util.stream.Stream;
    78  * By default this server will echo back whatever is present
    72  * By default this server will echo back whatever is present
    79  * in the request body. Note that the Digest authentication is
    73  * in the request body. Note that the Digest authentication is
    80  * a test implementation implemented only for tests purposes.
    74  * a test implementation implemented only for tests purposes.
    81  * @author danielfuchs
    75  * @author danielfuchs
    82  */
    76  */
    83 public class DigestEchoServer implements HttpServerAdapters {
    77 public abstract class DigestEchoServer implements HttpServerAdapters {
    84 
    78 
    85     public static final boolean DEBUG =
    79     public static final boolean DEBUG =
    86             Boolean.parseBoolean(System.getProperty("test.debug", "false"));
    80             Boolean.parseBoolean(System.getProperty("test.debug", "false"));
    87     public enum HttpAuthType {
    81     public enum HttpAuthType {
    88         SERVER, PROXY, SERVER307, PROXY305
    82         SERVER, PROXY, SERVER307, PROXY305
   145     final HttpTestServer       serverImpl; // this server endpoint
   139     final HttpTestServer       serverImpl; // this server endpoint
   146     final DigestEchoServer     redirect;   // the target server where to redirect 3xx
   140     final DigestEchoServer     redirect;   // the target server where to redirect 3xx
   147     final HttpTestHandler      delegate;   // unused
   141     final HttpTestHandler      delegate;   // unused
   148     final String               key;
   142     final String               key;
   149 
   143 
   150     private DigestEchoServer(String key,
   144     DigestEchoServer(String key,
   151                              HttpTestServer server,
   145                              HttpTestServer server,
   152                              DigestEchoServer target,
   146                              DigestEchoServer target,
   153                              HttpTestHandler delegate) {
   147                              HttpTestHandler delegate) {
   154         this.key = key;
   148         this.key = key;
   155         this.serverImpl = server;
   149         this.serverImpl = server;
   503         HttpTestServer impl = createHttpServer(version, protocol);
   497         HttpTestServer impl = createHttpServer(version, protocol);
   504         String key = String.format("DigestEchoServer[PID=%s,PORT=%s]:%s:%s:%s:%s",
   498         String key = String.format("DigestEchoServer[PID=%s,PORT=%s]:%s:%s:%s:%s",
   505                 ProcessHandle.current().pid(),
   499                 ProcessHandle.current().pid(),
   506                 impl.getAddress().getPort(),
   500                 impl.getAddress().getPort(),
   507                 version, protocol, authType, schemeType);
   501                 version, protocol, authType, schemeType);
   508         final DigestEchoServer server = new DigestEchoServer(key, impl, null, delegate);
   502         final DigestEchoServer server = new DigestEchoServerImpl(key, impl, null, delegate);
   509         final HttpTestHandler handler =
   503         final HttpTestHandler handler =
   510                 server.createHandler(schemeType, auth, authType, false);
   504                 server.createHandler(schemeType, auth, authType, false);
   511         HttpTestContext context = impl.addHandler(handler, path);
   505         HttpTestContext context = impl.addHandler(handler, path);
   512         server.configureAuthentication(context, schemeType, auth, authType);
   506         server.configureAuthentication(context, schemeType, auth, authType);
   513         impl.start();
   507         impl.start();
   534                 ProcessHandle.current().pid(),
   528                 ProcessHandle.current().pid(),
   535                 impl.getAddress().getPort(),
   529                 impl.getAddress().getPort(),
   536                 version, protocol, authType, schemeType);
   530                 version, protocol, authType, schemeType);
   537         final DigestEchoServer server = "https".equalsIgnoreCase(protocol)
   531         final DigestEchoServer server = "https".equalsIgnoreCase(protocol)
   538                 ? new HttpsProxyTunnel(key, impl, null, delegate)
   532                 ? new HttpsProxyTunnel(key, impl, null, delegate)
   539                 : new DigestEchoServer(key, impl, null, delegate);
   533                 : new DigestEchoServerImpl(key, impl, null, delegate);
   540 
   534 
   541         final HttpTestHandler hh = server.createHandler(HttpAuthSchemeType.NONE,
   535         final HttpTestHandler hh = server.createHandler(HttpAuthSchemeType.NONE,
   542                                          null, HttpAuthType.SERVER,
   536                                          null, HttpAuthType.SERVER,
   543                                          server instanceof HttpsProxyTunnel);
   537                                          server instanceof HttpsProxyTunnel);
   544         HttpTestContext ctxt = impl.addHandler(hh, path);
   538         HttpTestContext ctxt = impl.addHandler(hh, path);
   578                 impl.getAddress().getPort(),
   572                 impl.getAddress().getPort(),
   579                 version, protocol,
   573                 version, protocol,
   580                 HttpAuthType.SERVER, code300)
   574                 HttpAuthType.SERVER, code300)
   581                 + "->" + redirectTarget.key;
   575                 + "->" + redirectTarget.key;
   582         final DigestEchoServer redirectingServer =
   576         final DigestEchoServer redirectingServer =
   583                  new DigestEchoServer(key, impl, redirectTarget, null);
   577                  new DigestEchoServerImpl(key, impl, redirectTarget, null);
   584         InetSocketAddress redirectAddr = redirectTarget.getAddress();
   578         InetSocketAddress redirectAddr = redirectTarget.getAddress();
   585         URL locationURL = url(targetProtocol, redirectAddr, "/");
   579         URL locationURL = url(targetProtocol, redirectAddr, "/");
   586         final HttpTestHandler hh = redirectingServer.create300Handler(key, locationURL,
   580         final HttpTestHandler hh = redirectingServer.create300Handler(key, locationURL,
   587                                              HttpAuthType.SERVER, code300);
   581                                              HttpAuthType.SERVER, code300);
   588         impl.addHandler(hh,"/");
   582         impl.addHandler(hh,"/");
   589         impl.start();
   583         impl.start();
   590         return redirectingServer;
   584         return redirectingServer;
   591     }
   585     }
   592 
   586 
   593     public InetSocketAddress getAddress() {
   587     public abstract InetSocketAddress getServerAddress();
   594         return new InetSocketAddress("127.0.0.1",
   588     public abstract InetSocketAddress getProxyAddress();
   595                 serverImpl.getAddress().getPort());
   589     public abstract InetSocketAddress getAddress();
   596     }
   590     public abstract void stop();
   597 
   591     public abstract Version getServerVersion();
   598     public InetSocketAddress getServerAddress() {
   592 
   599         return new InetSocketAddress("127.0.0.1",
   593     private static class DigestEchoServerImpl extends DigestEchoServer {
   600                 serverImpl.getAddress().getPort());
   594         DigestEchoServerImpl(String key,
   601     }
   595                              HttpTestServer server,
   602 
   596                              DigestEchoServer target,
   603     public InetSocketAddress getProxyAddress() {
   597                              HttpTestHandler delegate) {
   604         return new InetSocketAddress("127.0.0.1",
   598             super(key, Objects.requireNonNull(server), target, delegate);
   605                 serverImpl.getAddress().getPort());
   599         }
   606     }
   600 
   607 
   601         public InetSocketAddress getAddress() {
   608     public Version getServerVersion() { return serverImpl.getVersion(); }
   602             return new InetSocketAddress("127.0.0.1",
   609 
   603                     serverImpl.getAddress().getPort());
   610     public void stop() {
   604         }
   611         serverImpl.stop();
   605 
   612         if (redirect != null) {
   606         public InetSocketAddress getServerAddress() {
   613             redirect.stop();
   607             return new InetSocketAddress("127.0.0.1",
       
   608                     serverImpl.getAddress().getPort());
       
   609         }
       
   610 
       
   611         public InetSocketAddress getProxyAddress() {
       
   612             return new InetSocketAddress("127.0.0.1",
       
   613                     serverImpl.getAddress().getPort());
       
   614         }
       
   615 
       
   616         public Version getServerVersion() {
       
   617             return serverImpl.getVersion();
       
   618         }
       
   619 
       
   620         public void stop() {
       
   621             serverImpl.stop();
       
   622             if (redirect != null) {
       
   623                 redirect.stop();
       
   624             }
   614         }
   625         }
   615     }
   626     }
   616 
   627 
   617     protected void writeResponse(HttpTestExchange he) throws IOException {
   628     protected void writeResponse(HttpTestExchange he) throws IOException {
   618         if (delegate == null) {
   629         if (delegate == null) {
  1418             response.append("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
  1429             response.append("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n");
  1419             return true;
  1430             return true;
  1420         }
  1431         }
  1421     }
  1432     }
  1422 
  1433 
       
  1434     public interface TunnelingProxy {
       
  1435         InetSocketAddress getProxyAddress();
       
  1436         void stop();
       
  1437     }
       
  1438 
  1423     // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden
  1439     // This is a bit hacky: HttpsProxyTunnel is an HTTPTestServer hidden
  1424     // behind a fake proxy that only understands CONNECT requests.
  1440     // behind a fake proxy that only understands CONNECT requests.
  1425     // The fake proxy is just a server socket that intercept the
  1441     // The fake proxy is just a server socket that intercept the
  1426     // CONNECT and then redirect streams to the real server.
  1442     // CONNECT and then redirect streams to the real server.
  1427     static class HttpsProxyTunnel extends DigestEchoServer
  1443     static class HttpsProxyTunnel extends DigestEchoServer
  1428             implements Runnable {
  1444             implements Runnable, TunnelingProxy {
  1429 
  1445 
  1430         final ServerSocket ss;
  1446         final ServerSocket ss;
  1431         final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs
  1447         final CopyOnWriteArrayList<CompletableFuture<Void>> connectionCFs
  1432                 = new CopyOnWriteArrayList<>();
  1448                 = new CopyOnWriteArrayList<>();
  1433         volatile ProxyAuthorization authorization;
  1449         volatile ProxyAuthorization authorization;
  1453             t.setDaemon(true);
  1469             t.setDaemon(true);
  1454             t.start();
  1470             t.start();
  1455         }
  1471         }
  1456 
  1472 
  1457         @Override
  1473         @Override
       
  1474         public Version getServerVersion() {
       
  1475             // serverImpl is not null when this proxy
       
  1476             // serves a single server. It will be null
       
  1477             // if this proxy can serve multiple servers.
       
  1478             if (serverImpl != null) return serverImpl.getVersion();
       
  1479             return null;
       
  1480         }
       
  1481 
       
  1482         @Override
  1458         public void stop() {
  1483         public void stop() {
  1459             stopped = true;
  1484             stopped = true;
  1460             super.stop();
  1485             if (serverImpl != null) {
       
  1486                 serverImpl.stop();
       
  1487             }
       
  1488             if (redirect != null) {
       
  1489                 redirect.stop();
       
  1490             }
  1461             try {
  1491             try {
  1462                 ss.close();
  1492                 ss.close();
  1463             } catch (IOException ex) {
  1493             } catch (IOException ex) {
  1464                 if (DEBUG) ex.printStackTrace(System.out);
  1494                 if (DEBUG) ex.printStackTrace(System.out);
  1465             }
  1495             }
  1514             };
  1544             };
  1515         }
  1545         }
  1516 
  1546 
  1517         @Override
  1547         @Override
  1518         public InetSocketAddress getAddress() {
  1548         public InetSocketAddress getAddress() {
  1519             return new InetSocketAddress(ss.getInetAddress(), ss.getLocalPort());
  1549             return new InetSocketAddress("127.0.0.1",
  1520         }
  1550                     ss.getLocalPort());
       
  1551         }
       
  1552         @Override
  1521         public InetSocketAddress getProxyAddress() {
  1553         public InetSocketAddress getProxyAddress() {
  1522             return getAddress();
  1554             return getAddress();
  1523         }
  1555         }
       
  1556         @Override
  1524         public InetSocketAddress getServerAddress() {
  1557         public InetSocketAddress getServerAddress() {
  1525             return new InetSocketAddress("127.0.0.1",
  1558             // serverImpl can be null if this proxy can serve
  1526                     serverImpl.getAddress().getPort());
  1559             // multiple servers.
       
  1560             if (serverImpl != null) {
       
  1561                 return serverImpl.getAddress();
       
  1562             }
       
  1563             return null;
  1527         }
  1564         }
  1528 
  1565 
  1529 
  1566 
  1530         // This is a bit shaky. It doesn't handle continuation
  1567         // This is a bit shaky. It doesn't handle continuation
  1531         // lines, but our client shouldn't send any.
  1568         // lines, but our client shouldn't send any.
  1572                     System.out.println(now() + "Tunnel: Request line: " + requestLine);
  1609                     System.out.println(now() + "Tunnel: Request line: " + requestLine);
  1573                     if (requestLine.startsWith("CONNECT ")) {
  1610                     if (requestLine.startsWith("CONNECT ")) {
  1574                         // We should probably check that the next word following
  1611                         // We should probably check that the next word following
  1575                         // CONNECT is the host:port of our HTTPS serverImpl.
  1612                         // CONNECT is the host:port of our HTTPS serverImpl.
  1576                         // Some improvement for a followup!
  1613                         // Some improvement for a followup!
       
  1614                         StringTokenizer tokenizer = new StringTokenizer(requestLine);
       
  1615                         String connect = tokenizer.nextToken();
       
  1616                         assert connect.equalsIgnoreCase("connect");
       
  1617                         String hostport = tokenizer.nextToken();
       
  1618                         InetSocketAddress targetAddress;
       
  1619                         try {
       
  1620                             URI uri = new URI("https", hostport, "/", null, null);
       
  1621                             int port = uri.getPort();
       
  1622                             port = port == -1 ? 443 : port;
       
  1623                             targetAddress = new InetSocketAddress(uri.getHost(), port);
       
  1624                             if (serverImpl != null) {
       
  1625                                 assert targetAddress.getHostString()
       
  1626                                         .equalsIgnoreCase(serverImpl.getAddress().getHostString());
       
  1627                                 assert targetAddress.getPort() == serverImpl.getAddress().getPort();
       
  1628                             }
       
  1629                         } catch (Throwable x) {
       
  1630                             System.err.printf("Bad target address: \"%s\" in \"%s\"%n",
       
  1631                                     hostport, requestLine);
       
  1632                             toClose.close();
       
  1633                             continue;
       
  1634                         }
  1577 
  1635 
  1578                         // Read all headers until we find the empty line that
  1636                         // Read all headers until we find the empty line that
  1579                         // signals the end of all headers.
  1637                         // signals the end of all headers.
  1580                         String line = requestLine;
  1638                         String line = requestLine;
  1581                         while(!line.equals("")) {
  1639                         while(!line.equals("")) {
  1593                             pw.print(response.toString());
  1651                             pw.print(response.toString());
  1594                             pw.flush();
  1652                             pw.flush();
  1595                             toClose.close();
  1653                             toClose.close();
  1596                             continue;
  1654                             continue;
  1597                         }
  1655                         }
       
  1656                         System.out.println(now()
       
  1657                                 + "Tunnel connecting to target server at "
       
  1658                                 + targetAddress.getAddress() + ":" + targetAddress.getPort());
  1598                         targetConnection = new Socket(
  1659                         targetConnection = new Socket(
  1599                                 serverImpl.getAddress().getAddress(),
  1660                                 targetAddress.getAddress(),
  1600                                 serverImpl.getAddress().getPort());
  1661                                 targetAddress.getPort());
  1601 
  1662 
  1602                         // Then send the 200 OK response to the client
  1663                         // Then send the 200 OK response to the client
  1603                         System.out.println(now() + "Tunnel: Sending "
  1664                         System.out.println(now() + "Tunnel: Sending "
  1604                                            + response);
  1665                                            + response);
  1605                         pw.print(response);
  1666                         pw.print(response);
  1657                 connectionCFs.forEach(cf -> cf.complete(null));
  1718                 connectionCFs.forEach(cf -> cf.complete(null));
  1658             }
  1719             }
  1659         }
  1720         }
  1660     }
  1721     }
  1661 
  1722 
       
  1723     /**
       
  1724      * Creates a TunnelingProxy that can serve multiple servers.
       
  1725      * The server address is extracted from the CONNECT request line.
       
  1726      * @param authScheme The authentication scheme supported by the proxy.
       
  1727      *                   Typically one of DIGEST, BASIC, NONE.
       
  1728      * @return A new TunnelingProxy able to serve multiple servers.
       
  1729      * @throws IOException If the proxy could not be created.
       
  1730      */
       
  1731     public static TunnelingProxy createHttpsProxyTunnel(HttpAuthSchemeType authScheme)
       
  1732             throws IOException {
       
  1733         HttpsProxyTunnel result = new HttpsProxyTunnel("", null, null, null);
       
  1734         if (authScheme != HttpAuthSchemeType.NONE) {
       
  1735             result.configureAuthentication(null,
       
  1736                                            authScheme,
       
  1737                                            AUTHENTICATOR,
       
  1738                                            HttpAuthType.PROXY);
       
  1739         }
       
  1740         return result;
       
  1741     }
       
  1742 
  1662     private static String protocol(String protocol) {
  1743     private static String protocol(String protocol) {
  1663         if ("http".equalsIgnoreCase(protocol)) return "http";
  1744         if ("http".equalsIgnoreCase(protocol)) return "http";
  1664         else if ("https".equalsIgnoreCase(protocol)) return "https";
  1745         else if ("https".equalsIgnoreCase(protocol)) return "https";
  1665         else throw new InternalError("Unsupported protocol: " + protocol);
  1746         else throw new InternalError("Unsupported protocol: " + protocol);
  1666     }
  1747     }