test/jdk/java/net/httpclient/MaxStreams.java
changeset 50681 4254bed3c09d
child 52121 934969c63223
child 56795 03ece2518428
equal deleted inserted replaced
50678:818a23db260c 50681:4254bed3c09d
       
     1 /*
       
     2  * Copyright (c) 2018, 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 
       
    24 /*
       
    25  * @test
       
    26  * @bug 8196389
       
    27  * @summary Should HttpClient support SETTINGS_MAX_CONCURRENT_STREAMS from the server
       
    28  *
       
    29  * @modules java.base/sun.net.www.http
       
    30  *          java.net.http/jdk.internal.net.http.common
       
    31  *          java.net.http/jdk.internal.net.http.frame
       
    32  *          java.net.http/jdk.internal.net.http.hpack
       
    33  *          java.logging
       
    34  *          jdk.httpserver
       
    35  * @library /lib/testlibrary http2/server
       
    36  * @build Http2TestServer
       
    37  * @build jdk.testlibrary.SimpleSSLContext
       
    38  * @run testng/othervm -ea -esa MaxStreams
       
    39  */
       
    40 
       
    41 import java.io.IOException;
       
    42 import java.io.InputStream;
       
    43 import java.io.OutputStream;
       
    44 import java.net.InetAddress;
       
    45 import java.net.InetSocketAddress;
       
    46 import java.net.URI;
       
    47 import java.util.List;
       
    48 import java.util.LinkedList;
       
    49 import java.util.Properties;
       
    50 import java.util.concurrent.atomic.AtomicInteger;
       
    51 import java.util.concurrent.CompletableFuture;
       
    52 import java.util.concurrent.CompletionException;
       
    53 import java.util.concurrent.CountDownLatch;
       
    54 import java.util.concurrent.Executors;
       
    55 import java.util.concurrent.ExecutorService;
       
    56 import java.util.concurrent.Semaphore;
       
    57 import javax.net.ssl.SSLContext;
       
    58 import java.net.http.HttpClient;
       
    59 import java.net.http.HttpRequest;
       
    60 import java.net.http.HttpResponse;
       
    61 import java.net.http.HttpResponse.BodyHandler;
       
    62 import java.net.http.HttpResponse.BodyHandlers;
       
    63 import jdk.testlibrary.SimpleSSLContext;
       
    64 import org.testng.annotations.AfterTest;
       
    65 import org.testng.annotations.BeforeTest;
       
    66 import org.testng.annotations.DataProvider;
       
    67 import org.testng.annotations.Test;
       
    68 import static java.nio.charset.StandardCharsets.UTF_8;
       
    69 import static java.net.http.HttpResponse.BodyHandlers.discarding;
       
    70 import static org.testng.Assert.assertEquals;
       
    71 import static org.testng.Assert.assertFalse;
       
    72 import static org.testng.Assert.fail;
       
    73 
       
    74 public class MaxStreams {
       
    75 
       
    76     Http2TestServer http2TestServer;   // HTTP/2 ( h2c )
       
    77     Http2TestServer https2TestServer;   // HTTP/2 ( h2 )
       
    78     final Http2FixedHandler handler = new Http2FixedHandler();
       
    79     SSLContext ctx;
       
    80     String http2FixedURI;
       
    81     String https2FixedURI;
       
    82     volatile CountDownLatch latch;
       
    83     ExecutorService exec;
       
    84     final Semaphore canStartTestRun = new Semaphore(1);
       
    85 
       
    86     // we send an initial warm up request, then MAX_STREAMS+1 requests
       
    87     // in parallel. The last of them should hit the limit.
       
    88     // Then we wait for all the responses and send a further request
       
    89     // which should succeed. The server should see (and respond to)
       
    90     // MAX_STREAMS+2 requests per test run.
       
    91 
       
    92     static final int MAX_STREAMS = 10;
       
    93     static final String RESPONSE = "Hello world";
       
    94 
       
    95     @DataProvider(name = "uris")
       
    96     public Object[][] variants() {
       
    97         return new Object[][]{
       
    98                 {http2FixedURI},
       
    99                 {https2FixedURI},
       
   100                 {http2FixedURI},
       
   101                 {https2FixedURI}
       
   102         };
       
   103     }
       
   104 
       
   105 
       
   106     @Test(dataProvider = "uris", timeOut=20000)
       
   107     void testAsString(String uri) throws Exception {
       
   108         canStartTestRun.acquire();
       
   109         latch = new CountDownLatch(1);
       
   110         handler.setLatch(latch);
       
   111         HttpClient client = HttpClient.newBuilder().sslContext(ctx).build();
       
   112         List<CompletableFuture<HttpResponse<String>>> responses = new LinkedList<>();
       
   113 
       
   114         HttpRequest request = HttpRequest.newBuilder(URI.create(uri))
       
   115                                          .version(HttpClient.Version.HTTP_2)
       
   116                                          .GET()
       
   117                                          .build();
       
   118         // send warmup to ensure we only have one Http2Connection
       
   119         HttpResponse<String> warmup = client.send(request, BodyHandlers.ofString());
       
   120         if (warmup.statusCode() != 200 || !warmup.body().equals(RESPONSE))
       
   121             throw new RuntimeException();
       
   122 
       
   123         for (int i=0;i<MAX_STREAMS+1; i++) {
       
   124             responses.add(client.sendAsync(request, BodyHandlers.ofString()));
       
   125         }
       
   126 
       
   127         // wait until we get local exception before allow server to proceed
       
   128         try {
       
   129             CompletableFuture.anyOf(responses.toArray(new CompletableFuture<?>[0])).join();
       
   130         } catch (Exception ee) {
       
   131             System.err.println("Expected exception 1 " + ee);
       
   132         }
       
   133 
       
   134         latch.countDown();
       
   135 
       
   136         // check the first MAX_STREAMS requests succeeded
       
   137         try {
       
   138             CompletableFuture.allOf(responses.toArray(new CompletableFuture<?>[0])).join();
       
   139             System.err.println("Did not get Expected exception 2 ");
       
   140         } catch (Exception ee) {
       
   141             System.err.println("Expected exception 2 " + ee);
       
   142         }
       
   143         int count = 0;
       
   144         int failures = 0;
       
   145         for (CompletableFuture<HttpResponse<String>> cf : responses) {
       
   146             HttpResponse<String> r = null;
       
   147             try {
       
   148                 count++;
       
   149                 r = cf.join();
       
   150                 if (r.statusCode() != 200 || !r.body().equals(RESPONSE))
       
   151                     throw new RuntimeException();
       
   152             } catch (Throwable t) {
       
   153                 failures++;
       
   154                 System.err.printf("Failure %d at count %d\n", failures, count);
       
   155                 System.err.println(t);
       
   156                 t.printStackTrace();
       
   157             }
       
   158         }
       
   159         if (failures != 1) {
       
   160             String msg = "Expected 1 failure. Got " + failures;
       
   161             throw new RuntimeException(msg);
       
   162         }
       
   163 
       
   164         // make sure it succeeds now as number of streams == 0 now
       
   165         HttpResponse<String> warmdown = client.send(request, BodyHandlers.ofString());
       
   166         if (warmdown.statusCode() != 200 || !warmdown.body().equals(RESPONSE))
       
   167             throw new RuntimeException();
       
   168         System.err.println("Test OK");
       
   169     }
       
   170 
       
   171     @BeforeTest
       
   172     public void setup() throws Exception {
       
   173         ctx = (new SimpleSSLContext()).get();
       
   174         exec = Executors.newCachedThreadPool();
       
   175 
       
   176         InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);
       
   177 
       
   178         Properties props = new Properties();
       
   179         props.setProperty("http2server.settings.max_concurrent_streams", Integer.toString(MAX_STREAMS));
       
   180         http2TestServer = new Http2TestServer("localhost", false, 0, exec, 10, props, null);
       
   181         http2TestServer.addHandler(handler, "/http2/fixed");
       
   182         http2FixedURI = "http://" + http2TestServer.serverAuthority()+ "/http2/fixed";
       
   183         http2TestServer.start();
       
   184 
       
   185         https2TestServer = new Http2TestServer("localhost", true, 0, exec, 10, props, ctx);
       
   186         https2TestServer.addHandler(handler, "/http2/fixed");
       
   187         https2FixedURI = "https://" + https2TestServer.serverAuthority()+ "/http2/fixed";
       
   188         https2TestServer.start();
       
   189     }
       
   190 
       
   191     @AfterTest
       
   192     public void teardown() throws Exception {
       
   193         System.err.println("Stopping test server now");
       
   194         http2TestServer.stop();
       
   195     }
       
   196 
       
   197     class Http2FixedHandler implements Http2Handler {
       
   198         final AtomicInteger counter = new AtomicInteger(0);
       
   199         CountDownLatch latch;
       
   200 
       
   201         synchronized void setLatch(CountDownLatch latch) {
       
   202             this.latch = latch;
       
   203         }
       
   204 
       
   205         synchronized CountDownLatch getLatch() {
       
   206             return latch;
       
   207         }
       
   208 
       
   209         @Override
       
   210         public void handle(Http2TestExchange t) throws IOException {
       
   211             int c = -1;
       
   212             try (InputStream is = t.getRequestBody();
       
   213                  OutputStream os = t.getResponseBody()) {
       
   214 
       
   215                 is.readAllBytes();
       
   216                 c = counter.getAndIncrement();
       
   217                 if (c > 0 && c <= MAX_STREAMS) {
       
   218                     // Wait for latch.
       
   219                     try {
       
   220                         // don't send any replies until all requests are sent
       
   221                         System.err.println("latch await");
       
   222                         getLatch().await();
       
   223                         System.err.println("latch resume");
       
   224                     } catch (InterruptedException ee) {}
       
   225                 }
       
   226                 t.sendResponseHeaders(200, RESPONSE.length());
       
   227                 os.write(RESPONSE.getBytes());
       
   228             } finally {
       
   229                 // client issues MAX_STREAMS + 3 requests in total
       
   230                 // but server should only see MAX_STREAMS + 2 in total. One is rejected by client
       
   231                 // counter c captured before increment so final value is MAX_STREAMS + 1
       
   232                 if (c == MAX_STREAMS + 1) {
       
   233                     counter.set(0);
       
   234                     canStartTestRun.release();
       
   235                 }
       
   236             }
       
   237         }
       
   238     }
       
   239 }