--- a/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java Wed Jun 20 17:15:16 2018 +0200
+++ b/src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java Wed Jun 20 09:05:57 2018 -0700
@@ -26,7 +26,7 @@
package jdk.internal.net.http;
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;
@@ -69,7 +69,7 @@
final AccessControlContext acc;
final HttpClientImpl client;
final HttpResponse.BodyHandler<T> responseHandler;
- final Executor executor;
+ final HttpClientImpl.DelegatingExecutor executor;
final AtomicInteger attempts = new AtomicInteger();
HttpRequestImpl currentreq; // used for retries & redirect
HttpRequestImpl previousreq; // used for retries & redirect
@@ -124,8 +124,8 @@
if (pushPromiseHandler != null) {
Executor executor = acc == null
- ? this.executor
- : new PrivilegedExecutor(this.executor, acc);
+ ? this.executor.delegate()
+ : new PrivilegedExecutor(this.executor.delegate(), acc);
this.pushGroup = new PushGroup<>(pushPromiseHandler, request, executor);
} else {
pushGroup = null;
@@ -193,7 +193,7 @@
getExchange().cancel(cause);
}
- public CompletableFuture<HttpResponse<T>> responseAsync() {
+ public CompletableFuture<HttpResponse<T>> responseAsync(Executor executor) {
CompletableFuture<Void> start = new MinimalFuture<>();
CompletableFuture<HttpResponse<T>> cf = responseAsync0(start);
start.completeAsync( () -> null, executor); // trigger execution
@@ -285,13 +285,22 @@
private static boolean retryPostValue() {
String s = Utils.getNetProperty("jdk.httpclient.enableAllMethodRetry");
- if (s == "" || "true".equals(s))
- return true;
- return false;
+ if (s == null)
+ return false;
+ return s.isEmpty() ? true : Boolean.parseBoolean(s);
+ }
+
+ private static boolean retryConnect() {
+ String s = Utils.getNetProperty("jdk.httpclient.disableRetryConnect");
+ if (s == null)
+ return false;
+ return s.isEmpty() ? true : Boolean.parseBoolean(s);
}
/** 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 +316,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 +345,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 +368,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 +384,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"));
}
}