You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by mi...@apache.org on 2019/11/27 14:22:05 UTC
[httpcomponents-client] 01/01: HTTPCLIENT-2019: WIP
This is an automated email from the ASF dual-hosted git repository.
michaelo pushed a commit to branch HTTPCLIENT-2019
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
commit 384ab239de30013868b0bda47928ce8a66c0a146
Author: Michael Osipov <mi...@apache.org>
AuthorDate: Wed Nov 27 15:21:40 2019 +0100
HTTPCLIENT-2019: WIP
---
.../hc/client5/http/HttpRequestRetryHandler.java | 2 +
...Strategy.java => HttpRequestRetryStrategy.java} | 37 +++-
.../http/ServiceUnavailableRetryStrategy.java | 2 +
.../http/impl/DefaultHttpRequestRetryHandler.java | 2 +
.../http/impl/DefaultHttpRequestRetryStrategy.java | 241 +++++++++++++++++++++
.../DefaultServiceUnavailableRetryStrategy.java | 2 +
6 files changed, 281 insertions(+), 5 deletions(-)
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryHandler.java
index e1573b4..3c430ad 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryHandler.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryHandler.java
@@ -43,7 +43,9 @@ import org.apache.hc.core5.http.protocol.HttpContext;
* from multiple threads.
*
* @since 4.0
+ * @see HttpRequestRetryStrategy
*/
+@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public interface HttpRequestRetryHandler {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryStrategy.java
similarity index 63%
copy from httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java
copy to httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryStrategy.java
index 9575330..dec7c72 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryStrategy.java
@@ -27,23 +27,43 @@
package org.apache.hc.client5.http;
+import java.io.IOException;
+
import org.apache.hc.core5.annotation.Contract;
import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.TimeValue;
/**
* Strategy interface that allows API users to plug in their own logic to
* control whether or not a retry should automatically be done, how many times
* it should be retried and so on.
*
- * @since 4.2
+ * @since 5.0
*/
@Contract(threading = ThreadingBehavior.STATELESS)
-public interface ServiceUnavailableRetryStrategy {
+public interface HttpRequestRetryStrategy {
/**
- * Determines if a method should be retried given the response from the target server.
+ * Determines if a method should be retried after an IOException
+ * occurs during execution.
+ *
+ * @param request request failed die to an I/O exception.
+ * @param exception the exception that occurred
+ * @param executionCount the number of times this method has been
+ * unsuccessfully executed
+ * @param context the context for the request execution
+ *
+ * @return {@code true} if the method should be retried, {@code false}
+ * otherwise
+ */
+ boolean retryRequest(HttpRequest request, IOException exception, int executionCount, HttpContext context);
+
+ /**
+ * Determines if a method should be retried given the response from
+ * the target server.
*
* @param response the response from the target server
* @param executionCount the number of times this method has been
@@ -56,8 +76,15 @@ public interface ServiceUnavailableRetryStrategy {
boolean retryRequest(HttpResponse response, int executionCount, HttpContext context);
/**
- * @return The interval between the subsequent retry in milliseconds.
+ * Determines the retry interval between subsequent retries.
+
+ * @param response the response from the target server
+ * @param executionCount the number of times this method has been
+ * unsuccessfully executed
+ * @param context the context for the request execution
+ *
+ * @return the retry interval between subsequent retries
*/
- long getRetryInterval(HttpResponse response, HttpContext context);
+ TimeValue getRetryInterval(HttpResponse response, int executionCount, HttpContext context);
}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java
index 9575330..96804de 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ServiceUnavailableRetryStrategy.java
@@ -38,7 +38,9 @@ import org.apache.hc.core5.http.protocol.HttpContext;
* it should be retried and so on.
*
* @since 4.2
+ * @see HttpRequestRetryStrategy
*/
+@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public interface ServiceUnavailableRetryStrategy {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryHandler.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryHandler.java
index 2591aa9..8ae7229 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryHandler.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryHandler.java
@@ -51,7 +51,9 @@ import org.apache.hc.core5.util.Args;
* The default {@link HttpRequestRetryHandler} used by request executors.
*
* @since 4.0
+ * @see DefaultHttpRequestRetryStrategy
*/
+@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java
new file mode 100644
index 0000000..ecdd01f
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryStrategy.java
@@ -0,0 +1,241 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.client5.http.impl;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ConnectException;
+import java.net.UnknownHostException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.net.ssl.SSLException;
+
+import org.apache.hc.client5.http.HttpRequestRetryStrategy;
+import org.apache.hc.client5.http.utils.DateUtils;
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.concurrent.CancellableDependency;
+import org.apache.hc.core5.http.ConnectionClosedException;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.Methods;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.TimeValue;
+
+/**
+ * Default implementation of the {@link HttpRequestRetryStrategy} interface.
+ *
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.STATELESS)
+public class DefaultHttpRequestRetryStrategy implements HttpRequestRetryStrategy {
+
+ public static final DefaultHttpRequestRetryStrategy INSTANCE = new DefaultHttpRequestRetryStrategy();
+
+ /**
+ * Maximum number of allowed retries
+ */
+ private final int maxRetries;
+
+ /**
+ * Retry interval between subsequent retries
+ */
+ private final TimeValue defaultRetryInterval;
+
+ /**
+ * Derived {@code IOExceptions} which shall not be retried
+ */
+ private final Set<Class<? extends IOException>> nonRetriableClasses;
+
+ /**
+ * HTTP status codes which shall be retried
+ */
+ private final Set<Integer> retriableCodes;
+
+ protected DefaultHttpRequestRetryStrategy(
+ final int maxRetries,
+ final TimeValue defaultRetryInterval,
+ final Collection<Class<? extends IOException>> clazzes,
+ final Collection<Integer> codes) {
+ Args.positive(maxRetries, "maxRetries");
+ Args.positive(defaultRetryInterval.getDuration(), "defaultRetryInterval");
+ this.maxRetries = maxRetries;
+ this.defaultRetryInterval = defaultRetryInterval;
+ this.nonRetriableClasses = new HashSet<>();
+ this.nonRetriableClasses.addAll(clazzes);
+ this.retriableCodes = new HashSet<>();
+ this.retriableCodes.addAll(codes);
+ }
+
+ /**
+ * Create the HTTP request retry strategy using the following list of
+ * non-retriable IOException classes:<br>
+ * <ul>
+ * <li>InterruptedIOException</li>
+ * <li>UnknownHostException</li>
+ * <li>ConnectException</li>
+ * <li>ConnectionClosedException</li>
+ * <li>SSLException</li>
+ * </ul>
+ *
+ * and retriable HTTP status codes:<br>
+ * <ul>
+ * <li>SC_TOO_MANY_REQUESTS (429)</li>
+ * <li>SC_SERVICE_UNAVAILABLE (503)</li>
+ * </ul>
+ *
+ * @param maxRetries how many times to retry; 0 means no retries
+ * @param defaultRetryInterval the default retry interval between
+ * subsequent retries if the {@code Retry-After} header is not set
+ * or invalid.
+ */
+ public DefaultHttpRequestRetryStrategy(
+ final int maxRetries,
+ final TimeValue defaultRetryInterval) {
+ this(maxRetries, defaultRetryInterval,
+ Arrays.asList(
+ InterruptedIOException.class,
+ UnknownHostException.class,
+ ConnectException.class,
+ ConnectionClosedException.class,
+ SSLException.class),
+ Arrays.asList(
+ HttpStatus.SC_TOO_MANY_REQUESTS,
+ HttpStatus.SC_SERVICE_UNAVAILABLE));
+ }
+
+ /**
+ * Create the HTTP request retry strategy with a max retry count of 1,
+ * default retry interval of 1 second, and using the following list of
+ * non-retriable IOException classes:<br>
+ * <ul>
+ * <li>InterruptedIOException</li>
+ * <li>UnknownHostException</li>
+ * <li>ConnectException</li>
+ * <li>ConnectionClosedException</li>
+ * <li>SSLException</li>
+ * </ul>
+ *
+ * and retriable HTTP status codes:<br>
+ * <ul>
+ * <li>SC_TOO_MANY_REQUESTS (429)</li>
+ * <li>SC_SERVICE_UNAVAILABLE (503)</li>
+ * </ul>
+ */
+ public DefaultHttpRequestRetryStrategy() {
+ this(1, TimeValue.ofSeconds(1L));
+ }
+
+ @Override
+ public boolean retryRequest(
+ final HttpRequest request,
+ final IOException exception,
+ final int executionCount,
+ final HttpContext context) {
+ Args.notNull(request, "request");
+ Args.notNull(exception, "exception");
+
+ if (executionCount > this.maxRetries) {
+ // Do not retry if over max retries
+ return false;
+ }
+ if (this.nonRetriableClasses.contains(exception.getClass())) {
+ return false;
+ } else {
+ for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) {
+ if (rejectException.isInstance(exception)) {
+ return false;
+ }
+ }
+ }
+ if (request instanceof CancellableDependency && ((CancellableDependency) request).isCancelled()) {
+ return false;
+ }
+
+ // Retry if the request is considered idempotent
+ return handleAsIdempotent(request);
+ }
+
+ @Override
+ public boolean retryRequest(
+ final HttpResponse response,
+ final int executionCount,
+ final HttpContext context) {
+ Args.notNull(response, "response");
+
+ return executionCount <= maxRetries && retriableCodes.contains(response.getCode());
+ }
+
+ @Override
+ public TimeValue getRetryInterval(
+ final HttpResponse response,
+ int executionCount,
+ final HttpContext context) {
+ Args.notNull(response, "response");
+
+ final Header header = response.getFirstHeader(HttpHeaders.RETRY_AFTER);
+ TimeValue retryAfter = null;
+ if (header != null) {
+ final String value = header.getValue();
+ try {
+ retryAfter = TimeValue.ofSeconds(Long.parseLong(value));
+ } catch (final NumberFormatException ignore) {
+ final Date retryAfterDate = DateUtils.parseDate(value);
+ if (retryAfterDate != null) {
+ retryAfter =
+ TimeValue.ofMilliseconds(retryAfterDate.getTime() - System.currentTimeMillis());
+ }
+ }
+
+ if (TimeValue.isPositive(retryAfter)) {
+ return retryAfter;
+ }
+ }
+ return this.defaultRetryInterval;
+ }
+
+ /**
+ * @return the maximum number of retries for a request
+ */
+ public int getMaxRetries() {
+ return maxRetries;
+ }
+
+ protected boolean handleAsIdempotent(final HttpRequest request) {
+ return Methods.isIdempotent(request.getMethod());
+ }
+
+}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultServiceUnavailableRetryStrategy.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultServiceUnavailableRetryStrategy.java
index 6c05c60..393de3e 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultServiceUnavailableRetryStrategy.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultServiceUnavailableRetryStrategy.java
@@ -46,7 +46,9 @@ import org.apache.hc.core5.util.Args;
* at a fixed interval.
*
* @since 4.2
+ * @see DefaultHttpRequestRetryStrategy
*/
+@Deprecated
@Contract(threading = ThreadingBehavior.STATELESS)
public class DefaultServiceUnavailableRetryStrategy implements ServiceUnavailableRetryStrategy {