# HG changeset patch # User dfuchs # Date 1526653436 -3600 # Node ID c8fe5ffdfe98e26b31195a86d1ee84fb3d212b3e # Parent 46bb98e9db7160f4474f5eb424a4253848699e99 http-client-branch: improve test stability by allowing retry on java.net.ConnectException diff -r 46bb98e9db71 -r c8fe5ffdfe98 src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java --- a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java Tue May 15 16:10:17 2018 +0100 +++ b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java Fri May 18 15:23:56 2018 +0100 @@ -27,6 +27,7 @@ import java.io.IOException; import java.lang.System.Logger.Level; +import java.net.ConnectException; import java.time.Duration; import java.util.Iterator; import java.util.LinkedList; @@ -290,8 +291,17 @@ return false; } + private static boolean retryConnect() { + String s = Utils.getNetProperty("jdk.httpclient.disableRetryConnect"); + if (s == "" || "true".equals(s)) + return false; + return true; + } + /** True if ALL ( even non-idempotent ) requests can be automatic retried. */ private static final boolean RETRY_ALWAYS = retryPostValue(); + /** True if ConnectException should cause a retry. Enabled by default */ + private static final boolean RETRY_CONNECT = retryConnect(); /** Returns true is given request has an idempotent method. */ private static boolean isIdempotentRequest(HttpRequest request) { @@ -307,13 +317,23 @@ /** Returns true if the given request can be automatically retried. */ private static boolean canRetryRequest(HttpRequest request) { + if (RETRY_ALWAYS) + return true; if (isIdempotentRequest(request)) return true; - if (RETRY_ALWAYS) - return true; return false; } + private boolean retryOnFailure(Throwable t) { + return t instanceof ConnectionExpiredException + || (RETRY_CONNECT && (t instanceof ConnectException)); + } + + private Throwable retryCause(Throwable t) { + Throwable cause = t instanceof ConnectionExpiredException ? t.getCause() : t; + return cause == null ? t : cause; + } + /** * Takes a Throwable and returns a suitable CompletableFuture that is * completed exceptionally, or null. @@ -326,21 +346,20 @@ } if (cancelled && t instanceof IOException) { t = new HttpTimeoutException("request timed out"); - } else if (t instanceof ConnectionExpiredException) { - Throwable cause = t; - if (t.getCause() != null) { - cause = t.getCause(); // unwrap the ConnectionExpiredException - } + } else if (retryOnFailure(t)) { + Throwable cause = retryCause(t); - if (!canRetryRequest(currentreq)) { - return failedFuture(cause); // fails with original cause + if (!(t instanceof ConnectException)) { + if (!canRetryRequest(currentreq)) { + return failedFuture(cause); // fails with original cause + } } // allow the retry mechanism to do its work retryCause = cause; if (!expiredOnce) { if (debug.on()) - debug.log("ConnectionExpiredException (async): retrying...", t); + debug.log(t.getClass().getSimpleName() + " (async): retrying...", t); expiredOnce = true; // The connection was abruptly closed. // We return null to retry the same request a second time. @@ -350,9 +369,11 @@ previousreq = currentreq; return null; } else { - if (debug.on()) - debug.log("ConnectionExpiredException (async): already retried once.", t); - if (t.getCause() != null) t = t.getCause(); + if (debug.on()) { + debug.log(t.getClass().getSimpleName() + + " (async): already retried once.", t); + } + t = cause; } } return failedFuture(t); @@ -364,9 +385,10 @@ } @Override public void handle() { - if (debug.on()) + if (debug.on()) { debug.log("Cancelling MultiExchange due to timeout for request %s", - request); + request); + } cancel(new HttpTimeoutException("request timed out")); } }