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:04 UTC

[httpcomponents-client] branch HTTPCLIENT-2019 created (now 384ab23)

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a change to branch HTTPCLIENT-2019
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git.


      at 384ab23  HTTPCLIENT-2019: WIP

This branch includes the following new commits:

     new 384ab23  HTTPCLIENT-2019: WIP

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[httpcomponents-client] 01/01: HTTPCLIENT-2019: WIP

Posted by mi...@apache.org.
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 {