--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/java/net/httpclient/AbstractConnectTimeout.java Thu Aug 09 11:23:12 2018 +0100
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.net.ConnectException;
+import java.net.InetSocketAddress;
+import java.net.NoRouteToHostException;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpClient.Version;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpRequest;
+import java.net.http.HttpRequest.BodyPublishers;
+import java.net.http.HttpResponse;
+import java.net.http.HttpResponse.BodyHandlers;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CompletionException;
+import org.testng.annotations.DataProvider;
+import static java.lang.System.out;
+import static java.net.http.HttpClient.Builder.NO_PROXY;
+import static java.net.http.HttpClient.Version.HTTP_1_1;
+import static java.net.http.HttpClient.Version.HTTP_2;
+import static java.time.Duration.*;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static org.testng.Assert.fail;
+
+public abstract class AbstractConnectTimeout {
+
+ static final Duration NO_DURATION = null;
+
+ static List<List<Duration>> TIMEOUTS = List.of(
+ // connectTimeout HttpRequest timeout
+ Arrays.asList( NO_DURATION, ofSeconds(1) ),
+ Arrays.asList( NO_DURATION, ofMillis(100) ),
+ Arrays.asList( NO_DURATION, ofNanos(99) ),
+ Arrays.asList( NO_DURATION, ofNanos(1) ),
+
+ Arrays.asList( ofSeconds(1), NO_DURATION ),
+ Arrays.asList( ofMillis(100), NO_DURATION ),
+ Arrays.asList( ofNanos(99), NO_DURATION ),
+ Arrays.asList( ofNanos(1), NO_DURATION ),
+
+ Arrays.asList( ofSeconds(1), ofMinutes(1) ),
+ Arrays.asList( ofMillis(100), ofMinutes(1) ),
+ Arrays.asList( ofNanos(99), ofMinutes(1) ),
+ Arrays.asList( ofNanos(1), ofMinutes(1) )
+ );
+
+ static final List<String> METHODS = List.of("GET", "POST");
+ static final List<Version> VERSIONS = List.of(HTTP_2, HTTP_1_1);
+ static final List<String> SCHEMES = List.of("https", "http");
+
+ @DataProvider(name = "variants")
+ public Object[][] variants() {
+ List<Object[]> l = new ArrayList<>();
+ for (List<Duration> timeouts : TIMEOUTS) {
+ Duration connectTimeout = timeouts.get(0);
+ Duration requestTimeout = timeouts.get(1);
+ for (String method: METHODS) {
+ for (String scheme : SCHEMES) {
+ for (Version requestVersion : VERSIONS) {
+ l.add(new Object[] {requestVersion, scheme, method, connectTimeout, requestTimeout});
+ }}}}
+ return l.stream().toArray(Object[][]::new);
+ }
+
+ static final ProxySelector EXAMPLE_DOT_COM_PROXY = ProxySelector.of(
+ InetSocketAddress.createUnresolved("example.com", 8080));
+
+ //@Test(dataProvider = "variants")
+ protected void timeoutNoProxySync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout)
+ throws Exception
+ {
+ timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);
+ }
+
+ //@Test(dataProvider = "variants")
+ protected void timeoutWithProxySync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout)
+ throws Exception
+ {
+ timeoutSync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);
+ }
+
+ private void timeoutSync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout,
+ ProxySelector proxy)
+ throws Exception
+ {
+ out.printf("%ntimeoutSync(requestVersion=%s, scheme=%s, method=%s,"
+ + " connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",
+ requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);
+
+ HttpClient client = newClient(connectTimeout, proxy);
+ HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
+
+ for (int i = 0; i < 2; i++) {
+ out.printf("iteration %d%n", i);
+ long startTime = System.nanoTime();
+ try {
+ HttpResponse<?> resp = client.send(request, BodyHandlers.ofString());
+ printResponse(resp);
+ fail("Unexpected response: " + resp);
+ } catch (HttpConnectTimeoutException expected) { // blocking thread-specific exception
+ long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
+ out.printf("Client: received in %d millis%n", elapsedTime);
+ assertExceptionTypeAndCause(expected.getCause());
+ } catch (ConnectException e) {
+ long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
+ out.printf("Client: received in %d millis%n", elapsedTime);
+ Throwable t = e.getCause().getCause(); // blocking thread-specific exception
+ if (!(t instanceof NoRouteToHostException)) { // tolerate only NRTHE
+ e.printStackTrace(out);
+ fail("Unexpected exception:" + e);
+ } else {
+ out.printf("Caught ConnectException with NoRouteToHostException"
+ + " cause: %s - skipping%n", t.getCause());
+ }
+ }
+ }
+ }
+
+ //@Test(dataProvider = "variants")
+ protected void timeoutNoProxyAsync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout) {
+ timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, NO_PROXY);
+ }
+
+ //@Test(dataProvider = "variants")
+ protected void timeoutWithProxyAsync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout) {
+ timeoutAsync(requestVersion, scheme, method, connectTimeout, requestTimeout, EXAMPLE_DOT_COM_PROXY);
+ }
+
+ private void timeoutAsync(Version requestVersion,
+ String scheme,
+ String method,
+ Duration connectTimeout,
+ Duration requestTimeout,
+ ProxySelector proxy) {
+ out.printf("%ntimeoutAsync(requestVersion=%s, scheme=%s, method=%s, "
+ + "connectTimeout=%s, requestTimeout=%s, proxy=%s)%n",
+ requestVersion, scheme, method, connectTimeout, requestTimeout, proxy);
+
+ HttpClient client = newClient(connectTimeout, proxy);
+ HttpRequest request = newRequest(scheme, requestVersion, method, requestTimeout);
+ for (int i = 0; i < 2; i++) {
+ out.printf("iteration %d%n", i);
+ long startTime = System.nanoTime();
+ try {
+ HttpResponse<?> resp = client.sendAsync(request, BodyHandlers.ofString()).join();
+ printResponse(resp);
+ fail("Unexpected response: " + resp);
+ } catch (CompletionException e) {
+ long elapsedTime = NANOSECONDS.toMillis(System.nanoTime() - startTime);
+ out.printf("Client: received in %d millis%n", elapsedTime);
+ Throwable t = e.getCause();
+ if (t instanceof ConnectException &&
+ t.getCause() instanceof NoRouteToHostException) { // tolerate only NRTHE
+ out.printf("Caught ConnectException with NoRouteToHostException"
+ + " cause: %s - skipping%n", t.getCause());
+ } else {
+ assertExceptionTypeAndCause(t);
+ }
+ }
+ }
+ }
+
+ static HttpClient newClient(Duration connectTimeout, ProxySelector proxy) {
+ HttpClient.Builder builder = HttpClient.newBuilder().proxy(proxy);
+ if (connectTimeout != NO_DURATION)
+ builder.connectTimeout(connectTimeout);
+ return builder.build();
+ }
+
+ static HttpRequest newRequest(String scheme,
+ Version reqVersion,
+ String method,
+ Duration requestTimeout) {
+ // Resolvable address. Most tested environments just ignore the TCP SYN,
+ // or occasionally return ICMP no route to host
+ URI uri = URI.create(scheme +"://example.com:81/");
+ HttpRequest.Builder reqBuilder = HttpRequest.newBuilder(uri);
+ reqBuilder = reqBuilder.version(reqVersion);
+ switch (method) {
+ case "GET" : reqBuilder.GET(); break;
+ case "POST" : reqBuilder.POST(BodyPublishers.noBody()); break;
+ default: throw new AssertionError("Unknown method:" + method);
+ }
+ if (requestTimeout != NO_DURATION)
+ reqBuilder.timeout(requestTimeout);
+ return reqBuilder.build();
+ }
+
+ static void assertExceptionTypeAndCause(Throwable t) {
+ if (!(t instanceof HttpConnectTimeoutException)) {
+ t.printStackTrace(out);
+ fail("Expected HttpConnectTimeoutException, got:" + t);
+ }
+ Throwable connEx = t.getCause();
+ if (!(connEx instanceof ConnectException)) {
+ t.printStackTrace(out);
+ fail("Expected ConnectException cause in:" + connEx);
+ }
+ out.printf("Caught expected HttpConnectTimeoutException with ConnectException"
+ + " cause: %n%s%n%s%n", t, connEx);
+ final String EXPECTED_MESSAGE = "HTTP connect timed out"; // impl dependent
+ if (!connEx.getMessage().equals(EXPECTED_MESSAGE))
+ fail("Expected: \"" + EXPECTED_MESSAGE + "\", got: \"" + connEx.getMessage() + "\"");
+
+ }
+
+ static void printResponse(HttpResponse<?> response) {
+ out.println("Unexpected response: " + response);
+ out.println("Headers: " + response.headers());
+ out.println("Body: " + response.body());
+ }
+}