< 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 >