Path: blob/master/test/jdk/java/net/httpclient/AbstractConnectTimeout.java
41149 views
/*1* Copyright (c) 2018, 2020, 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.net.ConnectException;24import java.net.InetSocketAddress;25import java.net.NoRouteToHostException;26import java.net.ProxySelector;27import java.net.URI;28import java.net.http.HttpClient;29import java.net.http.HttpClient.Version;30import java.net.http.HttpConnectTimeoutException;31import java.net.http.HttpRequest;32import java.net.http.HttpRequest.BodyPublishers;33import java.net.http.HttpResponse;34import java.net.http.HttpResponse.BodyHandlers;35import java.nio.channels.UnresolvedAddressException;36import java.time.Duration;37import java.util.ArrayList;38import java.util.Arrays;39import java.util.List;40import java.util.concurrent.CompletionException;41import org.testng.annotations.DataProvider;42import static java.lang.System.out;43import static java.net.http.HttpClient.Builder.NO_PROXY;44import static java.net.http.HttpClient.Version.HTTP_1_1;45import static java.net.http.HttpClient.Version.HTTP_2;46import static java.time.Duration.*;47import static java.util.concurrent.TimeUnit.NANOSECONDS;48import static org.testng.Assert.fail;4950public abstract class AbstractConnectTimeout {5152static final Duration NO_DURATION = null;5354static List<List<Duration>> TIMEOUTS = List.of(55// connectTimeout HttpRequest timeout56Arrays.asList( NO_DURATION, ofSeconds(1) ),57Arrays.asList( NO_DURATION, ofMillis(100) ),58Arrays.asList( NO_DURATION, ofNanos(99) ),59Arrays.asList( NO_DURATION, ofNanos(1) ),6061Arrays.asList( ofSeconds(1), NO_DURATION ),62Arrays.asList( ofMillis(100), NO_DURATION ),63Arrays.asList( ofNanos(99), NO_DURATION ),64Arrays.asList( ofNanos(1), NO_DURATION ),6566Arrays.asList( ofSeconds(1), ofMinutes(1) ),67Arrays.asList( ofMillis(100), ofMinutes(1) ),68Arrays.asList( ofNanos(99), ofMinutes(1) ),69Arrays.asList( ofNanos(1), ofMinutes(1) )70);7172static final List<String> METHODS = List.of("GET", "POST");73static final List<Version> VERSIONS = List.of(HTTP_2, HTTP_1_1);74static final List<String> SCHEMES = List.of("https", "http");7576@DataProvider(name = "variants")77public Object[][] variants() {78List<Object[]> l = new ArrayList<>();79for (List<Duration> timeouts : TIMEOUTS) {80Duration connectTimeout = timeouts.get(0);81Duration requestTimeout = timeouts.get(1);82for (String method: METHODS) {83for (String scheme : SCHEMES) {84for (Version requestVersion : VERSIONS) {85l.add(new Object[] {requestVersion, scheme, method, connectTimeout, requestTimeout});86}}}}87return l.stream().toArray(Object[][]::new);88}8990static final ProxySelector EXAMPLE_DOT_COM_PROXY = ProxySelector.of(91InetSocketAddress.createUnresolved("example.com", 8080));9293//@Test(dataProvider = "variants")94protected void timeoutNoProxySync(Version requestVersion,95String scheme,96String method,97Duration connectTimeout,98Duration requestTimeout)99throws Exception100{101timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);102}103104//@Test(dataProvider = "variants")105protected void timeoutWithProxySync(Version requestVersion,106String scheme,107String method,108Duration connectTimeout,109Duration requestTimeout)110throws Exception111{112timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);113}114115private void timeoutSync(Version requestVersion,116String scheme,117String method,118Duration connectTimeout,119Duration requestTimeout,120ProxySelector proxy)121throws Exception122{123out.printf("%ntimeoutSync(requestVersion=%s, scheme=%s, method=%s,"124+ " connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",125requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);126127HttpClient client = newClient(connectTimeout, proxy);128HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);129130for (int i = 0; i < 2; i++) {131out.printf("iteration %d%n", i);132long startTime = System.nanoTime();133try {134HttpResponse<?> resp = client.send(request, BodyHandlers.ofString());135printResponse(resp);136fail("Unexpected response: " + resp);137} catch (HttpConnectTimeoutException expected) { // blocking thread-specific exception138long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);139out.printf("Client: received in %d millis%n", elapsedTime);140assertExceptionTypeAndCause(expected.getCause());141} catch (ConnectException e) {142long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);143out.printf("Client: received in %d millis%n", elapsedTime);144Throwable t = e.getCause().getCause(); // blocking thread-specific exception145if (!isAcceptableCause(t)) { // tolerate only NRTHE or UAE146e.printStackTrace(out);147fail("Unexpected exception:" + e);148} else {149out.printf("Caught ConnectException with "150+ " cause: %s - skipping%n", t.getCause());151}152}153}154}155156//@Test(dataProvider = "variants")157protected void timeoutNoProxyAsync(Version requestVersion,158String scheme,159String method,160Duration connectTimeout,161Duration requestTimeout) {162timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);163}164165//@Test(dataProvider = "variants")166protected void timeoutWithProxyAsync(Version requestVersion,167String scheme,168String method,169Duration connectTimeout,170Duration requestTimeout) {171timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);172}173174private void timeoutAsync(Version requestVersion,175String scheme,176String method,177Duration connectTimeout,178Duration requestTimeout,179ProxySelector proxy) {180out.printf("%ntimeoutAsync(requestVersion=%s, scheme=%s, method=%s, "181+ "connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",182requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);183184HttpClient client = newClient(connectTimeout, proxy);185HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);186for (int i = 0; i < 2; i++) {187out.printf("iteration %d%n", i);188long startTime = System.nanoTime();189try {190HttpResponse<?> resp = client.sendAsync(request, BodyHandlers.ofString()).join();191printResponse(resp);192fail("Unexpected response: " + resp);193} catch (CompletionException e) {194long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);195out.printf("Client: received in %d millis%n", elapsedTime);196Throwable t = e.getCause();197if (t instanceof ConnectException && isAcceptableCause(t.getCause())) {198// tolerate only NRTHE and UAE199out.printf("Caught ConnectException with "200+ "cause: %s - skipping%n", t.getCause());201} else {202assertExceptionTypeAndCause(t);203}204}205}206}207208static boolean isAcceptableCause(Throwable cause) {209if (cause instanceof NoRouteToHostException) return true;210if (cause instanceof UnresolvedAddressException) return true;211return false;212}213214static HttpClient newClient(Duration connectTimeout, ProxySelector proxy) {215HttpClient.Builder builder = HttpClient.newBuilder().proxy(proxy);216if (connectTimeout != NO_DURATION)217builder.connectTimeout(connectTimeout);218return builder.build();219}220221static HttpRequest newRequest(String scheme,222Version reqVersion,223String method,224Duration requestTimeout) {225// Resolvable address. Most tested environments just ignore the TCP SYN,226// or occasionally return ICMP no route to host227URI uri = URI.create(scheme +"://example.com:81/");228HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri);229reqBuilder = reqBuilder.version(reqVersion);230switch (method) {231case "GET" : reqBuilder.GET(); break;232case "POST" : reqBuilder.POST(BodyPublishers.noBody()); break;233default: throw new AssertionError("Unknown method:" + method);234}235if (requestTimeout != NO_DURATION)236reqBuilder.timeout(requestTimeout);237return reqBuilder.build();238}239240static void assertExceptionTypeAndCause(Throwable t) {241if (!(t instanceof HttpConnectTimeoutException)) {242t.printStackTrace(out);243fail("Expected HttpConnectTimeoutException, got:" + t);244}245Throwable connEx = t.getCause();246if (!(connEx instanceof ConnectException)) {247t.printStackTrace(out);248fail("Expected ConnectException cause in:" + connEx);249}250out.printf("Caught expected HttpConnectTimeoutException with ConnectException"251+ " cause: %n%s%n%s%n", t, connEx);252final String EXPECTED_MESSAGE = "HTTP connect timed out"; // impl dependent253if (!connEx.getMessage().equals(EXPECTED_MESSAGE))254fail("Expected: \"" + EXPECTED_MESSAGE + "\", got: \"" + connEx.getMessage() + "\"");255256}257258static void printResponse(HttpResponse<?> response) {259out.println("Unexpected response: " + response);260out.println("Headers: " + response.headers());261out.println("Body: " + response.body());262}263}264265266