Path: blob/master/test/jdk/java/net/httpclient/DependentPromiseActionsTest.java
41152 views
/*1* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @summary Verify that dependent synchronous actions added before the promise CF26* completes are executed either asynchronously in an executor when the27* CF later completes, or in the user thread that joins.28* @library /test/lib http2/server29* @build jdk.test.lib.net.SimpleSSLContext HttpServerAdapters DependentPromiseActionsTest30* @modules java.base/sun.net.www.http31* java.net.http/jdk.internal.net.http.common32* java.net.http/jdk.internal.net.http.frame33* java.net.http/jdk.internal.net.http.hpack34* @run testng/othervm -Djdk.internal.httpclient.debug=true DependentPromiseActionsTest35* @run testng/othervm/java.security.policy=dependent.policy36* -Djdk.internal.httpclient.debug=true DependentPromiseActionsTest37*/3839import java.io.BufferedReader;40import java.io.InputStreamReader;41import java.lang.StackWalker.StackFrame;42import jdk.test.lib.net.SimpleSSLContext;43import org.testng.annotations.AfterTest;44import org.testng.annotations.AfterClass;45import org.testng.annotations.BeforeTest;46import org.testng.annotations.DataProvider;47import org.testng.annotations.Test;4849import javax.net.ssl.SSLContext;50import java.io.IOException;51import java.io.InputStream;52import java.io.OutputStream;53import java.net.URI;54import java.net.URISyntaxException;55import java.net.http.HttpClient;56import java.net.http.HttpHeaders;57import java.net.http.HttpRequest;58import java.net.http.HttpResponse;59import java.net.http.HttpResponse.BodyHandler;60import java.net.http.HttpResponse.BodyHandlers;61import java.net.http.HttpResponse.BodySubscriber;62import java.net.http.HttpResponse.PushPromiseHandler;63import java.nio.ByteBuffer;64import java.nio.charset.StandardCharsets;65import java.util.EnumSet;66import java.util.List;67import java.util.Map;68import java.util.Optional;69import java.util.concurrent.CompletableFuture;70import java.util.concurrent.CompletionException;71import java.util.concurrent.CompletionStage;72import java.util.concurrent.ConcurrentHashMap;73import java.util.concurrent.ConcurrentMap;74import java.util.concurrent.Executor;75import java.util.concurrent.Executors;76import java.util.concurrent.Flow;77import java.util.concurrent.Semaphore;78import java.util.concurrent.atomic.AtomicLong;79import java.util.concurrent.atomic.AtomicReference;80import java.util.function.BiPredicate;81import java.util.function.Consumer;82import java.util.function.Function;83import java.util.function.Supplier;84import java.util.stream.Collectors;85import java.util.stream.Stream;8687import static java.lang.System.err;88import static java.lang.System.out;89import static java.lang.String.format;90import static java.nio.charset.StandardCharsets.UTF_8;91import static org.testng.Assert.assertEquals;92import static org.testng.Assert.assertTrue;9394public class DependentPromiseActionsTest implements HttpServerAdapters {9596SSLContext sslContext;97HttpTestServer http2TestServer; // HTTP/2 ( h2c )98HttpTestServer https2TestServer; // HTTP/2 ( h2 )99String http2URI_fixed;100String http2URI_chunk;101String https2URI_fixed;102String https2URI_chunk;103104static final StackWalker WALKER =105StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);106107static final int ITERATION_COUNT = 1;108// a shared executor helps reduce the amount of threads created by the test109static final Executor executor = new TestExecutor(Executors.newCachedThreadPool());110static final ConcurrentMap<String, Throwable> FAILURES = new ConcurrentHashMap<>();111static volatile boolean tasksFailed;112static final AtomicLong serverCount = new AtomicLong();113static final AtomicLong clientCount = new AtomicLong();114static final long start = System.nanoTime();115public static String now() {116long now = System.nanoTime() - start;117long secs = now / 1000_000_000;118long mill = (now % 1000_000_000) / 1000_000;119long nan = now % 1000_000;120return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);121}122123private volatile HttpClient sharedClient;124125static class TestExecutor implements Executor {126final AtomicLong tasks = new AtomicLong();127Executor executor;128TestExecutor(Executor executor) {129this.executor = executor;130}131132@Override133public void execute(Runnable command) {134long id = tasks.incrementAndGet();135executor.execute(() -> {136try {137command.run();138} catch (Throwable t) {139tasksFailed = true;140System.out.printf(now() + "Task %s failed: %s%n", id, t);141System.err.printf(now() + "Task %s failed: %s%n", id, t);142FAILURES.putIfAbsent("Task " + id, t);143throw t;144}145});146}147}148149@AfterClass150static final void printFailedTests() {151out.println("\n=========================");152try {153out.printf("%n%sCreated %d servers and %d clients%n",154now(), serverCount.get(), clientCount.get());155if (FAILURES.isEmpty()) return;156out.println("Failed tests: ");157FAILURES.entrySet().forEach((e) -> {158out.printf("\t%s: %s%n", e.getKey(), e.getValue());159e.getValue().printStackTrace(out);160e.getValue().printStackTrace();161});162if (tasksFailed) {163System.out.println("WARNING: Some tasks failed");164}165} finally {166out.println("\n=========================\n");167}168}169170private String[] uris() {171return new String[] {172http2URI_fixed,173http2URI_chunk,174https2URI_fixed,175https2URI_chunk,176};177}178179enum SubscriberType {EAGER, LAZZY}180181static final class SemaphoreStallerSupplier182implements Supplier<SemaphoreStaller> {183@Override184public SemaphoreStaller get() {185return new SemaphoreStaller();186}187@Override188public String toString() {189return "SemaphoreStaller";190}191}192193@DataProvider(name = "noStalls")194public Object[][] noThrows() {195String[] uris = uris();196Object[][] result = new Object[uris.length * 2][];197int i = 0;198for (boolean sameClient : List.of(false, true)) {199for (String uri: uris()) {200result[i++] = new Object[] {uri, sameClient};201}202}203assert i == uris.length * 2;204return result;205}206207@DataProvider(name = "variants")208public Object[][] variants() {209String[] uris = uris();210Object[][] result = new Object[uris.length * 2][];211int i = 0;212Supplier<? extends Staller> s = new SemaphoreStallerSupplier();213for (Supplier<? extends Staller> staller : List.of(s)) {214for (boolean sameClient : List.of(false, true)) {215for (String uri : uris()) {216result[i++] = new Object[]{uri, sameClient, staller};217}218}219}220assert i == uris.length * 2;221return result;222}223224private HttpClient makeNewClient() {225clientCount.incrementAndGet();226return HttpClient.newBuilder()227.executor(executor)228.sslContext(sslContext)229.build();230}231232HttpClient newHttpClient(boolean share) {233if (!share) return makeNewClient();234HttpClient shared = sharedClient;235if (shared != null) return shared;236synchronized (this) {237shared = sharedClient;238if (shared == null) {239shared = sharedClient = makeNewClient();240}241return shared;242}243}244245@Test(dataProvider = "noStalls")246public void testNoStalls(String uri, boolean sameClient)247throws Exception {248HttpClient client = null;249out.printf("%ntestNoStalls(%s, %b)%n", uri, sameClient);250for (int i=0; i< ITERATION_COUNT; i++) {251if (!sameClient || client == null)252client = newHttpClient(sameClient);253254HttpRequest req = HttpRequest.newBuilder(URI.create(uri))255.build();256BodyHandler<Stream<String>> handler =257new StallingBodyHandler((w) -> {},258BodyHandlers.ofLines());259Map<HttpRequest, CompletableFuture<HttpResponse<Stream<String>>>> pushPromises =260new ConcurrentHashMap<>();261PushPromiseHandler<Stream<String>> pushHandler = new PushPromiseHandler<>() {262@Override263public void applyPushPromise(HttpRequest initiatingRequest,264HttpRequest pushPromiseRequest,265Function<BodyHandler<Stream<String>>,266CompletableFuture<HttpResponse<Stream<String>>>>267acceptor) {268pushPromises.putIfAbsent(pushPromiseRequest, acceptor.apply(handler));269}270};271HttpResponse<Stream<String>> response =272client.sendAsync(req, BodyHandlers.ofLines(), pushHandler).get();273String body = response.body().collect(Collectors.joining("|"));274assertEquals(URI.create(body).getPath(), URI.create(uri).getPath());275for (HttpRequest promised : pushPromises.keySet()) {276out.printf("%s Received promise: %s%n\tresponse: %s%n",277now(), promised, pushPromises.get(promised).get());278String promisedBody = pushPromises.get(promised).get().body()279.collect(Collectors.joining("|"));280assertEquals(promisedBody, promised.uri().toASCIIString());281}282assertEquals(3, pushPromises.size());283}284}285286@Test(dataProvider = "variants")287public void testAsStringAsync(String uri,288boolean sameClient,289Supplier<Staller> stallers)290throws Exception291{292String test = format("testAsStringAsync(%s, %b, %s)",293uri, sameClient, stallers);294testDependent(test, uri, sameClient, BodyHandlers::ofString,295this::finish, this::extractString, stallers,296SubscriberType.EAGER);297}298299@Test(dataProvider = "variants")300public void testAsLinesAsync(String uri,301boolean sameClient,302Supplier<Staller> stallers)303throws Exception304{305String test = format("testAsLinesAsync(%s, %b, %s)",306uri, sameClient, stallers);307testDependent(test, uri, sameClient, BodyHandlers::ofLines,308this::finish, this::extractStream, stallers,309SubscriberType.LAZZY);310}311312@Test(dataProvider = "variants")313public void testAsInputStreamAsync(String uri,314boolean sameClient,315Supplier<Staller> stallers)316throws Exception317{318String test = format("testAsInputStreamAsync(%s, %b, %s)",319uri, sameClient, stallers);320testDependent(test, uri, sameClient, BodyHandlers::ofInputStream,321this::finish, this::extractInputStream, stallers,322SubscriberType.LAZZY);323}324325private <T,U> void testDependent(String name, String uri, boolean sameClient,326Supplier<BodyHandler<T>> handlers,327Finisher finisher,328Extractor<T> extractor,329Supplier<Staller> stallers,330SubscriberType subscriberType)331throws Exception332{333out.printf("%n%s%s%n", now(), name);334try {335testDependent(uri, sameClient, handlers, finisher,336extractor, stallers, subscriberType);337} catch (Error | Exception x) {338FAILURES.putIfAbsent(name, x);339throw x;340}341}342343private <T,U> void testDependent(String uri, boolean sameClient,344Supplier<BodyHandler<T>> handlers,345Finisher finisher,346Extractor<T> extractor,347Supplier<Staller> stallers,348SubscriberType subscriberType)349throws Exception350{351HttpClient client = null;352for (Where where : EnumSet.of(Where.BODY_HANDLER)) {353if (!sameClient || client == null)354client = newHttpClient(sameClient);355356HttpRequest req = HttpRequest.357newBuilder(URI.create(uri))358.build();359StallingPushPromiseHandler<T> promiseHandler =360new StallingPushPromiseHandler<>(where, handlers, stallers);361BodyHandler<T> handler = handlers.get();362System.out.println("try stalling in " + where);363CompletableFuture<HttpResponse<T>> responseCF =364client.sendAsync(req, handler, promiseHandler);365assert subscriberType == SubscriberType.LAZZY || !responseCF.isDone();366finisher.finish(where, responseCF, promiseHandler, extractor);367}368}369370enum Where {371ON_PUSH_PROMISE, BODY_HANDLER, ON_SUBSCRIBE, ON_NEXT, ON_COMPLETE, ON_ERROR, GET_BODY, BODY_CF;372public Consumer<Where> select(Consumer<Where> consumer) {373return new Consumer<Where>() {374@Override375public void accept(Where where) {376if (Where.this == where) {377consumer.accept(where);378}379}380};381}382}383384static final class StallingPushPromiseHandler<T> implements PushPromiseHandler<T> {385386static final class Tuple<U> {387public final CompletableFuture<HttpResponse<U>> response;388public final Staller staller;389public final AtomicReference<RuntimeException> failed;390Tuple(AtomicReference<RuntimeException> failed,391CompletableFuture<HttpResponse<U>> response,392Staller staller) {393this.response = response;394this.staller = staller;395this.failed = failed;396}397}398399public final ConcurrentMap<HttpRequest, Tuple<T>> promiseMap =400new ConcurrentHashMap<>();401private final Supplier<Staller> stallers;402private final Supplier<BodyHandler<T>> handlers;403private final Where where;404private final Thread thread = Thread.currentThread(); // main thread405406StallingPushPromiseHandler(Where where,407Supplier<BodyHandler<T>> handlers,408Supplier<Staller> stallers) {409this.where = where;410this.handlers = handlers;411this.stallers = stallers;412}413414@Override415public void applyPushPromise(HttpRequest initiatingRequest,416HttpRequest pushPromiseRequest,417Function<BodyHandler<T>,418CompletableFuture<HttpResponse<T>>> acceptor) {419AtomicReference<RuntimeException> failed = new AtomicReference<>();420Staller staller = stallers.get();421staller.acquire();422assert staller.willStall();423try {424BodyHandler handler = new StallingBodyHandler<>(425where.select(staller), handlers.get());426CompletableFuture<HttpResponse<T>> cf = acceptor.apply(handler);427Tuple<T> tuple = new Tuple(failed, cf, staller);428promiseMap.putIfAbsent(pushPromiseRequest, tuple);429CompletableFuture<?> done = cf.whenComplete(430(r, t) -> checkThreadAndStack(thread, failed, r, t));431assert !cf.isDone();432} finally {433staller.release();434}435}436}437438interface Extractor<T> {439public List<String> extract(HttpResponse<T> resp);440}441442final List<String> extractString(HttpResponse<String> resp) {443return List.of(resp.body());444}445446final List<String> extractStream(HttpResponse<Stream<String>> resp) {447return resp.body().collect(Collectors.toList());448}449450final List<String> extractInputStream(HttpResponse<InputStream> resp) {451try (InputStream is = resp.body()) {452return new BufferedReader(new InputStreamReader(is))453.lines().collect(Collectors.toList());454} catch (IOException x) {455throw new CompletionException(x);456}457}458459interface Finisher<T> {460public void finish(Where w,461CompletableFuture<HttpResponse<T>> cf,462StallingPushPromiseHandler<T> ph,463Extractor<T> extractor);464}465466static Optional<StackFrame> findFrame(Stream<StackFrame> s, String name) {467return s.filter((f) -> f.getClassName().contains(name))468.filter((f) -> f.getDeclaringClass().getModule().equals(HttpClient.class.getModule()))469.findFirst();470}471472static <T> void checkThreadAndStack(Thread thread,473AtomicReference<RuntimeException> failed,474T result,475Throwable error) {476if (Thread.currentThread() == thread) {477//failed.set(new RuntimeException("Dependant action was executed in " + thread));478List<StackFrame> httpStack = WALKER.walk(s -> s.filter(f -> f.getDeclaringClass()479.getModule().equals(HttpClient.class.getModule()))480.collect(Collectors.toList()));481if (!httpStack.isEmpty()) {482System.out.println("Found unexpected trace: ");483httpStack.forEach(f -> System.out.printf("\t%s%n", f));484failed.set(new RuntimeException("Dependant action has unexpected frame in " +485Thread.currentThread() + ": " + httpStack.get(0)));486487} return;488} else if (System.getSecurityManager() != null) {489Optional<StackFrame> sf = WALKER.walk(s -> findFrame(s, "PrivilegedRunnable"));490if (!sf.isPresent()) {491failed.set(new RuntimeException("Dependant action does not have expected frame in "492+ Thread.currentThread()));493return;494} else {495System.out.println("Found expected frame: " + sf.get());496}497} else {498List<StackFrame> httpStack = WALKER.walk(s -> s.filter(f -> f.getDeclaringClass()499.getModule().equals(HttpClient.class.getModule()))500.collect(Collectors.toList()));501if (!httpStack.isEmpty()) {502System.out.println("Found unexpected trace: ");503httpStack.forEach(f -> System.out.printf("\t%s%n", f));504failed.set(new RuntimeException("Dependant action has unexpected frame in " +505Thread.currentThread() + ": " + httpStack.get(0)));506507}508}509}510511<T> void finish(Where w,512StallingPushPromiseHandler.Tuple<T> tuple,513Extractor<T> extractor) {514AtomicReference<RuntimeException> failed = tuple.failed;515CompletableFuture<HttpResponse<T>> done = tuple.response;516Staller staller = tuple.staller;517try {518HttpResponse<T> response = done.join();519List<String> result = extractor.extract(response);520URI uri = response.uri();521RuntimeException error = failed.get();522if (error != null) {523throw new RuntimeException("Test failed in "524+ w + ": " + uri, error);525}526assertEquals(result, List.of(response.request().uri().toASCIIString()));527} finally {528staller.reset();529}530}531532<T> void finish(Where w,533CompletableFuture<HttpResponse<T>> cf,534StallingPushPromiseHandler<T> ph,535Extractor<T> extractor) {536HttpResponse<T> response = cf.join();537List<String> result = extractor.extract(response);538for (HttpRequest req : ph.promiseMap.keySet()) {539finish(w, ph.promiseMap.get(req), extractor);540}541assertEquals(ph.promiseMap.size(), 3,542"Expected 3 push promises for " + w + " in "543+ response.request().uri());544assertEquals(result, List.of(response.request().uri().toASCIIString()));545546}547548interface Staller extends Consumer<Where> {549void release();550void acquire();551void reset();552boolean willStall();553}554555static final class SemaphoreStaller implements Staller {556final Semaphore sem = new Semaphore(1);557@Override558public void accept(Where where) {559sem.acquireUninterruptibly();560}561562@Override563public void release() {564sem.release();565}566567@Override568public void acquire() {569sem.acquireUninterruptibly();570}571572@Override573public void reset() {574sem.drainPermits();575sem.release();576}577578@Override579public boolean willStall() {580return sem.availablePermits() <= 0;581}582583@Override584public String toString() {585return "SemaphoreStaller";586}587}588589static final class StallingBodyHandler<T> implements BodyHandler<T> {590final Consumer<Where> stalling;591final BodyHandler<T> bodyHandler;592StallingBodyHandler(Consumer<Where> stalling, BodyHandler<T> bodyHandler) {593this.stalling = stalling;594this.bodyHandler = bodyHandler;595}596@Override597public BodySubscriber<T> apply(HttpResponse.ResponseInfo rinfo) {598stalling.accept(Where.BODY_HANDLER);599BodySubscriber<T> subscriber = bodyHandler.apply(rinfo);600return new StallingBodySubscriber(stalling, subscriber);601}602}603604static final class StallingBodySubscriber<T> implements BodySubscriber<T> {605private final BodySubscriber<T> subscriber;606volatile boolean onSubscribeCalled;607final Consumer<Where> stalling;608StallingBodySubscriber(Consumer<Where> stalling, BodySubscriber<T> subscriber) {609this.stalling = stalling;610this.subscriber = subscriber;611}612613@Override614public void onSubscribe(Flow.Subscription subscription) {615//out.println("onSubscribe ");616onSubscribeCalled = true;617stalling.accept(Where.ON_SUBSCRIBE);618subscriber.onSubscribe(subscription);619}620621@Override622public void onNext(List<ByteBuffer> item) {623// out.println("onNext " + item);624assertTrue(onSubscribeCalled);625stalling.accept(Where.ON_NEXT);626subscriber.onNext(item);627}628629@Override630public void onError(Throwable throwable) {631//out.println("onError");632assertTrue(onSubscribeCalled);633stalling.accept(Where.ON_ERROR);634subscriber.onError(throwable);635}636637@Override638public void onComplete() {639//out.println("onComplete");640assertTrue(onSubscribeCalled, "onComplete called before onSubscribe");641stalling.accept(Where.ON_COMPLETE);642subscriber.onComplete();643}644645@Override646public CompletionStage<T> getBody() {647stalling.accept(Where.GET_BODY);648try {649stalling.accept(Where.BODY_CF);650} catch (Throwable t) {651return CompletableFuture.failedFuture(t);652}653return subscriber.getBody();654}655}656657658@BeforeTest659public void setup() throws Exception {660sslContext = new SimpleSSLContext().get();661if (sslContext == null)662throw new AssertionError("Unexpected null sslContext");663664// HTTP/2665HttpTestHandler h2_fixedLengthHandler = new HTTP_FixedLengthHandler();666HttpTestHandler h2_chunkedHandler = new HTTP_ChunkedHandler();667668http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0));669http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed");670http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk");671http2URI_fixed = "http://" + http2TestServer.serverAuthority() + "/http2/fixed/y";672http2URI_chunk = "http://" + http2TestServer.serverAuthority() + "/http2/chunk/y";673674https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext));675https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed");676https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk");677https2URI_fixed = "https://" + https2TestServer.serverAuthority() + "/https2/fixed/y";678https2URI_chunk = "https://" + https2TestServer.serverAuthority() + "/https2/chunk/y";679680serverCount.addAndGet(4);681http2TestServer.start();682https2TestServer.start();683}684685@AfterTest686public void teardown() throws Exception {687sharedClient = null;688http2TestServer.stop();689https2TestServer.stop();690}691692static final BiPredicate<String,String> ACCEPT_ALL = (x, y) -> true;693694private static void pushPromiseFor(HttpTestExchange t,695URI requestURI,696String pushPath,697boolean fixed)698throws IOException699{700try {701URI promise = new URI(requestURI.getScheme(),702requestURI.getAuthority(),703pushPath, null, null);704byte[] promiseBytes = promise.toASCIIString().getBytes(UTF_8);705out.printf("TestServer: %s Pushing promise: %s%n", now(), promise);706err.printf("TestServer: %s Pushing promise: %s%n", now(), promise);707HttpHeaders headers;708if (fixed) {709String length = String.valueOf(promiseBytes.length);710headers = HttpHeaders.of(Map.of("Content-Length", List.of(length)),711ACCEPT_ALL);712} else {713headers = HttpHeaders.of(Map.of(), ACCEPT_ALL); // empty714}715t.serverPush(promise, headers, promiseBytes);716} catch (URISyntaxException x) {717throw new IOException(x.getMessage(), x);718}719}720721static class HTTP_FixedLengthHandler implements HttpTestHandler {722@Override723public void handle(HttpTestExchange t) throws IOException {724out.println("HTTP_FixedLengthHandler received request to " + t.getRequestURI());725try (InputStream is = t.getRequestBody()) {726is.readAllBytes();727}728URI requestURI = t.getRequestURI();729for (int i = 1; i<2; i++) {730String path = requestURI.getPath() + "/before/promise-" + i;731pushPromiseFor(t, requestURI, path, true);732}733byte[] resp = t.getRequestURI().toString().getBytes(StandardCharsets.UTF_8);734t.sendResponseHeaders(200, resp.length); //fixed content length735try (OutputStream os = t.getResponseBody()) {736int bytes = resp.length/3;737for (int i = 0; i<2; i++) {738String path = requestURI.getPath() + "/after/promise-" + (i + 2);739os.write(resp, i * bytes, bytes);740os.flush();741pushPromiseFor(t, requestURI, path, true);742}743os.write(resp, 2*bytes, resp.length - 2*bytes);744}745}746747}748749static class HTTP_ChunkedHandler implements HttpTestHandler {750@Override751public void handle(HttpTestExchange t) throws IOException {752out.println("HTTP_ChunkedHandler received request to " + t.getRequestURI());753byte[] resp = t.getRequestURI().toString().getBytes(StandardCharsets.UTF_8);754try (InputStream is = t.getRequestBody()) {755is.readAllBytes();756}757URI requestURI = t.getRequestURI();758for (int i = 1; i<2; i++) {759String path = requestURI.getPath() + "/before/promise-" + i;760pushPromiseFor(t, requestURI, path, false);761}762t.sendResponseHeaders(200, -1); // chunked/variable763try (OutputStream os = t.getResponseBody()) {764int bytes = resp.length/3;765for (int i = 0; i<2; i++) {766String path = requestURI.getPath() + "/after/promise-" + (i + 2);767os.write(resp, i * bytes, bytes);768os.flush();769pushPromiseFor(t, requestURI, path, false);770}771os.write(resp, 2*bytes, resp.length - 2*bytes);772}773}774}775776777}778779780