test/jdk/java/net/httpclient/LargeResponseTest.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  * @bug 8231449
       
    57  * @summary This test verifies that the HttpClient works correctly when the server
       
    58  *          sends large amount of data. Note that this test will pass even without
       
    59  *          the fix for JDK-8231449, which is unfortunate.
       
    60  * @library /test/lib http2/server
       
    61  * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DigestEchoServer LargeResponseTest
       
    62  * @modules java.net.http/jdk.internal.net.http.common
       
    63  *          java.net.http/jdk.internal.net.http.frame
       
    64  *          java.net.http/jdk.internal.net.http.hpack
       
    65  *          java.logging
       
    66  *          java.base/sun.net.www.http
       
    67  *          java.base/sun.net.www
       
    68  *          java.base/sun.net
       
    69  * @run main/othervm -Dtest.requiresHost=true
       
    70  *                   -Djdk.httpclient.HttpClient.log=headers
       
    71  *                   -Djdk.internal.httpclient.debug=true
       
    72  *                   LargeResponseTest
       
    73  *
       
    74  */
       
    75 public class LargeResponseTest implements HttpServerAdapters {
       
    76     static final byte[] DATA;
       
    77     static {
       
    78         DATA = new byte[64 * 1024];
       
    79         int len = 'z' - 'a';
       
    80         for (int i=0; i < DATA.length; i++) {
       
    81             DATA[i] = (byte) ('a' + (i % len));
       
    82         }
       
    83     }
       
    84 
       
    85     static final SSLContext context;
       
    86     static {
       
    87         try {
       
    88             context = new SimpleSSLContext().get();
       
    89             SSLContext.setDefault(context);
       
    90         } catch (Exception x) {
       
    91             throw new ExceptionInInitializerError(x);
       
    92         }
       
    93     }
       
    94 
       
    95     final AtomicLong requestCounter = new AtomicLong();
       
    96     final AtomicLong responseCounter = new AtomicLong();
       
    97     HttpTestServer http1Server;
       
    98     HttpTestServer http2Server;
       
    99     HttpTestServer https1Server;
       
   100     HttpTestServer https2Server;
       
   101     DigestEchoServer.TunnelingProxy proxy;
       
   102 
       
   103     URI http1URI;
       
   104     URI https1URI;
       
   105     URI http2URI;
       
   106     URI https2URI;
       
   107     InetSocketAddress proxyAddress;
       
   108     ProxySelector proxySelector;
       
   109     HttpClient client;
       
   110     List<CompletableFuture<?>>  futures = new CopyOnWriteArrayList<>();
       
   111     Set<URI> pending = new CopyOnWriteArraySet<>();
       
   112 
       
   113     final ExecutorService executor = new ThreadPoolExecutor(12, 60, 10,
       
   114             TimeUnit.SECONDS, new LinkedBlockingQueue<>());
       
   115     final ExecutorService clientexec = new ThreadPoolExecutor(6, 12, 1,
       
   116             TimeUnit.SECONDS, new LinkedBlockingQueue<>());
       
   117 
       
   118     public HttpClient newHttpClient(ProxySelector ps) {
       
   119         HttpClient.Builder builder = HttpClient
       
   120                 .newBuilder()
       
   121                 .sslContext(context)
       
   122                 .executor(clientexec)
       
   123                 .proxy(ps);
       
   124         return builder.build();
       
   125     }
       
   126 
       
   127     public void setUp() throws Exception {
       
   128         try {
       
   129             InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
       
   130 
       
   131             // HTTP/1.1
       
   132             HttpServer server1 = HttpServer.create(sa, 0);
       
   133             server1.setExecutor(executor);
       
   134             http1Server = HttpTestServer.of(server1);
       
   135             http1Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/http1/");
       
   136             http1Server.start();
       
   137             http1URI = new URI("http://" + http1Server.serverAuthority() + "/LargeResponseTest/http1/");
       
   138 
       
   139 
       
   140             // HTTPS/1.1
       
   141             HttpsServer sserver1 = HttpsServer.create(sa, 100);
       
   142             sserver1.setExecutor(executor);
       
   143             sserver1.setHttpsConfigurator(new HttpsConfigurator(context));
       
   144             https1Server = HttpTestServer.of(sserver1);
       
   145             https1Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/https1/");
       
   146             https1Server.start();
       
   147             https1URI = new URI("https://" + https1Server.serverAuthority() + "/LargeResponseTest/https1/");
       
   148 
       
   149             // HTTP/2.0
       
   150             http2Server = HttpTestServer.of(
       
   151                     new Http2TestServer("localhost", false, 0));
       
   152             http2Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/http2/");
       
   153             http2Server.start();
       
   154             http2URI = new URI("http://" + http2Server.serverAuthority() + "/LargeResponseTest/http2/");
       
   155 
       
   156             // HTTPS/2.0
       
   157             https2Server = HttpTestServer.of(
       
   158                     new Http2TestServer("localhost", true, 0));
       
   159             https2Server.addHandler(new HttpTestLargeHandler(), "/LargeResponseTest/https2/");
       
   160             https2Server.start();
       
   161             https2URI = new URI("https://" + https2Server.serverAuthority() + "/LargeResponseTest/https2/");
       
   162 
       
   163             proxy = DigestEchoServer.createHttpsProxyTunnel(
       
   164                     DigestEchoServer.HttpAuthSchemeType.NONE);
       
   165             proxyAddress = proxy.getProxyAddress();
       
   166             proxySelector = new HttpProxySelector(proxyAddress);
       
   167             client = newHttpClient(proxySelector);
       
   168             System.out.println("Setup: done");
       
   169         } catch (Exception x) {
       
   170             tearDown(); throw x;
       
   171         } catch (Error e) {
       
   172             tearDown(); throw e;
       
   173         }
       
   174     }
       
   175 
       
   176     public static void main(String[] args) throws Exception {
       
   177         LargeResponseTest test = new LargeResponseTest();
       
   178         test.setUp();
       
   179         long start = System.nanoTime();
       
   180         try {
       
   181             test.run(args);
       
   182         } finally {
       
   183             try {
       
   184                 long elapsed = System.nanoTime() - start;
       
   185                 System.out.println("*** Elapsed: " + Duration.ofNanos(elapsed));
       
   186             } finally {
       
   187                 test.tearDown();
       
   188             }
       
   189         }
       
   190     }
       
   191 
       
   192     public void run(String... args) throws Exception {
       
   193         List<URI> serverURIs = List.of(http1URI, http2URI, https1URI, https2URI);
       
   194         for (int i=0; i<5; i++) {
       
   195             for (URI base : serverURIs) {
       
   196                 if (base.getScheme().equalsIgnoreCase("https")) {
       
   197                     URI proxy = i % 1 == 0 ? base.resolve(URI.create("proxy/foo?n="+requestCounter.incrementAndGet()))
       
   198                     : base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
       
   199                     test(proxy);
       
   200                 }
       
   201             }
       
   202             for (URI base : serverURIs) {
       
   203                 URI direct = base.resolve(URI.create("direct/foo?n="+requestCounter.incrementAndGet()));
       
   204                 test(direct);
       
   205             }
       
   206         }
       
   207         CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
       
   208     }
       
   209 
       
   210     public void test(URI uri) throws Exception {
       
   211         System.out.println("Testing with " + uri);
       
   212         pending.add(uri);
       
   213         HttpRequest request = HttpRequest.newBuilder(uri).build();
       
   214         CompletableFuture<HttpResponse<String>> resp =
       
   215                 client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
       
   216                 .whenComplete((r, t) -> this.requestCompleted(request, r, t));
       
   217         futures.add(resp);
       
   218     }
       
   219 
       
   220     private void requestCompleted(HttpRequest request, HttpResponse<?> r, Throwable t) {
       
   221         responseCounter.incrementAndGet();
       
   222         pending.remove(request.uri());
       
   223         System.out.println(request + " -> " + (t == null ? r : t)
       
   224                 + " [still pending: " + (requestCounter.get() - responseCounter.get()) +"]");
       
   225         if (pending.size() < 10 && requestCounter.get() > 10) {
       
   226             pending.forEach(u -> System.out.println("\tpending: " + u));
       
   227         }
       
   228     }
       
   229 
       
   230     public void tearDown() {
       
   231         proxy = stop(proxy, DigestEchoServer.TunnelingProxy::stop);
       
   232         http1Server = stop(http1Server, HttpTestServer::stop);
       
   233         https1Server = stop(https1Server, HttpTestServer::stop);
       
   234         http2Server = stop(http2Server, HttpTestServer::stop);
       
   235         https2Server = stop(https2Server, HttpTestServer::stop);
       
   236         client = null;
       
   237         try {
       
   238             executor.awaitTermination(2000, TimeUnit.MILLISECONDS);
       
   239         } catch (Throwable x) {
       
   240         } finally {
       
   241             executor.shutdownNow();
       
   242         }
       
   243         try {
       
   244             clientexec.awaitTermination(2000, TimeUnit.MILLISECONDS);
       
   245         } catch (Throwable x) {
       
   246         } finally {
       
   247             clientexec.shutdownNow();
       
   248         }
       
   249         System.out.println("Teardown: done");
       
   250     }
       
   251 
       
   252     private interface Stoppable<T> { public void stop(T service) throws Exception; }
       
   253 
       
   254     static <T>  T stop(T service, Stoppable<T> stop) {
       
   255         try { if (service != null) stop.stop(service); } catch (Throwable x) { };
       
   256         return null;
       
   257     }
       
   258 
       
   259     static class HttpProxySelector extends ProxySelector {
       
   260         private static final List<Proxy> NO_PROXY = List.of(Proxy.NO_PROXY);
       
   261         private final List<Proxy> proxyList;
       
   262         HttpProxySelector(InetSocketAddress proxyAddress) {
       
   263             proxyList = List.of(new Proxy(Proxy.Type.HTTP, proxyAddress));
       
   264         }
       
   265 
       
   266         @Override
       
   267         public List<Proxy> select(URI uri) {
       
   268             // our proxy only supports tunneling
       
   269             if (uri.getScheme().equalsIgnoreCase("https")) {
       
   270                 if (uri.getPath().contains("/proxy/")) {
       
   271                     return proxyList;
       
   272                 }
       
   273             }
       
   274             return NO_PROXY;
       
   275         }
       
   276 
       
   277         @Override
       
   278         public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
       
   279             System.err.println("Connection to proxy failed: " + ioe);
       
   280             System.err.println("Proxy: " + sa);
       
   281             System.err.println("\tURI: " + uri);
       
   282             ioe.printStackTrace();
       
   283         }
       
   284     }
       
   285 
       
   286     public static class HttpTestLargeHandler implements HttpTestHandler {
       
   287         @Override
       
   288         public void handle(HttpTestExchange t) throws IOException {
       
   289             try (InputStream is = t.getRequestBody();
       
   290                  OutputStream os = t.getResponseBody()) {
       
   291                 byte[] bytes = is.readAllBytes();
       
   292                 assert bytes.length == 0;
       
   293                 URI u = t.getRequestURI();
       
   294                 long responseID = Long.parseLong(u.getQuery().substring(2));
       
   295                 System.out.println("Server " + t.getRequestURI() + " sending response " + responseID);
       
   296                 t.sendResponseHeaders(200, DATA.length * 3);
       
   297                 for (int i=0; i<3; i++) {
       
   298                     os.write(DATA);
       
   299                 }
       
   300                 System.out.println("\tresp:" + responseID + ": done");
       
   301             }
       
   302         }
       
   303     }
       
   304 
       
   305 }