test/jdk/java/net/httpclient/HttpSlowServerTest.java
changeset 58649 6b6bf0de534b
equal deleted inserted replaced
58644:64597a6fd186 58649:6b6bf0de534b
       
     1 /*
       
     2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 import com.sun.net.httpserver.HttpServer;
       
    24 import com.sun.net.httpserver.HttpsConfigurator;
       
    25 import com.sun.net.httpserver.HttpsServer;
       
    26 import jdk.test.lib.net.SimpleSSLContext;
       
    27 
       
    28 import javax.net.ssl.SSLContext;
       
    29 import java.io.IOException;
       
    30 import java.io.InputStream;
       
    31 import java.io.OutputStream;
       
    32 import java.net.InetAddress;
       
    33 import java.net.InetSocketAddress;
       
    34 import java.net.Proxy;
       
    35 import java.net.ProxySelector;
       
    36 import java.net.SocketAddress;
       
    37 import java.net.URI;
       
    38 import java.net.http.HttpClient;
       
    39 import java.net.http.HttpRequest;
       
    40 import java.net.http.HttpResponse;
       
    41 import java.nio.charset.StandardCharsets;
       
    42 import java.time.Duration;
       
    43 import java.util.List;
       
    44 import java.util.Set;
       
    45 import java.util.concurrent.CompletableFuture;
       
    46 import java.util.concurrent.CopyOnWriteArrayList;
       
    47 import java.util.concurrent.CopyOnWriteArraySet;
       
    48 import java.util.concurrent.ExecutorService;
       
    49 import java.util.concurrent.LinkedBlockingQueue;
       
    50 import java.util.concurrent.ThreadPoolExecutor;
       
    51 import java.util.concurrent.TimeUnit;
       
    52 import java.util.concurrent.atomic.AtomicLong;
       
    53 
       
    54 /**
       
    55  * @test
       
    56  * @summary This test verifies that the HttpClient works correctly when connected to a
       
    57  *          slow server.
       
    58  * @library /test/lib http2/server
       
    59  * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer HttpSlowServerTest
       
    60  * @modules java.net.http/jdk.internal.net.http.common
       
    61  *          java.net.http/jdk.internal.net.http.frame
       
    62  *          java.net.http/jdk.internal.net.http.hpack
       
    63  *          java.logging
       
    64  *          java.base/sun.net.www.http
       
    65  *          java.base/sun.net.www
       
    66  *          java.base/sun.net
       
    67  * @run main/othervm -Dtest.requiresHost=true
       
    68  *                   -Djdk.httpclient.HttpClient.log=headers
       
    69  *                   -Djdk.internal.httpclient.debug=false
       
    70  *                   HttpSlowServerTest
       
    71  *
       
    72  */
       
    73 public class HttpSlowServerTest implements HttpServerAdapters {
       
    74     static final List<String> data = List.of(
       
    75             "Lorem ipsum",
       
    76             "dolor sit amet",
       
    77             "consectetur adipiscing elit, sed do eiusmod tempor",
       
    78             "quis nostrud exercitation ullamco",
       
    79             "laboris nisi",
       
    80             "ut",
       
    81             "aliquip ex ea commodo consequat.",
       
    82             "Duis aute irure dolor in reprehenderit in voluptate velit esse",
       
    83             "cillum dolore eu fugiat nulla pariatur.",
       
    84             "Excepteur sint occaecat cupidatat non proident."
       
    85     );
       
    86 
       
    87     static final SSLContext context;
       
    88     static {
       
    89         try {
       
    90             context = new SimpleSSLContext().get();
       
    91             SSLContext.setDefault(context);
       
    92         } catch (Exception x) {
       
    93             throw new ExceptionInInitializerError(x);
       
    94         }
       
    95     }
       
    96 
       
    97     final AtomicLong requestCounter = new AtomicLong();
       
    98     final AtomicLong responseCounter = new AtomicLong();
       
    99     HttpTestServer http1Server;
       
   100     HttpTestServer http2Server;
       
   101     HttpTestServer https1Server;
       
   102     HttpTestServer https2Server;
       
   103     DigestEchoServer.TunnelingProxy proxy;
       
   104 
       
   105     URI http1URI;
       
   106     URI https1URI;
       
   107     URI http2URI;
       
   108     URI https2URI;
       
   109     InetSocketAddress proxyAddress;
       
   110     ProxySelector proxySelector;
       
   111     HttpClient client;
       
   112     List<CompletableFuture<?>>  futures = new CopyOnWriteArrayList<>();
       
   113     Set<URI> pending = new CopyOnWriteArraySet<>();
       
   114 
       
   115     final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10,
       
   116             TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Shared by HTTP/1.1 servers
       
   117     final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1,
       
   118             TimeUnit.SECONDS, new LinkedBlockingQueue<>()); // Used by the client
       
   119 
       
   120     public HttpClient newHttpClient(ProxySelector ps) {
       
   121         HttpClient.Builder builder = HttpClient
       
   122                 .newBuilder()
       
   123                 .sslContext(context)
       
   124                 .executor(clientexec)
       
   125                 .proxy(ps);
       
   126         return builder.build();
       
   127     }
       
   128 
       
   129     public void setUp() throws Exception {
       
   130         try {
       
   131             InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
       
   132 
       
   133             // HTTP/1.1
       
   134             HttpServer server1 = HttpServer.create(sa, 0);
       
   135             server1.setExecutor(executor);
       
   136             http1Server = HttpTestServer.of(server1);
       
   137             http1Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/http1/");
       
   138             http1Server.start();
       
   139             http1URI = new URI("http://" + http1Server.serverAuthority() + "/HttpSlowServerTest/http1/");
       
   140 
       
   141 
       
   142             // HTTPS/1.1
       
   143             HttpsServer sserver1 = HttpsServer.create(sa, 100);
       
   144             sserver1.setExecutor(executor);
       
   145             sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
       
   146             https1Server = HttpTestServer.of(sserver1);
       
   147             https1Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/https1/");
       
   148             https1Server.start();
       
   149             https1URI = new URI("https://" + https1Server.serverAuthority() + "/HttpSlowServerTest/https1/");
       
   150 
       
   151             // HTTP/2.0
       
   152             http2Server = HttpTestServer.of(
       
   153                     new Http2TestServer("localhost", false, 0));
       
   154             http2Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/http2/");
       
   155             http2Server.start();
       
   156             http2URI = new URI("http://" + http2Server.serverAuthority() + "/HttpSlowServerTest/http2/");
       
   157 
       
   158             // HTTPS/2.0
       
   159             https2Server = HttpTestServer.of(
       
   160                     new Http2TestServer("localhost", true, 0));
       
   161             https2Server.addHandler(new HttpTestSlowHandler(), "/HttpSlowServerTest/https2/");
       
   162             https2Server.start();
       
   163             https2URI = new URI("https://" + https2Server.serverAuthority() + "/HttpSlowServerTest/https2/");
       
   164 
       
   165             proxy = DigestEchoServer.createHttpsProxyTunnel(
       
   166                     DigestEchoServer.HttpAuthSchemeType.NONE);
       
   167             proxyAddress = proxy.getProxyAddress();
       
   168             proxySelector = new HttpProxySelector(proxyAddress);
       
   169             client = newHttpClient(proxySelector);
       
   170             System.out.println("Setup: done");
       
   171         } catch (Exception x) {
       
   172             tearDown(); throw x;
       
   173         } catch (Error e) {
       
   174             tearDown(); throw e;
       
   175         }
       
   176     }
       
   177 
       
   178     public static void main(String[] args) throws Exception {
       
   179         HttpSlowServerTest test = new HttpSlowServerTest();
       
   180         test.setUp();
       
   181         long start = System.nanoTime();
       
   182         try {
       
   183             test.run(args);
       
   184         } finally {
       
   185             try {
       
   186                 long elapsed = System.nanoTime() - start;
       
   187                 System.out.println("*** Elapsed: " + Duration.ofNanos(elapsed));
       
   188             } finally {
       
   189                 test.tearDown();
       
   190             }
       
   191         }
       
   192     }
       
   193 
       
   194     public void run(String... args) throws Exception {
       
   195         List<URI> serverURIs = List.of(http1URI, http2URI, https1URI, https2URI);
       
   196         for (int i=0; i<20; i++) {
       
   197             for (URI base : serverURIs) {
       
   198                 if (base.getScheme().equalsIgnoreCase("https")) {
       
   199                     URI proxy = i % 1 == 0 ? base.resolve(URI.create("proxy/foo?n="+requestCounter.incrementAndGet()))
       
   200                     : base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
       
   201                     test(proxy);
       
   202                 }
       
   203             }
       
   204             for (URI base : serverURIs) {
       
   205                 URI direct = base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
       
   206                 test(direct);
       
   207             }
       
   208         }
       
   209         CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
       
   210     }
       
   211 
       
   212     public void test(URI uri) throws Exception {
       
   213         System.out.println("Testing with " + uri);
       
   214         pending.add(uri);
       
   215         HttpRequest request = HttpRequest.newBuilder(uri).build();
       
   216         CompletableFuture<HttpResponse<String>> resp =
       
   217                 client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
       
   218                 .whenComplete((r, t) -> this.requestCompleted(request, r, t));
       
   219         futures.add(resp);
       
   220     }
       
   221 
       
   222     private void requestCompleted(HttpRequest request, HttpResponse<?> r, Throwable t) {
       
   223         responseCounter.incrementAndGet();
       
   224         pending.remove(request.uri());
       
   225         System.out.println(request + " -> " + (t == null ? r : t)
       
   226                 + " [still pending: " + (requestCounter.get() - responseCounter.get()) +"]");
       
   227         if (pending.size() < 5 && requestCounter.get() > 100) {
       
   228             pending.forEach(u -> System.out.println("\tpending: " + u));
       
   229         }
       
   230     }
       
   231 
       
   232     public void tearDown() {
       
   233         proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
       
   234         http1Server = stop(http1Server, HttpTestServer::stop);
       
   235         https1Server = stop(https1Server, HttpTestServer::stop);
       
   236         http2Server = stop(http2Server, HttpTestServer::stop);
       
   237         https2Server = stop(https2Server, HttpTestServer::stop);
       
   238         client = null;
       
   239         try {
       
   240             executor.awaitTermination(2000, TimeUnit.MILLISECONDS);
       
   241         } catch (Throwable x) {
       
   242         } finally {
       
   243             executor.shutdownNow();
       
   244         }
       
   245         try {
       
   246             clientexec.awaitTermination(2000, TimeUnit.MILLISECONDS);
       
   247         } catch (Throwable x) {
       
   248         } finally {
       
   249             clientexec.shutdownNow();
       
   250         }
       
   251         System.out.println("Teardown: done");
       
   252     }
       
   253 
       
   254     private interface Stoppable<T> { public void stop(T service) throws Exception; }
       
   255 
       
   256     static <T>  T stop(T service, Stoppable<T> stop) {
       
   257         try { if (service != null) stop.stop(service); } catch (Throwable x) { };
       
   258         return null;
       
   259     }
       
   260 
       
   261     static class HttpProxySelector extends ProxySelector {
       
   262         private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
       
   263         private final List<Proxy> proxyList;
       
   264         HttpProxySelector(InetSocketAddress proxyAddress) {
       
   265             proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
       
   266         }
       
   267 
       
   268         @Override
       
   269         public List<Proxy> select(URI uri) {
       
   270             // our proxy only supports tunneling
       
   271             if (uri.getScheme().equalsIgnoreCase("https")) {
       
   272                 if (uri.getPath().contains("/proxy/")) {
       
   273                     return proxyList;
       
   274                 }
       
   275             }
       
   276             return NO_PROXY;
       
   277         }
       
   278 
       
   279         @Override
       
   280         public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
       
   281             System.err.println("Connection to proxy failed: " + ioe);
       
   282             System.err.println("Proxy: " + sa);
       
   283             System.err.println("\tURI: " + uri);
       
   284             ioe.printStackTrace();
       
   285         }
       
   286     }
       
   287 
       
   288     public static class HttpTestSlowHandler implements HttpTestHandler {
       
   289         static final AtomicLong respCounter = new AtomicLong();
       
   290         @Override
       
   291         public void handle(HttpTestExchange t) throws IOException {
       
   292             try (InputStream is = t.getRequestBody();
       
   293                  OutputStream os = t.getResponseBody()) {
       
   294                 byte[] bytes = is.readAllBytes();
       
   295                 assert bytes.length == 0;
       
   296                 URI u = t.getRequestURI();
       
   297                 long responseID = Long.parseLong(u.getQuery().substring(2));
       
   298                 System.out.println("Server " + t.getRequestURI() + " sending response " + responseID);
       
   299                 t.sendResponseHeaders(200, -1);
       
   300                 for (String part : data) {
       
   301                     bytes = part.getBytes(StandardCharsets.UTF_8);
       
   302                     os.write(bytes);
       
   303                     os.flush();
       
   304                     System.out.println("\tresp:" + responseID + ": wrote " + bytes.length + " bytes");
       
   305                     // wait...
       
   306                     try { Thread.sleep(300); } catch (InterruptedException x) {};
       
   307                 }
       
   308                 System.out.println("\tresp:" + responseID + ": done");
       
   309             }
       
   310         }
       
   311     }
       
   312 
       
   313 }