1 /* 2 * Copyright (c) 2015, 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 8087112 8159814 27 * @library /lib/testlibrary server 28 * @build jdk.testlibrary.SimpleSSLContext 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 * @run testng/othervm 34 * -Djdk.httpclient.HttpClient.log=errors,requests,responses 35 * ServerPush 36 */ 37 38 import java.io.*; 39 import java.net.*; 40 import java.nio.ByteBuffer; 41 import java.nio.file.*; 42 import java.net.http.*; 43 import java.net.http.HttpResponse.BodyHandler; 44 import java.net.http.HttpResponse.BodyHandlers; 45 import java.net.http.HttpResponse.BodySubscribers; 46 import java.net.http.HttpResponse.PushPromiseHandler; 47 import java.util.*; 48 import java.util.concurrent.*; 49 import java.util.function.Consumer; 50 import org.testng.annotations.AfterTest; 51 import org.testng.annotations.BeforeTest; 52 import org.testng.annotations.Test; 53 import static java.nio.charset.StandardCharsets.UTF_8; 54 import static org.testng.Assert.*; 55 56 public class ServerPush { 57 58 static final int LOOPS = 13; 59 static final int FILE_SIZE = 512 * 1024 + 343; 60 61 static Path tempFile; 62 63 Http2TestServer server; 64 URI uri; 65 66 @BeforeTest setup()67 public void setup() throws Exception { 68 tempFile = TestUtil.getAFile(FILE_SIZE); 69 server = new Http2TestServer(false, 0); 70 server.addHandler(new PushHandler(tempFile, LOOPS), "/"); 71 System.out.println("Using temp file:" + tempFile); 72 73 System.err.println("Server listening on port " + server.getAddress().getPort()); 74 server.start(); 75 int port = server.getAddress().getPort(); 76 uri = new URI("http://localhost:" + port + "/foo/a/b/c"); 77 } 78 79 @AfterTest teardown()80 public void teardown() { 81 server.stop(); 82 } 83 84 // Test 1 - custom written push promise handler, everything as a String 85 @Test testTypeString()86 public void testTypeString() throws Exception { 87 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 88 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<String>>> 89 resultMap = new ConcurrentHashMap<>(); 90 91 PushPromiseHandler<String> pph = (initial, pushRequest, acceptor) -> { 92 BodyHandler<String> s = BodyHandlers.ofString(UTF_8); 93 CompletableFuture<HttpResponse<String>> cf = acceptor.apply(s); 94 resultMap.put(pushRequest, cf); 95 }; 96 97 HttpClient client = HttpClient.newHttpClient(); 98 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 99 CompletableFuture<HttpResponse<String>> cf = 100 client.sendAsync(request, BodyHandlers.ofString(UTF_8), pph); 101 cf.join(); 102 resultMap.put(request, cf); 103 System.err.println("results.size: " + resultMap.size()); 104 for (HttpRequest r : resultMap.keySet()) { 105 HttpResponse<String> response = resultMap.get(r).join(); 106 assertEquals(response.statusCode(), 200); 107 assertEquals(response.body(), tempFileAsString); 108 } 109 assertEquals(resultMap.size(), LOOPS + 1); 110 } 111 112 // Test 2 - of(...) populating the given Map, everything as a String 113 @Test testTypeStringOfMap()114 public void testTypeStringOfMap() throws Exception { 115 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 116 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<String>>> 117 resultMap = new ConcurrentHashMap<>(); 118 119 PushPromiseHandler<String> pph = 120 PushPromiseHandler.of(pushPromise -> BodyHandlers.ofString(UTF_8), 121 resultMap); 122 123 HttpClient client = HttpClient.newHttpClient(); 124 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 125 CompletableFuture<HttpResponse<String>> cf = 126 client.sendAsync(request, BodyHandlers.ofString(UTF_8), pph); 127 cf.join(); 128 resultMap.put(request, cf); 129 System.err.println("results.size: " + resultMap.size()); 130 for (HttpRequest r : resultMap.keySet()) { 131 HttpResponse<String> response = resultMap.get(r).join(); 132 assertEquals(response.statusCode(), 200); 133 assertEquals(response.body(), tempFileAsString); 134 } 135 assertEquals(resultMap.size(), LOOPS + 1); 136 } 137 138 // --- Path --- 139 140 static final Path dir = Paths.get(".", "serverPush"); requestToPath(HttpRequest req)141 static BodyHandler<Path> requestToPath(HttpRequest req) { 142 URI u = req.uri(); 143 Path path = Paths.get(dir.toString(), u.getPath()); 144 try { 145 Files.createDirectories(path.getParent()); 146 } catch (IOException ee) { 147 throw new UncheckedIOException(ee); 148 } 149 return BodyHandlers.ofFile(path); 150 } 151 152 // Test 3 - custom written push promise handler, everything as a Path 153 @Test testTypePath()154 public void testTypePath() throws Exception { 155 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 156 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Path>>> resultsMap 157 = new ConcurrentHashMap<>(); 158 159 PushPromiseHandler<Path> pushPromiseHandler = (initial, pushRequest, acceptor) -> { 160 BodyHandler<Path> pp = requestToPath(pushRequest); 161 CompletableFuture<HttpResponse<Path>> cf = acceptor.apply(pp); 162 resultsMap.put(pushRequest, cf); 163 }; 164 165 HttpClient client = HttpClient.newHttpClient(); 166 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 167 CompletableFuture<HttpResponse<Path>> cf = 168 client.sendAsync(request, requestToPath(request), pushPromiseHandler); 169 cf.join(); 170 resultsMap.put(request, cf); 171 172 for (HttpRequest r : resultsMap.keySet()) { 173 HttpResponse<Path> response = resultsMap.get(r).join(); 174 assertEquals(response.statusCode(), 200); 175 String fileAsString = new String(Files.readAllBytes(response.body()), UTF_8); 176 assertEquals(fileAsString, tempFileAsString); 177 } 178 assertEquals(resultsMap.size(), LOOPS + 1); 179 } 180 181 // Test 4 - of(...) populating the given Map, everything as a Path 182 @Test testTypePathOfMap()183 public void testTypePathOfMap() throws Exception { 184 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 185 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Path>>> resultsMap 186 = new ConcurrentHashMap<>(); 187 188 PushPromiseHandler<Path> pushPromiseHandler = 189 PushPromiseHandler.of(pushRequest -> requestToPath(pushRequest), 190 resultsMap); 191 192 HttpClient client = HttpClient.newHttpClient(); 193 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 194 CompletableFuture<HttpResponse<Path>> cf = 195 client.sendAsync(request, requestToPath(request), pushPromiseHandler); 196 cf.join(); 197 resultsMap.put(request, cf); 198 199 for (HttpRequest r : resultsMap.keySet()) { 200 HttpResponse<Path> response = resultsMap.get(r).join(); 201 assertEquals(response.statusCode(), 200); 202 String fileAsString = new String(Files.readAllBytes(response.body()), UTF_8); 203 assertEquals(fileAsString, tempFileAsString); 204 } 205 assertEquals(resultsMap.size(), LOOPS + 1); 206 } 207 208 // --- Consumer<byte[]> --- 209 210 static class ByteArrayConsumer implements Consumer<Optional<byte[]>> { 211 volatile List<byte[]> listByteArrays = new ArrayList<>(); 212 volatile byte[] accumulatedBytes; 213 getAccumulatedBytes()214 public byte[] getAccumulatedBytes() { return accumulatedBytes; } 215 216 @Override accept(Optional<byte[]> optionalBytes)217 public void accept(Optional<byte[]> optionalBytes) { 218 assert accumulatedBytes == null; 219 if (!optionalBytes.isPresent()) { 220 int size = listByteArrays.stream().mapToInt(ba -> ba.length).sum(); 221 ByteBuffer bb = ByteBuffer.allocate(size); 222 listByteArrays.stream().forEach(ba -> bb.put(ba)); 223 accumulatedBytes = bb.array(); 224 } else { 225 listByteArrays.add(optionalBytes.get()); 226 } 227 } 228 } 229 230 // Test 5 - custom written handler, everything as a consumer of optional byte[] 231 @Test testTypeByteArrayConsumer()232 public void testTypeByteArrayConsumer() throws Exception { 233 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 234 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Void>>> resultsMap 235 = new ConcurrentHashMap<>(); 236 Map<HttpRequest,ByteArrayConsumer> byteArrayConsumerMap 237 = new ConcurrentHashMap<>(); 238 239 HttpClient client = HttpClient.newHttpClient(); 240 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 241 ByteArrayConsumer bac = new ByteArrayConsumer(); 242 byteArrayConsumerMap.put(request, bac); 243 244 PushPromiseHandler<Void> pushPromiseHandler = (initial, pushRequest, acceptor) -> { 245 CompletableFuture<HttpResponse<Void>> cf = acceptor.apply( 246 (info) -> { 247 ByteArrayConsumer bc = new ByteArrayConsumer(); 248 byteArrayConsumerMap.put(pushRequest, bc); 249 return BodySubscribers.ofByteArrayConsumer(bc); } ); 250 resultsMap.put(pushRequest, cf); 251 }; 252 253 CompletableFuture<HttpResponse<Void>> cf = 254 client.sendAsync(request, BodyHandlers.ofByteArrayConsumer(bac), pushPromiseHandler); 255 cf.join(); 256 resultsMap.put(request, cf); 257 258 for (HttpRequest r : resultsMap.keySet()) { 259 HttpResponse<Void> response = resultsMap.get(r).join(); 260 assertEquals(response.statusCode(), 200); 261 byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); 262 String result = new String(ba, UTF_8); 263 assertEquals(result, tempFileAsString); 264 } 265 assertEquals(resultsMap.size(), LOOPS + 1); 266 } 267 268 // Test 6 - of(...) populating the given Map, everything as a consumer of optional byte[] 269 @Test testTypeByteArrayConsumerOfMap()270 public void testTypeByteArrayConsumerOfMap() throws Exception { 271 String tempFileAsString = new String(Files.readAllBytes(tempFile), UTF_8); 272 ConcurrentMap<HttpRequest, CompletableFuture<HttpResponse<Void>>> resultsMap 273 = new ConcurrentHashMap<>(); 274 Map<HttpRequest,ByteArrayConsumer> byteArrayConsumerMap 275 = new ConcurrentHashMap<>(); 276 277 HttpClient client = HttpClient.newHttpClient(); 278 HttpRequest request = HttpRequest.newBuilder(uri).GET().build(); 279 ByteArrayConsumer bac = new ByteArrayConsumer(); 280 byteArrayConsumerMap.put(request, bac); 281 282 PushPromiseHandler<Void> pushPromiseHandler = 283 PushPromiseHandler.of( 284 pushRequest -> { 285 ByteArrayConsumer bc = new ByteArrayConsumer(); 286 byteArrayConsumerMap.put(pushRequest, bc); 287 return BodyHandlers.ofByteArrayConsumer(bc); 288 }, 289 resultsMap); 290 291 CompletableFuture<HttpResponse<Void>> cf = 292 client.sendAsync(request, BodyHandlers.ofByteArrayConsumer(bac), pushPromiseHandler); 293 cf.join(); 294 resultsMap.put(request, cf); 295 296 for (HttpRequest r : resultsMap.keySet()) { 297 HttpResponse<Void> response = resultsMap.get(r).join(); 298 assertEquals(response.statusCode(), 200); 299 byte[] ba = byteArrayConsumerMap.get(r).getAccumulatedBytes(); 300 String result = new String(ba, UTF_8); 301 assertEquals(result, tempFileAsString); 302 } 303 assertEquals(resultsMap.size(), LOOPS + 1); 304 } 305 } 306