Path: blob/master/test/jdk/java/net/httpclient/AbstractConnectTimeoutHandshake.java
41149 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*/2223import java.io.BufferedInputStream;24import java.io.IOException;25import java.io.InputStream;26import java.io.UncheckedIOException;27import java.net.ConnectException;28import java.net.InetAddress;29import java.net.InetSocketAddress;30import java.net.ServerSocket;31import java.net.Socket;32import java.net.URI;33import java.time.Duration;34import java.util.ArrayList;35import java.util.Arrays;36import java.util.List;37import java.util.concurrent.CompletableFuture;38import java.util.concurrent.CompletionException;39import java.net.http.HttpClient;40import java.net.http.HttpClient.Version;41import java.net.http.HttpConnectTimeoutException;42import java.net.http.HttpRequest;43import java.net.http.HttpRequest.BodyPublishers;44import java.net.http.HttpResponse;45import java.net.http.HttpResponse.BodyHandlers;46import org.testng.annotations.AfterTest;47import org.testng.annotations.BeforeTest;48import org.testng.annotations.DataProvider;49import static java.lang.String.format;50import static java.lang.System.out;51import static java.net.http.HttpClient.Builder.NO_PROXY;52import static java.net.http.HttpClient.Version.HTTP_1_1;53import static java.net.http.HttpClient.Version.HTTP_2;54import static java.time.Duration.*;55import static java.util.concurrent.TimeUnit.NANOSECONDS;56import static org.testng.Assert.fail;5758public abstract class AbstractConnectTimeoutHandshake {5960// The number of iterations each testXXXClient performs.61static final int TIMES = 2;6263Server server;64URI httpsURI;6566static final Duration NO_DURATION = null;6768static List<List<Duration>> TIMEOUTS = List.of(69// connectTimeout HttpRequest timeout70Arrays.asList( NO_DURATION, ofSeconds(1) ),71Arrays.asList( NO_DURATION, ofSeconds(2) ),72Arrays.asList( NO_DURATION, ofMillis(500) ),7374Arrays.asList( ofSeconds(1), NO_DURATION ),75Arrays.asList( ofSeconds(2), NO_DURATION ),76Arrays.asList( ofMillis(500), NO_DURATION ),7778Arrays.asList( ofSeconds(1), ofMinutes(1) ),79Arrays.asList( ofSeconds(2), ofMinutes(1) ),80Arrays.asList( ofMillis(500), ofMinutes(1) )81);8283static final List<String> METHODS = List.of("GET" , "POST");84static final List<Version> VERSIONS = List.of(HTTP_2, HTTP_1_1);8586@DataProvider(name = "variants")87public Object[][] variants() {88List<Object[]> l = new ArrayList<>();89for (List<Duration> timeouts : TIMEOUTS) {90Duration connectTimeout = timeouts.get(0);91Duration requestTimeout = timeouts.get(1);92for (String method: METHODS) {93for (Version requestVersion : VERSIONS) {94l.add(new Object[] {requestVersion, method, connectTimeout, requestTimeout});95}}}96return l.stream().toArray(Object[][]::new);97}9899//@Test(dataProvider = "variants")100protected void timeoutSync(Version requestVersion,101String method,102Duration connectTimeout,103Duration requestTimeout)104throws Exception105{106out.printf("%n--- timeoutSync requestVersion=%s, method=%s, "107+ "connectTimeout=%s, requestTimeout=%s ---%n",108requestVersion, method, connectTimeout, requestTimeout);109HttpClient client = newClient(connectTimeout);110HttpRequest request = newRequest(requestVersion, method, requestTimeout);111112for (int i = 0; i < TIMES; i++) {113out.printf("iteration %d%n", i);114long startTime = System.nanoTime();115try {116HttpResponse<String> resp = client.send(request, BodyHandlers.ofString());117printResponse(resp);118fail("Unexpected response: " + resp);119} catch (HttpConnectTimeoutException expected) {120long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);121out.printf("Client: received in %d millis%n", elapsedTime);122out.printf("Client: caught expected HttpConnectTimeoutException: %s%n", expected);123checkExceptionOrCause(ConnectException.class, expected);124}125}126}127128//@Test(dataProvider = "variants")129protected void timeoutAsync(Version requestVersion,130String method,131Duration connectTimeout,132Duration requestTimeout) {133out.printf("%n--- timeoutAsync requestVersion=%s, method=%s, "134+ "connectTimeout=%s, requestTimeout=%s ---%n",135requestVersion, method, connectTimeout, requestTimeout);136HttpClient client = newClient(connectTimeout);137HttpRequest request = newRequest(requestVersion, method, requestTimeout);138139for (int i = 0; i < TIMES; i++) {140out.printf("iteration %d%n", i);141long startTime = System.nanoTime();142CompletableFuture<HttpResponse<String>> cf =143client.sendAsync(request, BodyHandlers.ofString());144try {145HttpResponse<String> resp = cf.join();146printResponse(resp);147fail("Unexpected response: " + resp);148} catch (CompletionException ce) {149long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);150out.printf("Client: received in %d millis%n", elapsedTime);151Throwable expected = ce.getCause();152if (expected instanceof HttpConnectTimeoutException) {153out.printf("Client: caught expected HttpConnectTimeoutException: %s%n", expected);154checkExceptionOrCause(ConnectException.class, expected);155} else {156out.printf("Client: caught UNEXPECTED exception: %s%n", expected);157throw ce;158}159}160}161}162163static HttpClient newClient(Duration connectTimeout) {164HttpClient.Builder builder = HttpClient.newBuilder().proxy(NO_PROXY);165if (connectTimeout != NO_DURATION)166builder.connectTimeout(connectTimeout);167return builder.build();168}169170HttpRequest newRequest(Version reqVersion,171String method,172Duration requestTimeout) {173HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(httpsURI);174reqBuilder = reqBuilder.version(reqVersion);175switch (method) {176case "GET" : reqBuilder.GET(); break;177case "POST" : reqBuilder.POST(BodyPublishers.noBody()); break;178default: throw new AssertionError("Unknown method:" + method);179}180if (requestTimeout != NO_DURATION)181reqBuilder.timeout(requestTimeout);182return reqBuilder.build();183}184185static void checkExceptionOrCause(Class<? extends Throwable> clazz, Throwable t) {186final Throwable original = t;187do {188if (clazz.isInstance(t)) {189System.out.println("Found expected exception/cause: " + t);190return; // found191}192} while ((t = t.getCause()) != null);193original.printStackTrace(System.out);194throw new RuntimeException("Expected " + clazz + "in " + original);195}196197static void printResponse(HttpResponse<?> response) {198out.println("Unexpected response: " + response);199out.println("Headers: " + response.headers());200out.println("Body: " + response.body());201}202203// -- Infrastructure204205static String serverAuthority(Server server) {206return InetAddress.getLoopbackAddress().getHostName() + ":"207+ server.getPort();208}209210@BeforeTest211public void setup() throws Exception {212server = new Server();213httpsURI = URI.create("https://" + serverAuthority(server) + "/foo");214out.println("HTTPS URI: " + httpsURI);215}216217@AfterTest218public void teardown() throws Exception {219server.close();220out.printf("%n--- teardown ---%n");221222int numClientConnections = variants().length * TIMES;223int serverCount = server.count;224out.printf("Client made %d connections.%n", numClientConnections);225out.printf("Server received %d connections.%n", serverCount);226227// This is usually the case, but not always, do not assert. Remains228// as an informative message only.229//if (numClientConnections != serverCount)230// fail(format("[numTests: %d] != [serverCount: %d]",231// numClientConnections, serverCount));232}233234/**235* Emulates a server-side, using plain cleartext Sockets, that just reads236* initial client hello and does nothing more.237*/238static class Server extends Thread implements AutoCloseable {239private final ServerSocket ss;240private volatile boolean closed;241volatile int count;242243Server() throws IOException {244super("Server");245ss = new ServerSocket();246ss.setReuseAddress(false);247ss.bind(new InetSocketAddress(InetAddress.getLoopbackAddress(), 0));248this.start();249}250251int getPort() {252return ss.getLocalPort();253}254255@Override256public void close() {257if (closed)258return;259closed = true;260try {261ss.close();262} catch (IOException e) {263throw new UncheckedIOException("Unexpected", e);264}265}266267@Override268public void run() {269while (!closed) {270try (Socket s = ss.accept()) {271count++;272out.println("Server: accepted new connection");273InputStream is = new BufferedInputStream(s.getInputStream());274275out.println("Server: starting to read");276while (is.read() != -1) ;277278out.println("Server: closing connection");279s.close(); // close without giving any reply280} catch (IOException e) {281if (!closed)282out.println("UNEXPECTED " + e);283}284}285}286}287}288289290