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 * @summary Verify that dependent synchronous actions added before the CF 27 * completes are executed either asynchronously in an executor when the 28 * CF later completes, or in the user thread that joins. 29 * @library /test/lib http2/server 30 * @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DependentActionsTest 31 * @modules java.base/sun.net.www.http 32 * java.net.http/jdk.internal.net.http.common 33 * java.net.http/jdk.internal.net.http.frame 34 * java.net.http/jdk.internal.net.http.hpack 35 * @run testng/othervm -Djdk.internal.httpclient.debug=true DependentActionsTest 36 * @run testng/othervm/java.security.policy=dependent.policy 37 * -Djdk.internal.httpclient.debug=true DependentActionsTest 38 */ 39 40 import java.io.BufferedReader; 41 import java.io.InputStreamReader; 42 import java.lang.StackWalker.StackFrame; 43 import com.sun.net.httpserver.HttpServer; 44 import com.sun.net.httpserver.HttpsConfigurator; 45 import com.sun.net.httpserver.HttpsServer; 46 import jdk.test.lib.net.SimpleSSLContext; 47 import org.testng.annotations.AfterTest; 48 import org.testng.annotations.AfterClass; 49 import org.testng.annotations.BeforeTest; 50 import org.testng.annotations.DataProvider; 51 import org.testng.annotations.Test; 52 53 import javax.net.ssl.SSLContext; 54 import java.io.IOException; 55 import java.io.InputStream; 56 import java.io.OutputStream; 57 import java.net.InetAddress; 58 import java.net.InetSocketAddress; 59 import java.net.URI; 60 import java.net.http.HttpClient; 61 import java.net.http.HttpHeaders; 62 import java.net.http.HttpRequest; 63 import java.net.http.HttpResponse; 64 import java.net.http.HttpResponse.BodyHandler; 65 import java.net.http.HttpResponse.BodyHandlers; 66 import java.net.http.HttpResponse.BodySubscriber; 67 import java.nio.ByteBuffer; 68 import java.nio.charset.StandardCharsets; 69 import java.util.EnumSet; 70 import java.util.List; 71 import java.util.Optional; 72 import java.util.concurrent.CompletableFuture; 73 import java.util.concurrent.CompletionException; 74 import java.util.concurrent.CompletionStage; 75 import java.util.concurrent.ConcurrentHashMap; 76 import java.util.concurrent.ConcurrentMap; 77 import java.util.concurrent.Executor; 78 import java.util.concurrent.Executors; 79 import java.util.concurrent.Flow; 80 import java.util.concurrent.Semaphore; 81 import java.util.concurrent.atomic.AtomicBoolean; 82 import java.util.concurrent.atomic.AtomicLong; 83 import java.util.concurrent.atomic.AtomicReference; 84 import java.util.function.Consumer; 85 import java.util.function.Predicate; 86 import java.util.function.Supplier; 87 import java.util.stream.Collectors; 88 import java.util.stream.Stream; 89 90 import static java.lang.System.out; 91 import static java.lang.String.format; 92 import static java.util.stream.Collectors.toList; 93 import static org.testng.Assert.assertEquals; 94 import static org.testng.Assert.assertTrue; 95 96 public class DependentActionsTest implements HttpServerAdapters { 97 98 SSLContext sslContext; 99 HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ] 100 HttpTestServer httpsTestServer; // HTTPS/1.1 101 HttpTestServer http2TestServer; // HTTP/2 ( h2c ) 102 HttpTestServer https2TestServer; // HTTP/2 ( h2 ) 103 String httpURI_fixed; 104 String httpURI_chunk; 105 String httpsURI_fixed; 106 String httpsURI_chunk; 107 String http2URI_fixed; 108 String http2URI_chunk; 109 String https2URI_fixed; 110 String https2URI_chunk; 111 112 static final StackWalker WALKER = 113 StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 114 115 static final int ITERATION_COUNT = 1; 116 // a shared executor helps reduce the amount of threads created by the test 117 static final Executor executor = new TestExecutor(Executors.newCachedThreadPool()); 118 static final ConcurrentMap<String, Throwable> FAILURES = new ConcurrentHashMap<>(); 119 static volatile boolean tasksFailed; 120 static final AtomicLong serverCount = new AtomicLong(); 121 static final AtomicLong clientCount = new AtomicLong(); 122 static final long start = System.nanoTime(); now()123 public static String now() { 124 long now = System.nanoTime() - start; 125 long secs = now / 1000_000_000; 126 long mill = (now % 1000_000_000) / 1000_000; 127 long nan = now % 1000_000; 128 return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan); 129 } 130 131 private volatile HttpClient sharedClient; 132 133 static class TestExecutor implements Executor { 134 final AtomicLong tasks = new AtomicLong(); 135 Executor executor; TestExecutor(Executor executor)136 TestExecutor(Executor executor) { 137 this.executor = executor; 138 } 139 140 @Override execute(Runnable command)141 public void execute(Runnable command) { 142 long id = tasks.incrementAndGet(); 143 executor.execute(() -> { 144 try { 145 command.run(); 146 } catch (Throwable t) { 147 tasksFailed = true; 148 System.out.printf(now() + "Task %s failed: %s%n", id, t); 149 System.err.printf(now() + "Task %s failed: %s%n", id, t); 150 FAILURES.putIfAbsent("Task " + id, t); 151 throw t; 152 } 153 }); 154 } 155 } 156 157 @AfterClass printFailedTests()158 static final void printFailedTests() { 159 out.println("\n========================="); 160 try { 161 out.printf("%n%sCreated %d servers and %d clients%n", 162 now(), serverCount.get(), clientCount.get()); 163 if (FAILURES.isEmpty()) return; 164 out.println("Failed tests: "); 165 FAILURES.entrySet().forEach((e) -> { 166 out.printf("\t%s: %s%n", e.getKey(), e.getValue()); 167 e.getValue().printStackTrace(out); 168 e.getValue().printStackTrace(); 169 }); 170 if (tasksFailed) { 171 System.out.println("WARNING: Some tasks failed"); 172 } 173 } finally { 174 out.println("\n=========================\n"); 175 } 176 } 177 uris()178 private String[] uris() { 179 return new String[] { 180 httpURI_fixed, 181 httpURI_chunk, 182 httpsURI_fixed, 183 httpsURI_chunk, 184 http2URI_fixed, 185 http2URI_chunk, 186 https2URI_fixed, 187 https2URI_chunk, 188 }; 189 } 190 191 static final class SemaphoreStallerSupplier 192 implements Supplier<SemaphoreStaller> { 193 @Override get()194 public SemaphoreStaller get() { 195 return new SemaphoreStaller(); 196 } 197 @Override toString()198 public String toString() { 199 return "SemaphoreStaller"; 200 } 201 } 202 203 @DataProvider(name = "noStalls") noThrows()204 public Object[][] noThrows() { 205 String[] uris = uris(); 206 Object[][] result = new Object[uris.length * 2][]; 207 int i = 0; 208 for (boolean sameClient : List.of(false, true)) { 209 for (String uri: uris()) { 210 result[i++] = new Object[] {uri, sameClient}; 211 } 212 } 213 assert i == uris.length * 2; 214 return result; 215 } 216 217 @DataProvider(name = "variants") variants()218 public Object[][] variants() { 219 String[] uris = uris(); 220 Object[][] result = new Object[uris.length * 2][]; 221 int i = 0; 222 Supplier<? extends Staller> s = new SemaphoreStallerSupplier(); 223 for (Supplier<? extends Staller> staller : List.of(s)) { 224 for (boolean sameClient : List.of(false, true)) { 225 for (String uri : uris()) { 226 result[i++] = new Object[]{uri, sameClient, staller}; 227 } 228 } 229 } 230 assert i == uris.length * 2; 231 return result; 232 } 233 makeNewClient()234 private HttpClient makeNewClient() { 235 clientCount.incrementAndGet(); 236 return HttpClient.newBuilder() 237 .executor(executor) 238 .sslContext(sslContext) 239 .build(); 240 } 241 newHttpClient(boolean share)242 HttpClient newHttpClient(boolean share) { 243 if (!share) return makeNewClient(); 244 HttpClient shared = sharedClient; 245 if (shared != null) return shared; 246 synchronized (this) { 247 shared = sharedClient; 248 if (shared == null) { 249 shared = sharedClient = makeNewClient(); 250 } 251 return shared; 252 } 253 } 254 255 @Test(dataProvider = "noStalls") testNoStalls(String uri, boolean sameClient)256 public void testNoStalls(String uri, boolean sameClient) 257 throws Exception { 258 HttpClient client = null; 259 out.printf("%ntestNoStalls(%s, %b)%n", uri, sameClient); 260 for (int i=0; i< ITERATION_COUNT; i++) { 261 if (!sameClient || client == null) 262 client = newHttpClient(sameClient); 263 264 HttpRequest req = HttpRequest.newBuilder(URI.create(uri)) 265 .build(); 266 BodyHandler<String> handler = 267 new StallingBodyHandler((w) -> {}, 268 BodyHandlers.ofString()); 269 HttpResponse<String> response = client.send(req, handler); 270 String body = response.body(); 271 assertEquals(URI.create(body).getPath(), URI.create(uri).getPath()); 272 } 273 } 274 275 @Test(dataProvider = "variants") testAsStringAsync(String uri, boolean sameClient, Supplier<Staller> s)276 public void testAsStringAsync(String uri, 277 boolean sameClient, 278 Supplier<Staller> s) 279 throws Exception 280 { 281 Staller staller = s.get(); 282 String test = format("testAsStringAsync(%s, %b, %s)", 283 uri, sameClient, staller); 284 testDependent(test, uri, sameClient, BodyHandlers::ofString, 285 this::finish, this::extractString, staller); 286 } 287 288 @Test(dataProvider = "variants") testAsLinesAsync(String uri, boolean sameClient, Supplier<Staller> s)289 public void testAsLinesAsync(String uri, 290 boolean sameClient, 291 Supplier<Staller> s) 292 throws Exception 293 { 294 Staller staller = s.get(); 295 String test = format("testAsLinesAsync(%s, %b, %s)", 296 uri, sameClient, staller); 297 testDependent(test, uri, sameClient, BodyHandlers::ofLines, 298 this::finish, this::extractStream, staller); 299 } 300 301 @Test(dataProvider = "variants") testAsInputStreamAsync(String uri, boolean sameClient, Supplier<Staller> s)302 public void testAsInputStreamAsync(String uri, 303 boolean sameClient, 304 Supplier<Staller> s) 305 throws Exception 306 { 307 Staller staller = s.get(); 308 String test = format("testAsInputStreamAsync(%s, %b, %s)", 309 uri, sameClient, staller); 310 testDependent(test, uri, sameClient, BodyHandlers::ofInputStream, 311 this::finish, this::extractInputStream, staller); 312 } 313 testDependent(String name, String uri, boolean sameClient, Supplier<BodyHandler<T>> handlers, Finisher finisher, Extractor extractor, Staller staller)314 private <T,U> void testDependent(String name, String uri, boolean sameClient, 315 Supplier<BodyHandler<T>> handlers, 316 Finisher finisher, 317 Extractor extractor, 318 Staller staller) 319 throws Exception 320 { 321 out.printf("%n%s%s%n", now(), name); 322 try { 323 testDependent(uri, sameClient, handlers, finisher, extractor, staller); 324 } catch (Error | Exception x) { 325 FAILURES.putIfAbsent(name, x); 326 throw x; 327 } 328 } 329 testDependent(String uri, boolean sameClient, Supplier<BodyHandler<T>> handlers, Finisher finisher, Extractor extractor, Staller staller)330 private <T,U> void testDependent(String uri, boolean sameClient, 331 Supplier<BodyHandler<T>> handlers, 332 Finisher finisher, 333 Extractor extractor, 334 Staller staller) 335 throws Exception 336 { 337 HttpClient client = null; 338 for (Where where : EnumSet.of(Where.BODY_HANDLER)) { 339 if (!sameClient || client == null) 340 client = newHttpClient(sameClient); 341 342 HttpRequest req = HttpRequest. 343 newBuilder(URI.create(uri)) 344 .build(); 345 BodyHandler<T> handler = 346 new StallingBodyHandler(where.select(staller), handlers.get()); 347 System.out.println("try stalling in " + where); 348 staller.acquire(); 349 assert staller.willStall(); 350 CompletableFuture<HttpResponse<T>> responseCF = client.sendAsync(req, handler); 351 assert !responseCF.isDone(); 352 finisher.finish(where, responseCF, staller, extractor); 353 } 354 } 355 356 enum Where { 357 BODY_HANDLER, ON_SUBSCRIBE, ON_NEXT, ON_COMPLETE, ON_ERROR, GET_BODY, BODY_CF; select(Consumer<Where> consumer)358 public Consumer<Where> select(Consumer<Where> consumer) { 359 return new Consumer<Where>() { 360 @Override 361 public void accept(Where where) { 362 if (Where.this == where) { 363 consumer.accept(where); 364 } 365 } 366 }; 367 } 368 } 369 370 interface Extractor<T> { 371 public List<String> extract(HttpResponse<T> resp); 372 } 373 374 final List<String> extractString(HttpResponse<String> resp) { 375 return List.of(resp.body()); 376 } 377 378 final List<String> extractStream(HttpResponse<Stream<String>> resp) { 379 return resp.body().collect(toList()); 380 } 381 382 final List<String> extractInputStream(HttpResponse<InputStream> resp) { 383 try (InputStream is = resp.body()) { 384 return new BufferedReader(new InputStreamReader(is)) 385 .lines().collect(toList()); 386 } catch (IOException x) { 387 throw new CompletionException(x); 388 } 389 } 390 391 interface Finisher<T> { 392 public void finish(Where w, 393 CompletableFuture<HttpResponse<T>> cf, 394 Staller staller, 395 Extractor extractor); 396 } 397 398 Optional<StackFrame> findFrame(Stream<StackFrame> s, String name) { 399 return s.filter((f) -> f.getClassName().contains(name)) 400 .filter((f) -> f.getDeclaringClass().getModule().equals(HttpClient.class.getModule())) 401 .findFirst(); 402 } 403 404 static final Predicate<StackFrame> DAT = sfe -> 405 sfe.getClassName().startsWith("DependentActionsTest"); 406 static final Predicate<StackFrame> JUC = sfe -> 407 sfe.getClassName().startsWith("java.util.concurrent"); 408 static final Predicate<StackFrame> JLT = sfe -> 409 sfe.getClassName().startsWith("java.lang.Thread"); 410 static final Predicate<StackFrame> NotDATorJUCorJLT = Predicate.not(DAT.or(JUC).or(JLT)); 411 412 413 <T> void checkThreadAndStack(Thread thread, 414 AtomicReference<RuntimeException> failed, 415 T result, 416 Throwable error) { 417 //failed.set(new RuntimeException("Dependant action was executed in " + thread)); 418 List<StackFrame> otherFrames = WALKER.walk(s -> s.filter(NotDATorJUCorJLT).collect(toList())); 419 if (!otherFrames.isEmpty()) { 420 System.out.println("Found unexpected trace: "); 421 otherFrames.forEach(f -> System.out.printf("\t%s%n", f)); 422 failed.set(new RuntimeException("Dependant action has unexpected frame in " + 423 Thread.currentThread() + ": " + otherFrames.get(0))); 424 425 } 426 } 427 428 <T> void finish(Where w, CompletableFuture<HttpResponse<T>> cf, 429 Staller staller, 430 Extractor<T> extractor) { 431 Thread thread = Thread.currentThread(); 432 AtomicReference<RuntimeException> failed = new AtomicReference<>(); 433 CompletableFuture<HttpResponse<T>> done = cf.whenComplete( 434 (r,t) -> checkThreadAndStack(thread, failed, r, t)); 435 assert !cf.isDone(); 436 try { 437 Thread.sleep(100); 438 } catch (Throwable t) {/* don't care */} 439 assert !cf.isDone(); 440 staller.release(); 441 try { 442 HttpResponse<T> response = done.join(); 443 List<String> result = extractor.extract(response); 444 RuntimeException error = failed.get(); 445 if (error != null) { 446 throw new RuntimeException("Test failed in " 447 + w + ": " + response, error); 448 } 449 assertEquals(result, List.of(response.request().uri().getPath())); 450 } finally { 451 staller.reset(); 452 } 453 } 454 455 interface Staller extends Consumer<Where> { 456 void release(); 457 void acquire(); 458 void reset(); 459 boolean willStall(); 460 } 461 462 static final class SemaphoreStaller implements Staller { 463 final Semaphore sem = new Semaphore(1); 464 @Override 465 public void accept(Where where) { 466 System.out.println("Acquiring semaphore in " 467 + where + " permits=" + sem.availablePermits()); 468 sem.acquireUninterruptibly(); 469 System.out.println("Semaphored acquired in " + where); 470 } 471 472 @Override 473 public void release() { 474 System.out.println("Releasing semaphore: permits=" 475 + sem.availablePermits()); 476 sem.release(); 477 } 478 479 @Override 480 public void acquire() { 481 sem.acquireUninterruptibly(); 482 System.out.println("Semaphored acquired"); 483 } 484 485 @Override 486 public void reset() { 487 System.out.println("Reseting semaphore: permits=" 488 + sem.availablePermits()); 489 sem.drainPermits(); 490 sem.release(); 491 System.out.println("Semaphore reset: permits=" 492 + sem.availablePermits()); 493 } 494 495 @Override 496 public boolean willStall() { 497 return sem.availablePermits() <= 0; 498 } 499 500 @Override 501 public String toString() { 502 return "SemaphoreStaller"; 503 } 504 } 505 506 static final class StallingBodyHandler<T> implements BodyHandler<T> { 507 final Consumer<Where> stalling; 508 final BodyHandler<T> bodyHandler; 509 StallingBodyHandler(Consumer<Where> stalling, BodyHandler<T> bodyHandler) { 510 this.stalling = stalling; 511 this.bodyHandler = bodyHandler; 512 } 513 @Override 514 public BodySubscriber<T> apply(HttpResponse.ResponseInfo rinfo) { 515 stalling.accept(Where.BODY_HANDLER); 516 BodySubscriber<T> subscriber = bodyHandler.apply(rinfo); 517 return new StallingBodySubscriber(stalling, subscriber); 518 } 519 } 520 521 static final class StallingBodySubscriber<T> implements BodySubscriber<T> { 522 private final BodySubscriber<T> subscriber; 523 volatile boolean onSubscribeCalled; 524 final Consumer<Where> stalling; 525 StallingBodySubscriber(Consumer<Where> stalling, BodySubscriber<T> subscriber) { 526 this.stalling = stalling; 527 this.subscriber = subscriber; 528 } 529 530 @Override 531 public void onSubscribe(Flow.Subscription subscription) { 532 //out.println("onSubscribe "); 533 onSubscribeCalled = true; 534 stalling.accept(Where.ON_SUBSCRIBE); 535 subscriber.onSubscribe(subscription); 536 } 537 538 @Override 539 public void onNext(List<ByteBuffer> item) { 540 // out.println("onNext " + item); 541 assertTrue(onSubscribeCalled); 542 stalling.accept(Where.ON_NEXT); 543 subscriber.onNext(item); 544 } 545 546 @Override 547 public void onError(Throwable throwable) { 548 //out.println("onError"); 549 assertTrue(onSubscribeCalled); 550 stalling.accept(Where.ON_ERROR); 551 subscriber.onError(throwable); 552 } 553 554 @Override 555 public void onComplete() { 556 //out.println("onComplete"); 557 assertTrue(onSubscribeCalled, "onComplete called before onSubscribe"); 558 stalling.accept(Where.ON_COMPLETE); 559 subscriber.onComplete(); 560 } 561 562 @Override 563 public CompletionStage<T> getBody() { 564 stalling.accept(Where.GET_BODY); 565 try { 566 stalling.accept(Where.BODY_CF); 567 } catch (Throwable t) { 568 return CompletableFuture.failedFuture(t); 569 } 570 return subscriber.getBody(); 571 } 572 } 573 574 575 @BeforeTest 576 public void setup() throws Exception { 577 sslContext = new SimpleSSLContext().get(); 578 if (sslContext == null) 579 throw new AssertionError("Unexpected null sslContext"); 580 581 // HTTP/1.1 582 HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler(); 583 HttpTestHandler h1_chunkHandler = new HTTP_ChunkedHandler(); 584 InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0); 585 httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0)); 586 httpTestServer.addHandler(h1_fixedLengthHandler, "/http1/fixed"); 587 httpTestServer.addHandler(h1_chunkHandler, "/http1/chunk"); 588 httpURI_fixed = "http://" + httpTestServer.serverAuthority() + "/http1/fixed/x"; 589 httpURI_chunk = "http://" + httpTestServer.serverAuthority() + "/http1/chunk/x"; 590 591 HttpsServer httpsServer = HttpsServer.create(sa, 0); 592 httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext)); 593 httpsTestServer = HttpTestServer.of(httpsServer); 594 httpsTestServer.addHandler(h1_fixedLengthHandler, "/https1/fixed"); 595 httpsTestServer.addHandler(h1_chunkHandler, "/https1/chunk"); 596 httpsURI_fixed = "https://" + httpsTestServer.serverAuthority() + "/https1/fixed/x"; 597 httpsURI_chunk = "https://" + httpsTestServer.serverAuthority() + "/https1/chunk/x"; 598 599 // HTTP/2 600 HttpTestHandler h2_fixedLengthHandler = new HTTP_FixedLengthHandler(); 601 HttpTestHandler h2_chunkedHandler = new HTTP_ChunkedHandler(); 602 603 http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0)); 604 http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed"); 605 http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk"); 606 http2URI_fixed = "http://" + http2TestServer.serverAuthority() + "/http2/fixed/x"; 607 http2URI_chunk = "http://" + http2TestServer.serverAuthority() + "/http2/chunk/x"; 608 609 https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext)); 610 https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed"); 611 https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk"); 612 https2URI_fixed = "https://" + https2TestServer.serverAuthority() + "/https2/fixed/x"; 613 https2URI_chunk = "https://" + https2TestServer.serverAuthority() + "/https2/chunk/x"; 614 615 serverCount.addAndGet(4); 616 httpTestServer.start(); 617 httpsTestServer.start(); 618 http2TestServer.start(); 619 https2TestServer.start(); 620 } 621 622 @AfterTest 623 public void teardown() throws Exception { 624 sharedClient = null; 625 httpTestServer.stop(); 626 httpsTestServer.stop(); 627 http2TestServer.stop(); 628 https2TestServer.stop(); 629 } 630 631 static class HTTP_FixedLengthHandler implements HttpTestHandler { 632 @Override 633 public void handle(HttpTestExchange t) throws IOException { 634 out.println("HTTP_FixedLengthHandler received request to " + t.getRequestURI()); 635 try (InputStream is = t.getRequestBody()) { 636 is.readAllBytes(); 637 } 638 byte[] resp = t.getRequestURI().getPath().getBytes(StandardCharsets.UTF_8); 639 t.sendResponseHeaders(200, resp.length); //fixed content length 640 try (OutputStream os = t.getResponseBody()) { 641 os.write(resp); 642 } 643 } 644 } 645 646 static class HTTP_ChunkedHandler implements HttpTestHandler { 647 @Override 648 public void handle(HttpTestExchange t) throws IOException { 649 out.println("HTTP_ChunkedHandler received request to " + t.getRequestURI()); 650 byte[] resp = t.getRequestURI().getPath().toString().getBytes(StandardCharsets.UTF_8); 651 try (InputStream is = t.getRequestBody()) { 652 is.readAllBytes(); 653 } 654 t.sendResponseHeaders(200, -1); // chunked/variable 655 try (OutputStream os = t.getResponseBody()) { 656 os.write(resp); 657 } 658 } 659 } 660 } 661