< prev index next >
   1 /*
   2  * Copyright (c) 2015, 2016, 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 8087112
  27  * @library /lib/testlibrary/ /
  28  * @build jdk.testlibrary.SimpleSSLContext EchoHandler
  29  * @compile ../../../com/sun/net/httpserver/LogFilter.java
  30  * @compile ../../../com/sun/net/httpserver/FileServerHandler.java
  31  * @run main/othervm/timeout=40 -Djava.net.http.HttpClient.log=ssl ManyRequests
  32  * @summary Send a large number of requests asynchronously
  33  */
  34 
  35 //package javaapplication16;
  36 
  37 import com.sun.net.httpserver.*;
  38 import java.io.IOException;
  39 import java.io.UncheckedIOException;
  40 import java.net.http.HttpClient;
  41 import java.net.http.HttpRequest;
  42 import java.net.http.HttpResponse;
  43 import java.net.InetSocketAddress;
  44 import java.net.URI;
  45 import java.util.Arrays;
  46 import java.util.HashMap;
  47 import java.util.LinkedList;
  48 import java.util.Random;
  49 import java.util.logging.*;
  50 import java.util.concurrent.CompletableFuture;
  51 import javax.net.ssl.*;
  52 import jdk.testlibrary.SimpleSSLContext;
  53 
  54 public class ManyRequests {
  55 
  56     volatile static int counter = 0;
  57 
  58     public static void main(String[] args) throws Exception {
  59         Logger logger = Logger.getLogger("com.sun.net.httpserver");
  60         logger.setLevel(Level.ALL);
  61         logger.info("TEST");
  62 
  63         SSLContext ctx = new SimpleSSLContext().get();
  64 
  65         InetSocketAddress addr = new InetSocketAddress(0);
  66         HttpsServer server = HttpsServer.create(addr, 0);
  67         server.setHttpsConfigurator(new Configurator(ctx));
  68 
  69         HttpClient client = HttpClient.create()
  70                                       .sslContext(ctx)
  71                                       .build();
  72         try {
  73             test(server, client);
  74             System.out.println("OK");
  75         } finally {
  76             server.stop(0);
  77             client.executorService().shutdownNow();
  78         }
  79     }
  80 
  81     //static final int REQUESTS = 1000;
  82     static final int REQUESTS = 20;
  83 
  84     static void test(HttpsServer server, HttpClient client) throws Exception {
  85         int port = server.getAddress().getPort();
  86         URI uri = new URI("https://127.0.0.1:" + port + "/foo/x");
  87         server.createContext("/foo", new EchoHandler());
  88         server.start();
  89 
  90         RequestLimiter limiter = new RequestLimiter(40);
  91         Random rand = new Random();
  92         CompletableFuture<Void>[] results = new CompletableFuture[REQUESTS];
  93         HashMap<HttpRequest,byte[]> bodies = new HashMap<>();
  94 
  95         for (int i=0; i<REQUESTS; i++) {
  96             byte[] buf = new byte[i+1];  // different size bodies
  97             rand.nextBytes(buf);
  98             HttpRequest r = client.request(uri)
  99                                   .body(HttpRequest.fromByteArray(buf))
 100                                   .POST();
 101             bodies.put(r, buf);
 102 
 103             results[i] =
 104                 limiter.whenOkToSend()
 105                        .thenCompose((v) -> r.responseAsync())
 106                        .thenCompose((resp) -> {
 107                            limiter.requestComplete();
 108                            if (resp.statusCode() != 200) {
 109                                resp.bodyAsync(HttpResponse.ignoreBody());
 110                                String s = "Expected 200, got: " + resp.statusCode();
 111                                return completedWithIOException(s);
 112                            } else {
 113                                counter++;
 114                                System.out.println("Result from " + counter);
 115                            }
 116                            return resp.bodyAsync(HttpResponse.asByteArray())
 117                                       .thenApply((b) -> new Pair<>(resp, b));
 118                        })
 119                       .thenAccept((pair) -> {
 120                           HttpRequest request = pair.t.request();
 121                           byte[] requestBody = bodies.get(request);
 122                           check(Arrays.equals(requestBody, pair.u),
 123                                 "bodies not equal");
 124 
 125                       });
 126         }
 127 
 128         // wait for them all to complete and throw exception in case of error
 129         //try {
 130             CompletableFuture.allOf(results).join();
 131         //} catch (Exception  e) {
 132             //e.printStackTrace();
 133             //throw e;
 134         //}
 135     }
 136 
 137     static <T> CompletableFuture<T> completedWithIOException(String message) {
 138         return CompletableFuture.failedFuture(new IOException(message));
 139     }
 140 
 141     static final class Pair<T,U> {
 142         Pair(T t, U u) {
 143             this.t = t; this.u = u;
 144         }
 145         T t;
 146         U u;
 147     }
 148 
 149     /**
 150      * A simple limiter for controlling the number of requests to be run in
 151      * parallel whenOkToSend() is called which returns a CF<Void> that allows
 152      * each individual request to proceed, or block temporarily (blocking occurs
 153      * on the waiters list here. As each request actually completes
 154      * requestComplete() is called to notify this object, and allow some
 155      * requests to continue.
 156      */
 157     static class RequestLimiter {
 158 
 159         static final CompletableFuture<Void> COMPLETED_FUTURE =
 160                 CompletableFuture.completedFuture(null);
 161 
 162         final int maxnumber;
 163         final LinkedList<CompletableFuture<Void>> waiters;
 164         int number;
 165         boolean blocked;
 166 
 167         RequestLimiter(int maximum) {
 168             waiters = new LinkedList<>();
 169             maxnumber = maximum;
 170         }
 171 
 172         synchronized void requestComplete() {
 173             number--;
 174             // don't unblock until number of requests has halved.
 175             if ((blocked && number <= maxnumber / 2) ||
 176                         (!blocked && waiters.size() > 0)) {
 177                 int toRelease = Math.min(maxnumber - number, waiters.size());
 178                 for (int i=0; i<toRelease; i++) {
 179                     CompletableFuture<Void> f = waiters.remove();
 180                     number ++;
 181                     f.complete(null);
 182                 }
 183                 blocked = number >= maxnumber;
 184             }
 185         }
 186 
 187         synchronized CompletableFuture<Void> whenOkToSend() {
 188             if (blocked || number + 1 >= maxnumber) {
 189                 blocked = true;
 190                 CompletableFuture<Void> r = new CompletableFuture<>();
 191                 waiters.add(r);
 192                 return r;
 193             } else {
 194                 number++;
 195                 return COMPLETED_FUTURE;
 196             }
 197         }
 198     }
 199 
 200     static void check(boolean cond, Object... msg) {
 201         if (cond)
 202             return;
 203         StringBuilder sb = new StringBuilder();
 204         for (Object o : msg)
 205             sb.append(o);
 206         throw new RuntimeException(sb.toString());
 207     }
 208 }
 209 
 210 class Configurator extends HttpsConfigurator {
 211     public Configurator(SSLContext ctx) {
 212         super(ctx);
 213     }
 214 
 215     public void configure (HttpsParameters params) {
 216         params.setSSLParameters (getSSLContext().getSupportedSSLParameters());
 217     }
 218 }
 219 
< prev index next >