Path: blob/master/test/jdk/java/net/httpclient/AbstractThrowingPublishers.java
66644 views
/*1* Copyright (c) 2018, 2021, 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*/2223import com.sun.net.httpserver.HttpServer;24import com.sun.net.httpserver.HttpsConfigurator;25import com.sun.net.httpserver.HttpsServer;26import jdk.test.lib.net.SimpleSSLContext;27import org.testng.ITestContext;28import org.testng.ITestResult;29import org.testng.SkipException;30import org.testng.annotations.AfterClass;31import org.testng.annotations.AfterTest;32import org.testng.annotations.BeforeMethod;33import org.testng.annotations.BeforeTest;34import org.testng.annotations.DataProvider;35import org.testng.annotations.Test;3637import javax.net.ssl.SSLContext;38import java.io.IOException;39import java.io.InputStream;40import java.io.OutputStream;41import java.io.UncheckedIOException;42import java.net.InetAddress;43import java.net.InetSocketAddress;44import java.net.URI;45import java.net.http.HttpClient;46import java.net.http.HttpRequest;47import java.net.http.HttpRequest.BodyPublisher;48import java.net.http.HttpRequest.BodyPublishers;49import java.net.http.HttpResponse;50import java.net.http.HttpResponse.BodyHandler;51import java.net.http.HttpResponse.BodyHandlers;52import java.nio.ByteBuffer;53import java.nio.charset.StandardCharsets;54import java.util.Arrays;55import java.util.EnumSet;56import java.util.List;57import java.util.Set;58import java.util.concurrent.CompletableFuture;59import java.util.concurrent.CompletionException;60import java.util.concurrent.ConcurrentHashMap;61import java.util.concurrent.ConcurrentMap;62import java.util.concurrent.ExecutionException;63import java.util.concurrent.Executor;64import java.util.concurrent.Executors;65import java.util.concurrent.Flow;66import java.util.concurrent.SubmissionPublisher;67import java.util.concurrent.atomic.AtomicLong;68import java.util.concurrent.atomic.AtomicReference;69import java.util.function.BiPredicate;70import java.util.function.Consumer;71import java.util.function.Supplier;72import java.util.stream.Collectors;73import java.util.stream.Stream;7475import static java.lang.String.format;76import static java.lang.System.out;77import static java.nio.charset.StandardCharsets.UTF_8;78import static org.testng.Assert.assertEquals;79import static org.testng.Assert.assertTrue;8081public abstract class AbstractThrowingPublishers implements HttpServerAdapters {8283SSLContext sslContext;84HttpTestServer httpTestServer; // HTTP/1.1 [ 4 servers ]85HttpTestServer httpsTestServer; // HTTPS/1.186HttpTestServer http2TestServer; // HTTP/2 ( h2c )87HttpTestServer https2TestServer; // HTTP/2 ( h2 )88String httpURI_fixed;89String httpURI_chunk;90String httpsURI_fixed;91String httpsURI_chunk;92String http2URI_fixed;93String http2URI_chunk;94String https2URI_fixed;95String https2URI_chunk;9697static final int ITERATION_COUNT = 1;98// a shared executor helps reduce the amount of threads created by the test99static final Executor executor = new TestExecutor(Executors.newCachedThreadPool());100static final ConcurrentMap<String, Throwable> FAILURES = new ConcurrentHashMap<>();101static volatile boolean tasksFailed;102static final AtomicLong serverCount = new AtomicLong();103static final AtomicLong clientCount = new AtomicLong();104static final long start = System.nanoTime();105public static String now() {106long now = System.nanoTime() - start;107long secs = now / 1000_000_000;108long mill = (now % 1000_000_000) / 1000_000;109long nan = now % 1000_000;110return String.format("[%d s, %d ms, %d ns] ", secs, mill, nan);111}112113final ReferenceTracker TRACKER = ReferenceTracker.INSTANCE;114private volatile HttpClient sharedClient;115116static class TestExecutor implements Executor {117final AtomicLong tasks = new AtomicLong();118Executor executor;119TestExecutor(Executor executor) {120this.executor = executor;121}122123@Override124public void execute(Runnable command) {125long id = tasks.incrementAndGet();126executor.execute(() -> {127try {128command.run();129} catch (Throwable t) {130tasksFailed = true;131System.out.printf(now() + "Task %s failed: %s%n", id, t);132System.err.printf(now() + "Task %s failed: %s%n", id, t);133FAILURES.putIfAbsent("Task " + id, t);134throw t;135}136});137}138}139140protected boolean stopAfterFirstFailure() {141return Boolean.getBoolean("jdk.internal.httpclient.debug");142}143144final AtomicReference<SkipException> skiptests = new AtomicReference<>();145void checkSkip() {146var skip = skiptests.get();147if (skip != null) throw skip;148}149static String name(ITestResult result) {150var params = result.getParameters();151return result.getName()152+ (params == null ? "()" : Arrays.toString(result.getParameters()));153}154155@BeforeMethod156void beforeMethod(ITestContext context) {157if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {158if (skiptests.get() == null) {159SkipException skip = new SkipException("some tests failed");160skip.setStackTrace(new StackTraceElement[0]);161skiptests.compareAndSet(null, skip);162}163}164}165166@AfterClass167static final void printFailedTests(ITestContext context) {168out.println("\n=========================");169try {170// Exceptions should already have been added to FAILURES171// var failed = context.getFailedTests().getAllResults().stream()172// .collect(Collectors.toMap(r -> name(r), ITestResult::getThrowable));173// FAILURES.putAll(failed);174175out.printf("%n%sCreated %d servers and %d clients%n",176now(), serverCount.get(), clientCount.get());177if (FAILURES.isEmpty()) return;178out.println("Failed tests: ");179FAILURES.entrySet().forEach((e) -> {180out.printf("\t%s: %s%n", e.getKey(), e.getValue());181e.getValue().printStackTrace(out);182});183if (tasksFailed) {184System.out.println("WARNING: Some tasks failed");185}186} finally {187out.println("\n=========================\n");188}189}190191private String[] uris() {192return new String[] {193httpURI_fixed,194httpURI_chunk,195httpsURI_fixed,196httpsURI_chunk,197http2URI_fixed,198http2URI_chunk,199https2URI_fixed,200https2URI_chunk,201};202}203204@DataProvider(name = "sanity")205public Object[][] sanity() {206String[] uris = uris();207Object[][] result = new Object[uris.length * 2][];208//Object[][] result = new Object[uris.length][];209int i = 0;210for (boolean sameClient : List.of(false, true)) {211//if (!sameClient) continue;212for (String uri: uris()) {213result[i++] = new Object[] {uri + "/sanity", sameClient};214}215}216assert i == uris.length * 2;217// assert i == uris.length ;218return result;219}220221enum Where {222BEFORE_SUBSCRIBE, BEFORE_REQUEST, BEFORE_NEXT_REQUEST, BEFORE_CANCEL,223AFTER_SUBSCRIBE, AFTER_REQUEST, AFTER_NEXT_REQUEST, AFTER_CANCEL;224public Consumer<Where> select(Consumer<Where> consumer) {225return new Consumer<Where>() {226@Override227public void accept(Where where) {228if (Where.this == where) {229consumer.accept(where);230}231}232};233}234}235236private Object[][] variants(List<Thrower> throwers, Set<Where> whereValues) {237String[] uris = uris();238Object[][] result = new Object[uris.length * 2 * throwers.size()][];239//Object[][] result = new Object[(uris.length/2) * 2 * 2][];240int i = 0;241for (Thrower thrower : throwers) {242for (boolean sameClient : List.of(false, true)) {243for (String uri : uris()) {244// if (uri.contains("http2") || uri.contains("https2")) continue;245// if (!sameClient) continue;246result[i++] = new Object[]{uri, sameClient, thrower, whereValues};247}248}249}250assert i == uris.length * 2 * throwers.size();251//assert Stream.of(result).filter(o -> o != null).count() == result.length;252return result;253}254255@DataProvider(name = "subscribeProvider")256public Object[][] subscribeProvider(ITestContext context) {257if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {258return new Object[0][];259}260return variants(List.of(261new UncheckedCustomExceptionThrower(),262new UncheckedIOExceptionThrower()),263EnumSet.of(Where.BEFORE_SUBSCRIBE, Where.AFTER_SUBSCRIBE));264}265266@DataProvider(name = "requestProvider")267public Object[][] requestProvider(ITestContext context) {268if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {269return new Object[0][];270}271return variants(List.of(272new UncheckedCustomExceptionThrower(),273new UncheckedIOExceptionThrower()),274EnumSet.of(Where.BEFORE_REQUEST, Where.AFTER_REQUEST));275}276277@DataProvider(name = "nextRequestProvider")278public Object[][] nextRequestProvider(ITestContext context) {279if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {280return new Object[0][];281}282return variants(List.of(283new UncheckedCustomExceptionThrower(),284new UncheckedIOExceptionThrower()),285EnumSet.of(Where.BEFORE_NEXT_REQUEST, Where.AFTER_NEXT_REQUEST));286}287288@DataProvider(name = "beforeCancelProviderIO")289public Object[][] beforeCancelProviderIO(ITestContext context) {290if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {291return new Object[0][];292}293return variants(List.of(294new UncheckedIOExceptionThrower()),295EnumSet.of(Where.BEFORE_CANCEL));296}297298@DataProvider(name = "afterCancelProviderIO")299public Object[][] afterCancelProviderIO(ITestContext context) {300if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {301return new Object[0][];302}303return variants(List.of(304new UncheckedIOExceptionThrower()),305EnumSet.of(Where.AFTER_CANCEL));306}307308@DataProvider(name = "beforeCancelProviderCustom")309public Object[][] beforeCancelProviderCustom(ITestContext context) {310if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {311return new Object[0][];312}313return variants(List.of(314new UncheckedCustomExceptionThrower()),315EnumSet.of(Where.BEFORE_CANCEL));316}317318@DataProvider(name = "afterCancelProviderCustom")319public Object[][] afterCancelProvider(ITestContext context) {320if (stopAfterFirstFailure() && context.getFailedTests().size() > 0) {321return new Object[0][];322}323return variants(List.of(324new UncheckedCustomExceptionThrower()),325EnumSet.of(Where.AFTER_CANCEL));326}327328private HttpClient makeNewClient() {329clientCount.incrementAndGet();330return TRACKER.track(HttpClient.newBuilder()331.proxy(HttpClient.Builder.NO_PROXY)332.executor(executor)333.sslContext(sslContext)334.build());335}336337HttpClient newHttpClient(boolean share) {338if (!share) return makeNewClient();339HttpClient shared = sharedClient;340if (shared != null) return shared;341synchronized (this) {342shared = sharedClient;343if (shared == null) {344shared = sharedClient = makeNewClient();345}346return shared;347}348}349350final String BODY = "Some string | that ? can | be split ? several | ways.";351352//@Test(dataProvider = "sanity")353protected void testSanityImpl(String uri, boolean sameClient)354throws Exception {355HttpClient client = null;356out.printf("%n%s testSanity(%s, %b)%n", now(), uri, sameClient);357for (int i=0; i< ITERATION_COUNT; i++) {358if (!sameClient || client == null)359client = newHttpClient(sameClient);360361SubmissionPublisher<ByteBuffer> publisher362= new SubmissionPublisher<>(executor,10);363ThrowingBodyPublisher bodyPublisher = new ThrowingBodyPublisher((w) -> {},364BodyPublishers.fromPublisher(publisher));365CompletableFuture<Void> subscribedCF = bodyPublisher.subscribedCF();366subscribedCF.whenComplete((r,t) -> System.out.println(now() + " subscribe completed " + t))367.thenAcceptAsync((v) -> {368Stream.of(BODY.split("\\|"))369.forEachOrdered(s -> {370System.out.println("submitting \"" + s +"\"");371publisher.submit(ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)));372});373System.out.println("publishing done");374publisher.close();375},376executor);377378HttpRequest req = HttpRequest.newBuilder(URI.create(uri))379.POST(bodyPublisher)380.build();381BodyHandler<String> handler = BodyHandlers.ofString();382CompletableFuture<HttpResponse<String>> response = client.sendAsync(req, handler);383384String body = response.join().body();385assertEquals(body, Stream.of(BODY.split("\\|")).collect(Collectors.joining()));386}387}388389// @Test(dataProvider = "variants")390protected void testThrowingAsStringImpl(String uri,391boolean sameClient,392Thrower thrower,393Set<Where> whereValues)394throws Exception395{396String test = format("testThrowingAsString(%s, %b, %s, %s)",397uri, sameClient, thrower, whereValues);398List<byte[]> bytes = Stream.of(BODY.split("|"))399.map(s -> s.getBytes(UTF_8))400.collect(Collectors.toList());401testThrowing(test, uri, sameClient, () -> BodyPublishers.ofByteArrays(bytes),402this::shouldNotThrowInCancel, thrower,false, whereValues);403}404405private <T,U> void testThrowing(String name, String uri, boolean sameClient,406Supplier<BodyPublisher> publishers,407Finisher finisher, Thrower thrower,408boolean async, Set<Where> whereValues)409throws Exception410{411checkSkip();412out.printf("%n%s%s%n", now(), name);413try {414testThrowing(uri, sameClient, publishers, finisher, thrower, async, whereValues);415} catch (Error | Exception x) {416FAILURES.putIfAbsent(name, x);417throw x;418}419}420421private void testThrowing(String uri, boolean sameClient,422Supplier<BodyPublisher> publishers,423Finisher finisher, Thrower thrower,424boolean async, Set<Where> whereValues)425throws Exception426{427HttpClient client = null;428for (Where where : whereValues) {429//if (where == Where.ON_SUBSCRIBE) continue;430//if (where == Where.ON_ERROR) continue;431if (!sameClient || client == null)432client = newHttpClient(sameClient);433434ThrowingBodyPublisher bodyPublisher =435new ThrowingBodyPublisher(where.select(thrower), publishers.get());436HttpRequest req = HttpRequest.437newBuilder(URI.create(uri))438.header("X-expect-exception", "true")439.POST(bodyPublisher)440.build();441BodyHandler<String> handler = BodyHandlers.ofString();442System.out.println("try throwing in " + where);443HttpResponse<String> response = null;444if (async) {445try {446response = client.sendAsync(req, handler).join();447} catch (Error | Exception x) {448Throwable cause = findCause(where, x, thrower);449if (cause == null) throw causeNotFound(where, x);450System.out.println(now() + "Got expected exception: " + cause);451}452} else {453try {454response = client.send(req, handler);455} catch (Error | Exception t) {456// synchronous send will rethrow exceptions457Throwable throwable = t.getCause();458assert throwable != null;459460if (thrower.test(where, throwable)) {461System.out.println(now() + "Got expected exception: " + throwable);462} else throw causeNotFound(where, t);463}464}465if (response != null) {466finisher.finish(where, response, thrower);467}468}469}470471// can be used to reduce the surface of the test when diagnosing472// some failure473Set<Where> whereValues() {474//return EnumSet.of(Where.BEFORE_CANCEL, Where.AFTER_CANCEL);475return EnumSet.allOf(Where.class);476}477478interface Thrower extends Consumer<Where>, BiPredicate<Where,Throwable> {479480}481482interface Finisher<T,U> {483U finish(Where w, HttpResponse<T> resp, Thrower thrower) throws IOException;484}485486final <T,U> U shouldNotThrowInCancel(Where w, HttpResponse<T> resp, Thrower thrower) {487switch (w) {488case BEFORE_CANCEL: return null;489case AFTER_CANCEL: return null;490default: break;491}492return shouldHaveThrown(w, resp, thrower);493}494495496final <T,U> U shouldHaveThrown(Where w, HttpResponse<T> resp, Thrower thrower) {497String msg = "Expected exception not thrown in " + w498+ "\n\tReceived: " + resp499+ "\n\tWith body: " + resp.body();500System.out.println(msg);501throw new RuntimeException(msg);502}503504505private static Throwable findCause(Where w,506Throwable x,507BiPredicate<Where, Throwable> filter) {508while (x != null && !filter.test(w,x)) x = x.getCause();509return x;510}511512static AssertionError causeNotFound(Where w, Throwable t) {513return new AssertionError("Expected exception not found in " + w, t);514}515516static boolean isConnectionClosedLocally(Throwable t) {517if (t instanceof CompletionException) t = t.getCause();518if (t instanceof ExecutionException) t = t.getCause();519if (t instanceof IOException) {520String msg = t.getMessage();521return msg == null ? false522: msg.contains("connection closed locally");523}524return false;525}526527static final class UncheckedCustomExceptionThrower implements Thrower {528@Override529public void accept(Where where) {530out.println(now() + "Throwing in " + where);531throw new UncheckedCustomException(where.name());532}533534@Override535public boolean test(Where w, Throwable throwable) {536switch (w) {537case AFTER_REQUEST:538case BEFORE_NEXT_REQUEST:539case AFTER_NEXT_REQUEST:540if (isConnectionClosedLocally(throwable)) return true;541break;542default:543break;544}545return UncheckedCustomException.class.isInstance(throwable);546}547548@Override549public String toString() {550return "UncheckedCustomExceptionThrower";551}552}553554static final class UncheckedIOExceptionThrower implements Thrower {555@Override556public void accept(Where where) {557out.println(now() + "Throwing in " + where);558throw new UncheckedIOException(new CustomIOException(where.name()));559}560561@Override562public boolean test(Where w, Throwable throwable) {563switch (w) {564case AFTER_REQUEST:565case BEFORE_NEXT_REQUEST:566case AFTER_NEXT_REQUEST:567if (isConnectionClosedLocally(throwable)) return true;568break;569default:570break;571}572return UncheckedIOException.class.isInstance(throwable)573&& CustomIOException.class.isInstance(throwable.getCause());574}575576@Override577public String toString() {578return "UncheckedIOExceptionThrower";579}580}581582static final class UncheckedCustomException extends RuntimeException {583UncheckedCustomException(String message) {584super(message);585}586UncheckedCustomException(String message, Throwable cause) {587super(message, cause);588}589}590591static final class CustomIOException extends IOException {592CustomIOException(String message) {593super(message);594}595CustomIOException(String message, Throwable cause) {596super(message, cause);597}598}599600601static final class ThrowingBodyPublisher implements BodyPublisher {602private final BodyPublisher publisher;603private final CompletableFuture<Void> subscribedCF = new CompletableFuture<>();604final Consumer<Where> throwing;605ThrowingBodyPublisher(Consumer<Where> throwing, BodyPublisher publisher) {606this.throwing = throwing;607this.publisher = publisher;608}609610@Override611public long contentLength() {612return publisher.contentLength();613}614615@Override616public void subscribe(Flow.Subscriber<? super ByteBuffer> subscriber) {617try {618throwing.accept(Where.BEFORE_SUBSCRIBE);619publisher.subscribe(new SubscriberWrapper(subscriber));620subscribedCF.complete(null);621throwing.accept(Where.AFTER_SUBSCRIBE);622} catch (Throwable t) {623subscribedCF.completeExceptionally(t);624throw t;625}626}627628CompletableFuture<Void> subscribedCF() {629return subscribedCF;630}631632class SubscriptionWrapper implements Flow.Subscription {633final Flow.Subscription subscription;634final AtomicLong requestCount = new AtomicLong();635SubscriptionWrapper(Flow.Subscription subscription) {636this.subscription = subscription;637}638@Override639public void request(long n) {640long count = requestCount.incrementAndGet();641System.out.printf("%s request-%d(%d)%n", now(), count, n);642if (count > 1) throwing.accept(Where.BEFORE_NEXT_REQUEST);643throwing.accept(Where.BEFORE_REQUEST);644subscription.request(n);645throwing.accept(Where.AFTER_REQUEST);646if (count > 1) throwing.accept(Where.AFTER_NEXT_REQUEST);647}648649@Override650public void cancel() {651throwing.accept(Where.BEFORE_CANCEL);652subscription.cancel();653throwing.accept(Where.AFTER_CANCEL);654}655}656657class SubscriberWrapper implements Flow.Subscriber<ByteBuffer> {658final Flow.Subscriber<? super ByteBuffer> subscriber;659SubscriberWrapper(Flow.Subscriber<? super ByteBuffer> subscriber) {660this.subscriber = subscriber;661}662@Override663public void onSubscribe(Flow.Subscription subscription) {664subscriber.onSubscribe(new SubscriptionWrapper(subscription));665}666@Override667public void onNext(ByteBuffer item) {668subscriber.onNext(item);669}670@Override671public void onComplete() {672subscriber.onComplete();673}674675@Override676public void onError(Throwable throwable) {677subscriber.onError(throwable);678}679}680}681682683@BeforeTest684public void setup() throws Exception {685sslContext = new SimpleSSLContext().get();686if (sslContext == null)687throw new AssertionError("Unexpected null sslContext");688689// HTTP/1.1690HttpTestHandler h1_fixedLengthHandler = new HTTP_FixedLengthHandler();691HttpTestHandler h1_chunkHandler = new HTTP_ChunkedHandler();692InetSocketAddress sa = new InetSocketAddress(InetAddress.getLoopbackAddress(), 0);693httpTestServer = HttpTestServer.of(HttpServer.create(sa, 0));694httpTestServer.addHandler(h1_fixedLengthHandler, "/http1/fixed");695httpTestServer.addHandler(h1_chunkHandler, "/http1/chunk");696httpURI_fixed = "http://" + httpTestServer.serverAuthority() + "/http1/fixed/x";697httpURI_chunk = "http://" + httpTestServer.serverAuthority() + "/http1/chunk/x";698699HttpsServer httpsServer = HttpsServer.create(sa, 0);700httpsServer.setHttpsConfigurator(new HttpsConfigurator(sslContext));701httpsTestServer = HttpTestServer.of(httpsServer);702httpsTestServer.addHandler(h1_fixedLengthHandler, "/https1/fixed");703httpsTestServer.addHandler(h1_chunkHandler, "/https1/chunk");704httpsURI_fixed = "https://" + httpsTestServer.serverAuthority() + "/https1/fixed/x";705httpsURI_chunk = "https://" + httpsTestServer.serverAuthority() + "/https1/chunk/x";706707// HTTP/2708HttpTestHandler h2_fixedLengthHandler = new HTTP_FixedLengthHandler();709HttpTestHandler h2_chunkedHandler = new HTTP_ChunkedHandler();710711http2TestServer = HttpTestServer.of(new Http2TestServer("localhost", false, 0));712http2TestServer.addHandler(h2_fixedLengthHandler, "/http2/fixed");713http2TestServer.addHandler(h2_chunkedHandler, "/http2/chunk");714http2URI_fixed = "http://" + http2TestServer.serverAuthority() + "/http2/fixed/x";715http2URI_chunk = "http://" + http2TestServer.serverAuthority() + "/http2/chunk/x";716717https2TestServer = HttpTestServer.of(new Http2TestServer("localhost", true, sslContext));718https2TestServer.addHandler(h2_fixedLengthHandler, "/https2/fixed");719https2TestServer.addHandler(h2_chunkedHandler, "/https2/chunk");720https2URI_fixed = "https://" + https2TestServer.serverAuthority() + "/https2/fixed/x";721https2URI_chunk = "https://" + https2TestServer.serverAuthority() + "/https2/chunk/x";722723serverCount.addAndGet(4);724httpTestServer.start();725httpsTestServer.start();726http2TestServer.start();727https2TestServer.start();728}729730@AfterTest731public void teardown() throws Exception {732String sharedClientName =733sharedClient == null ? null : sharedClient.toString();734sharedClient = null;735Thread.sleep(100);736AssertionError fail = TRACKER.check(500);737try {738httpTestServer.stop();739httpsTestServer.stop();740http2TestServer.stop();741https2TestServer.stop();742} finally {743if (fail != null) {744if (sharedClientName != null) {745System.err.println("Shared client name is: " + sharedClientName);746}747throw fail;748}749}750}751752static class HTTP_FixedLengthHandler implements HttpTestHandler {753@Override754public void handle(HttpTestExchange t) throws IOException {755out.println("HTTP_FixedLengthHandler received request to " + t.getRequestURI());756byte[] resp;757try (InputStream is = t.getRequestBody()) {758resp = is.readAllBytes();759}760t.sendResponseHeaders(200, resp.length); //fixed content length761try (OutputStream os = t.getResponseBody()) {762os.write(resp);763}764}765}766767static class HTTP_ChunkedHandler implements HttpTestHandler {768@Override769public void handle(HttpTestExchange t) throws IOException {770out.println("HTTP_ChunkedHandler received request to " + t.getRequestURI());771byte[] resp;772try (InputStream is = t.getRequestBody()) {773resp = is.readAllBytes();774}775t.sendResponseHeaders(200, -1); // chunked/variable776try (OutputStream os = t.getResponseBody()) {777os.write(resp);778}779}780}781782}783784785