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/12/10 21:31:16 UTC

[httpcomponents-client] branch HTTPCLIENT-2035 updated (820de7d -> 4b757a5)

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

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


 discard 820de7d  HTTPCLIENT-2035: Remove HttpRequestRetryHandler in favor of HttpRequestRetryStrategy
     new 4b757a5  HTTPCLIENT-2035: Remove HttpRequestRetryHandler in favor of HttpRequestRetryStrategy

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (820de7d)
            \
             N -- N -- N   refs/heads/HTTPCLIENT-2035 (4b757a5)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

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.


Summary of changes:
 .../src/main/java/org/apache/hc/client5/http/impl/ChainElement.java     | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)


[httpcomponents-client] 01/01: HTTPCLIENT-2035: Remove HttpRequestRetryHandler in favor of HttpRequestRetryStrategy

Posted by mi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4b757a52aea7de406e52ead28f7f8a9e2cc854a0
Author: Michael Osipov <mi...@apache.org>
AuthorDate: Tue Dec 10 22:24:17 2019 +0100

    HTTPCLIENT-2035: Remove HttpRequestRetryHandler in favor of HttpRequestRetryStrategy
---
 .../hc/client5/http/HttpRequestRetryHandler.java   |  65 -------
 .../apache/hc/client5/http/impl/ChainElement.java  |   2 +-
 .../http/impl/DefaultHttpRequestRetryHandler.java  | 169 -------------------
 .../hc/client5/http/impl/async/AsyncRetryExec.java | 151 -----------------
 .../http/impl/async/H2AsyncClientBuilder.java      |  32 +---
 .../http/impl/async/HttpAsyncClientBuilder.java    |  32 +---
 .../http/impl/classic/HttpClientBuilder.java       |  33 +---
 .../hc/client5/http/impl/classic/RetryExec.java    | 122 --------------
 .../impl/TestDefaultHttpRequestRetryHandler.java   |  88 ----------
 .../client5/http/impl/classic/TestRetryExec.java   | 186 ---------------------
 10 files changed, 19 insertions(+), 861 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
deleted file mode 100644
index e1573b4..0000000
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/HttpRequestRetryHandler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * ====================================================================
- * 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;
-
-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.protocol.HttpContext;
-
-/**
- * A handler for determining if an HttpRequest should be retried after a
- * recoverable exception during execution.
- * <p>
- * Implementations of this interface must be thread-safe. Access to shared
- * data must be synchronized as methods of this interface may be executed
- * from multiple threads.
- *
- * @since 4.0
- */
-@Contract(threading = ThreadingBehavior.STATELESS)
-public interface HttpRequestRetryHandler {
-
-    /**
-     * 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);
-
-}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ChainElement.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ChainElement.java
index 3c9b9fb..294c28e 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ChainElement.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/ChainElement.java
@@ -34,7 +34,7 @@ package org.apache.hc.client5.http.impl;
  */
 public enum ChainElement {
 
-    REDIRECT, BACK_OFF, RETRY, RETRY_SERVICE_UNAVAILABLE, RETRY_IO_ERROR, CACHING, PROTOCOL,
+    REDIRECT, BACK_OFF, RETRY, RETRY_SERVICE_UNAVAILABLE, CACHING, PROTOCOL,
     CONNECT, MAIN_TRANSPORT
 
 }
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
deleted file mode 100644
index 2591aa9..0000000
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultHttpRequestRetryHandler.java
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * ====================================================================
- * 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.HashSet;
-import java.util.Set;
-
-import javax.net.ssl.SSLException;
-
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
-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.HttpRequest;
-import org.apache.hc.core5.http.Methods;
-import org.apache.hc.core5.http.protocol.HttpContext;
-import org.apache.hc.core5.util.Args;
-
-/**
- * The default {@link HttpRequestRetryHandler} used by request executors.
- *
- * @since 4.0
- */
-@Contract(threading = ThreadingBehavior.STATELESS)
-public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler {
-
-    public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler();
-
-    /**
-     * the number of times a method will be retried
-     */
-    private final int retryCount;
-
-    private final Set<Class<? extends IOException>> nonRetriableClasses;
-
-    /**
-     * Create the request retry handler using the specified IOException classes
-     *
-     * @param retryCount how many times to retry; 0 means no retries
-     * @param clazzes    the IOException types that should not be retried
-     * @since 5.0
-     */
-    @SafeVarargs
-    protected DefaultHttpRequestRetryHandler(
-            final int retryCount,
-            final Class<? extends IOException>... clazzes) {
-        super();
-        this.retryCount = retryCount;
-        this.nonRetriableClasses = new HashSet<>();
-        this.nonRetriableClasses.addAll(Arrays.asList(clazzes));
-    }
-
-    /**
-     * Create the request retry handler 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>
-     *
-     * @param retryCount how many times to retry; 0 means no retries
-     * @since 5.0
-     */
-    public DefaultHttpRequestRetryHandler(final int retryCount) {
-        this(retryCount,
-                InterruptedIOException.class,
-                UnknownHostException.class,
-                ConnectException.class,
-                ConnectionClosedException.class,
-                SSLException.class);
-    }
-
-    /**
-     * Create the request retry handler with a retry count of 3, requestSentRetryEnabled false
-     * and using the following list of non-retriable IOException classes: <br>
-     * <ul>
-     * <li>InterruptedIOException</li>
-     * <li>UnknownHostException</li>
-     * <li>ConnectException</li>
-     * <li>SSLException</li>
-     * </ul>
-     */
-    public DefaultHttpRequestRetryHandler() {
-        this(3);
-    }
-
-    /**
-     * Used {@code retryCount} and {@code requestSentRetryEnabled} to determine
-     * if the given method should be retried.
-     */
-    @Override
-    public boolean retryRequest(
-            final HttpRequest request,
-            final IOException exception,
-            final int executionCount,
-            final HttpContext context) {
-        Args.notNull(request, "HTTP request");
-        Args.notNull(exception, "I/O exception");
-
-        if (executionCount > this.retryCount) {
-            // Do not retry if over max retry count
-            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);
-    }
-
-    /**
-     * @return the maximum number of times a method will be retried
-     */
-    public int getRetryCount() {
-        return retryCount;
-    }
-
-    /**
-     * @since 4.2
-     */
-    protected boolean handleAsIdempotent(final HttpRequest request) {
-        return Methods.isIdempotent(request.getMethod());
-    }
-
-}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRetryExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRetryExec.java
deleted file mode 100644
index d301cf0..0000000
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRetryExec.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * ====================================================================
- * 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.async;
-
-import java.io.IOException;
-
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
-import org.apache.hc.client5.http.HttpRoute;
-import org.apache.hc.client5.http.async.AsyncExecCallback;
-import org.apache.hc.client5.http.async.AsyncExecChain;
-import org.apache.hc.client5.http.async.AsyncExecChainHandler;
-import org.apache.hc.client5.http.impl.RequestCopier;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.Internal;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.EntityDetails;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.HttpResponse;
-import org.apache.hc.core5.http.nio.AsyncDataConsumer;
-import org.apache.hc.core5.http.nio.AsyncEntityProducer;
-import org.apache.hc.core5.util.Args;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Request execution handler in the asynchronous request execution chain
- * responsbile for making a decision whether a request failed due to
- * an I/O error should be re-executed.
- * <p>
- * Further responsibilities such as communication with the opposite
- * endpoint is delegated to the next executor in the request execution
- * chain.
- * </p>
- *
- * @since 5.0
- */
-@Contract(threading = ThreadingBehavior.STATELESS)
-@Internal
-public final class AsyncRetryExec implements AsyncExecChainHandler {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    private final HttpRequestRetryHandler retryHandler;
-
-    public AsyncRetryExec(final HttpRequestRetryHandler retryHandler) {
-        Args.notNull(retryHandler, "HTTP request retry handler");
-        this.retryHandler = retryHandler;
-    }
-
-    private void internalExecute(
-            final int execCount,
-            final HttpRequest request,
-            final AsyncEntityProducer entityProducer,
-            final AsyncExecChain.Scope scope,
-            final AsyncExecChain chain,
-            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
-
-        final String exchangeId = scope.exchangeId;
-
-        chain.proceed(RequestCopier.INSTANCE.copy(request), entityProducer, scope, new AsyncExecCallback() {
-
-            @Override
-            public AsyncDataConsumer handleResponse(
-                    final HttpResponse response,
-                    final EntityDetails entityDetails) throws HttpException, IOException {
-                return asyncExecCallback.handleResponse(response, entityDetails);
-            }
-
-            @Override
-            public void handleInformationResponse(final HttpResponse response) throws HttpException, IOException {
-                asyncExecCallback.handleInformationResponse(response);
-            }
-
-            @Override
-            public void completed() {
-                asyncExecCallback.completed();
-            }
-
-            @Override
-            public void failed(final Exception cause) {
-                if (cause instanceof IOException) {
-                    final HttpRoute route = scope.route;
-                    final HttpClientContext clientContext = scope.clientContext;
-                    if (entityProducer != null && !entityProducer.isRepeatable()) {
-                        if (log.isDebugEnabled()) {
-                            log.debug(exchangeId + ": cannot retry non-repeatable request");
-                        }
-                    } else if (retryHandler.retryRequest(request, (IOException) cause, execCount, clientContext)) {
-                        if (log.isDebugEnabled()) {
-                            log.debug(exchangeId + ": " + cause.getMessage(), cause);
-                        }
-                        if (log.isInfoEnabled()) {
-                            log.info("Recoverable I/O exception ("+ cause.getClass().getName() + ") " +
-                                    "caught when processing request to " + route);
-                        }
-                        scope.execRuntime.discardEndpoint();
-                        if (entityProducer != null) {
-                            entityProducer.releaseResources();
-                        }
-                        try {
-                            internalExecute(execCount + 1, request, entityProducer, scope, chain, asyncExecCallback);
-                        } catch (final IOException | HttpException ex) {
-                            asyncExecCallback.failed(ex);
-                        }
-                        return;
-                    }
-                }
-                asyncExecCallback.failed(cause);
-            }
-
-        });
-
-    }
-
-    @Override
-    public void execute(
-            final HttpRequest request,
-            final AsyncEntityProducer entityProducer,
-            final AsyncExecChain.Scope scope,
-            final AsyncExecChain chain,
-            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
-        internalExecute(1, request, entityProducer, scope, chain, asyncExecCallback);
-    }
-
-}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
index 501ece7..13ddb63 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/H2AsyncClientBuilder.java
@@ -40,7 +40,6 @@ import java.util.concurrent.ThreadFactory;
 
 import org.apache.hc.client5.http.AuthenticationStrategy;
 import org.apache.hc.client5.http.DnsResolver;
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
 import org.apache.hc.client5.http.HttpRequestRetryStrategy;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.SystemDefaultDnsResolver;
@@ -192,7 +191,6 @@ public class H2AsyncClientBuilder {
 
     private HttpRoutePlanner routePlanner;
     private RedirectStrategy redirectStrategy;
-    private HttpRequestRetryHandler retryHandler;
     private HttpRequestRetryStrategy retryStrategy;
 
     private Lookup<AuthSchemeProvider> authSchemeRegistry;
@@ -381,17 +379,6 @@ public class H2AsyncClientBuilder {
     }
 
     /**
-     * Assigns {@link HttpRequestRetryHandler} instance.
-     * <p>
-     * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
-     * method.
-     */
-    public final H2AsyncClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) {
-        this.retryHandler = retryHandler;
-        return this;
-    }
-
-    /**
      * Assigns {@link HttpRequestRetryStrategy} instance.
      * <p>
      * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
@@ -687,20 +674,13 @@ public class H2AsyncClientBuilder {
 
         // Add request retry executor, if not disabled
         if (!automaticRetriesDisabled) {
-            final HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
-            if (retryHandlerCopy != null) {
-                execChainDefinition.addFirst(
-                        new AsyncRetryExec(retryHandlerCopy),
-                        ChainElement.RETRY_IO_ERROR.name());
-            } else {
-                HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
-                if (retryStrategyCopy == null) {
-                    retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
-                }
-                execChainDefinition.addFirst(
-                        new AsyncHttpRequestRetryExec(retryStrategyCopy),
-                        ChainElement.RETRY.name());
+            HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
+            if (retryStrategyCopy == null) {
+                retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
             }
+            execChainDefinition.addFirst(
+                    new AsyncHttpRequestRetryExec(retryStrategyCopy),
+                    ChainElement.RETRY.name());
         }
 
         HttpRoutePlanner routePlannerCopy = this.routePlanner;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
index 97784b8..3a724f9 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/HttpAsyncClientBuilder.java
@@ -40,7 +40,6 @@ import java.util.concurrent.ThreadFactory;
 
 import org.apache.hc.client5.http.AuthenticationStrategy;
 import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
 import org.apache.hc.client5.http.HttpRequestRetryStrategy;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.SystemDefaultDnsResolver;
@@ -227,7 +226,6 @@ public class HttpAsyncClientBuilder {
 
     private HttpRoutePlanner routePlanner;
     private RedirectStrategy redirectStrategy;
-    private HttpRequestRetryHandler retryHandler;
     private HttpRequestRetryStrategy retryStrategy;
 
     private ConnectionReuseStrategy reuseStrategy;
@@ -488,17 +486,6 @@ public class HttpAsyncClientBuilder {
     }
 
     /**
-     * Assigns {@link HttpRequestRetryHandler} instance.
-     * <p>
-     * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
-     * method.
-     */
-    public final HttpAsyncClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) {
-        this.retryHandler = retryHandler;
-        return this;
-    }
-
-    /**
      * Assigns {@link HttpRequestRetryStrategy} instance.
      * <p>
      * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
@@ -837,20 +824,13 @@ public class HttpAsyncClientBuilder {
 
         // Add request retry executor, if not disabled
         if (!automaticRetriesDisabled) {
-            final HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
-            if (retryHandlerCopy != null) {
-                execChainDefinition.addFirst(
-                        new AsyncRetryExec(retryHandlerCopy),
-                        ChainElement.RETRY_IO_ERROR.name());
-            } else {
-                HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
-                if (retryStrategyCopy == null) {
-                    retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
-                }
-                execChainDefinition.addFirst(
-                        new AsyncHttpRequestRetryExec(retryStrategyCopy),
-                        ChainElement.RETRY.name());
+            HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
+            if (retryStrategyCopy == null) {
+                retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
             }
+            execChainDefinition.addFirst(
+                    new AsyncHttpRequestRetryExec(retryStrategyCopy),
+                    ChainElement.RETRY.name());
         }
 
         HttpRoutePlanner routePlannerCopy = this.routePlanner;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
index ddb3199..56f1361 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/HttpClientBuilder.java
@@ -39,7 +39,6 @@ import java.util.Map;
 
 import org.apache.hc.client5.http.AuthenticationStrategy;
 import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
 import org.apache.hc.client5.http.HttpRequestRetryStrategy;
 import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.ServiceUnavailableRetryStrategy;
@@ -204,7 +203,6 @@ public class HttpClientBuilder {
     private LinkedList<ResponseInterceptorEntry> responseInterceptors;
     private LinkedList<ExecInterceptorEntry> execInterceptors;
 
-    private HttpRequestRetryHandler retryHandler;
     private HttpRequestRetryStrategy retryStrategy;
     private HttpRoutePlanner routePlanner;
     private RedirectStrategy redirectStrategy;
@@ -497,17 +495,6 @@ public class HttpClientBuilder {
     }
 
     /**
-     * Assigns {@link HttpRequestRetryHandler} instance.
-     * <p>
-     * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
-     * method.
-     */
-    public final HttpClientBuilder setRetryHandler(final HttpRequestRetryHandler retryHandler) {
-        this.retryHandler = retryHandler;
-        return this;
-    }
-
-    /**
      * Assigns {@link HttpRequestRetryStrategy} instance.
      * <p>
      * Please note this value can be overridden by the {@link #disableAutomaticRetries()}
@@ -872,21 +859,13 @@ public class HttpClientBuilder {
 
         // Add request retry executor, if not disabled
         if (!automaticRetriesDisabled) {
-            // This needs to be cleaned up as soon as HttpRequestRetryHandler will be removed
-            final HttpRequestRetryHandler retryHandlerCopy = this.retryHandler;
-            if (retryHandlerCopy != null) {
-                execChainDefinition.addFirst(
-                        new RetryExec(retryHandlerCopy),
-                        ChainElement.RETRY_IO_ERROR.name());
-            } else {
-                HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
-                if (retryStrategyCopy == null) {
-                    retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
-                }
-                execChainDefinition.addFirst(
-                        new HttpRequestRetryExec(retryStrategyCopy),
-                        ChainElement.RETRY.name());
+            HttpRequestRetryStrategy retryStrategyCopy = this.retryStrategy;
+            if (retryStrategyCopy == null) {
+                retryStrategyCopy = DefaultHttpRequestRetryStrategy.INSTANCE;
             }
+            execChainDefinition.addFirst(
+                    new HttpRequestRetryExec(retryStrategyCopy),
+                    ChainElement.RETRY.name());
         }
 
         HttpRoutePlanner routePlannerCopy = this.routePlanner;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/RetryExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/RetryExec.java
deleted file mode 100644
index 2e89f4a..0000000
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/RetryExec.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * ====================================================================
- * 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.classic;
-
-import java.io.IOException;
-
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
-import org.apache.hc.client5.http.HttpRoute;
-import org.apache.hc.client5.http.classic.ExecChain;
-import org.apache.hc.client5.http.classic.ExecChainHandler;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.Internal;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.ClassicHttpResponse;
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.NoHttpResponseException;
-import org.apache.hc.core5.util.Args;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Request execution handler in the classic request execution chain
- * responsible for making a decision whether a request failed due to
- * an I/O error should be re-executed.
- * <p>
- * Further responsibilities such as communication with the opposite
- * endpoint is delegated to the next executor in the request execution
- * chain.
- * </p>
- *
- * @since 4.3
- */
-@Contract(threading = ThreadingBehavior.STATELESS)
-@Internal
-public final class RetryExec implements ExecChainHandler {
-
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    private final HttpRequestRetryHandler retryHandler;
-
-    public RetryExec(
-            final HttpRequestRetryHandler retryHandler) {
-        Args.notNull(retryHandler, "HTTP request retry handler");
-        this.retryHandler = retryHandler;
-    }
-
-    @Override
-    public ClassicHttpResponse execute(
-            final ClassicHttpRequest request,
-            final ExecChain.Scope scope,
-            final ExecChain chain) throws IOException, HttpException {
-        Args.notNull(request, "HTTP request");
-        Args.notNull(scope, "Scope");
-        final String exchangeId = scope.exchangeId;
-        final HttpRoute route = scope.route;
-        final HttpClientContext context = scope.clientContext;
-        ClassicHttpRequest currentRequest = request;
-        for (int execCount = 1;; execCount++) {
-            try {
-                return chain.proceed(currentRequest, scope);
-            } catch (final IOException ex) {
-                if (scope.execRuntime.isExecutionAborted()) {
-                    throw new RequestFailedException("Request aborted");
-                }
-                final HttpEntity requestEntity = request.getEntity();
-                if (requestEntity != null && !requestEntity.isRepeatable()) {
-                    if (log.isDebugEnabled()) {
-                        log.debug(exchangeId + ": cannot retry non-repeatable request");
-                    }
-                    throw ex;
-                }
-                if (retryHandler.retryRequest(request, ex, execCount, context)) {
-                    if (log.isDebugEnabled()) {
-                        log.debug(exchangeId + ": " + ex.getMessage(), ex);
-                    }
-                    if (log.isInfoEnabled()) {
-                        log.info("Recoverable I/O exception ("+ ex.getClass().getName() + ") " +
-                                "caught when processing request to " + route);
-                    }
-                    currentRequest = ClassicRequestCopier.INSTANCE.copy(scope.originalRequest);
-                } else {
-                    if (ex instanceof NoHttpResponseException) {
-                        final NoHttpResponseException updatedex = new NoHttpResponseException(
-                                route.getTargetHost().toHostString() + " failed to respond");
-                        updatedex.setStackTrace(ex.getStackTrace());
-                        throw updatedex;
-                    }
-                    throw ex;
-                }
-            }
-        }
-    }
-
-}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestDefaultHttpRequestRetryHandler.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestDefaultHttpRequestRetryHandler.java
deleted file mode 100644
index faf13af..0000000
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/TestDefaultHttpRequestRetryHandler.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * ====================================================================
- * 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.net.SocketTimeoutException;
-import java.net.UnknownHostException;
-
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.junit.Assert;
-import org.junit.Test;
-
-
-@SuppressWarnings("boxing") // test class
-public class TestDefaultHttpRequestRetryHandler {
-
-    @Test
-    public void noRetryOnConnectTimeout() throws Exception {
-        final HttpGet request = new HttpGet("/");
-
-        final DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
-        Assert.assertEquals(3, retryHandler.getRetryCount());
-
-        Assert.assertFalse(retryHandler.retryRequest(request, new SocketTimeoutException(), 1, null));
-    }
-
-    @Test
-    public void noRetryOnUnknownHost() throws Exception {
-        final HttpGet request = new HttpGet("/");
-
-        final DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
-
-        Assert.assertFalse(retryHandler.retryRequest(request, new UnknownHostException(), 1, null));
-    }
-
-    @Test
-    public void noRetryOnAbortedRequests() throws Exception{
-        final HttpGet request = new HttpGet("/");
-        request.cancel();
-
-        final DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
-
-        Assert.assertFalse(retryHandler.retryRequest(request, new IOException(), 3, null));
-    }
-
-    @Test
-    public void retryOnNonAbortedRequests() throws Exception{
-        final HttpGet request = new HttpGet("/");
-
-        final DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
-
-        Assert.assertTrue(retryHandler.retryRequest(request, new IOException(), 3, null));
-    }
-
-    @Test
-    public void noRetryOnConnectionTimeout() throws Exception{
-        final HttpGet request = new HttpGet("/");
-
-        final DefaultHttpRequestRetryHandler retryHandler = new DefaultHttpRequestRetryHandler();
-
-        Assert.assertFalse(retryHandler.retryRequest(request, new SocketTimeoutException(), 3, null));
-    }
-
-}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java
deleted file mode 100644
index 955753b..0000000
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestRetryExec.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * ====================================================================
- * 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.classic;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
-import org.apache.hc.client5.http.HttpRequestRetryHandler;
-import org.apache.hc.client5.http.HttpRoute;
-import org.apache.hc.client5.http.classic.ExecChain;
-import org.apache.hc.client5.http.classic.ExecRuntime;
-import org.apache.hc.client5.http.classic.methods.HttpGet;
-import org.apache.hc.client5.http.classic.methods.HttpPost;
-import org.apache.hc.client5.http.entity.EntityBuilder;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.core5.http.ClassicHttpRequest;
-import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpHost;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.protocol.HttpContext;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-
-@SuppressWarnings({"boxing","static-access"}) // test code
-public class TestRetryExec {
-
-    @Mock
-    private HttpRequestRetryHandler retryHandler;
-    @Mock
-    private ExecRuntime endpoint;
-    @Mock
-    private ExecChain chain;
-
-    private RetryExec retryExec;
-    private HttpHost target;
-
-    @Before
-    public void setup() throws Exception {
-        MockitoAnnotations.initMocks(this);
-        retryExec = new RetryExec(retryHandler);
-        target = new HttpHost("localhost", 80);
-    }
-
-    @Test(expected = IOException.class)
-    public void testFundamentals() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpGet originalRequest = new HttpGet("/test");
-        originalRequest.addHeader("header", "this");
-        originalRequest.addHeader("header", "that");
-        final HttpClientContext context = HttpClientContext.create();
-
-        Mockito.when(chain.proceed(
-                Mockito.<ClassicHttpRequest>any(),
-                Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
-
-            @Override
-            public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
-                final Object[] args = invocationOnMock.getArguments();
-                final ClassicHttpRequest wrapper = (ClassicHttpRequest) args[0];
-                final Header[] headers = wrapper.getHeaders();
-                Assert.assertEquals(2, headers.length);
-                Assert.assertEquals("this", headers[0].getValue());
-                Assert.assertEquals("that", headers[1].getValue());
-                wrapper.addHeader("Cookie", "monster");
-                throw new IOException("Ka-boom");
-            }
-
-        });
-        Mockito.when(retryHandler.retryRequest(
-                Mockito.<HttpRequest>any(),
-                Mockito.<IOException>any(),
-                Mockito.eq(1),
-                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
-        final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
-        try {
-            retryExec.execute(request, scope, chain);
-        } catch (final IOException ex) {
-            Mockito.verify(chain, Mockito.times(2)).proceed(
-                    Mockito.<ClassicHttpRequest>any(),
-                    Mockito.same(scope));
-            throw ex;
-        }
-    }
-
-    @Test(expected = IOException.class)
-    public void testAbortedRequest() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpGet originalRequest = new HttpGet("/test");
-        final HttpClientContext context = HttpClientContext.create();
-
-        Mockito.when(chain.proceed(
-                Mockito.<ClassicHttpRequest>any(),
-                Mockito.<ExecChain.Scope>any())).thenThrow(new IOException("Ka-boom"));
-        Mockito.when(endpoint.isExecutionAborted()).thenReturn(true);
-
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
-        final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
-        try {
-            retryExec.execute(request, scope, chain);
-        } catch (final IOException ex) {
-            Mockito.verify(chain, Mockito.times(1)).proceed(
-                    Mockito.same(request),
-                    Mockito.same(scope));
-            Mockito.verify(retryHandler, Mockito.never()).retryRequest(
-                    Mockito.<HttpRequest>any(),
-                    Mockito.<IOException>any(),
-                    Mockito.anyInt(),
-                    Mockito.<HttpContext>any());
-
-            throw ex;
-        }
-    }
-
-    @Test(expected = IOException.class)
-    public void testNonRepeatableRequest() throws Exception {
-        final HttpRoute route = new HttpRoute(target);
-        final HttpPost originalRequest = new HttpPost("/test");
-        originalRequest.setEntity(EntityBuilder.create()
-                .setStream(new ByteArrayInputStream(new byte[]{}))
-                .build());
-        final HttpClientContext context = HttpClientContext.create();
-
-        Mockito.when(chain.proceed(
-                Mockito.<ClassicHttpRequest>any(),
-                Mockito.<ExecChain.Scope>any())).thenAnswer(new Answer<Object>() {
-
-            @Override
-            public Object answer(final InvocationOnMock invocationOnMock) throws Throwable {
-                final Object[] args = invocationOnMock.getArguments();
-                final ClassicHttpRequest req = (ClassicHttpRequest) args[0];
-                req.getEntity().writeTo(new ByteArrayOutputStream());
-                throw new IOException("Ka-boom");
-            }
-
-        });
-        Mockito.when(retryHandler.retryRequest(
-                Mockito.<HttpRequest>any(),
-                Mockito.<IOException>any(),
-                Mockito.eq(1),
-                Mockito.<HttpContext>any())).thenReturn(Boolean.TRUE);
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, originalRequest, endpoint, context);
-        final ClassicHttpRequest request = ClassicRequestCopier.INSTANCE.copy(originalRequest);
-        try {
-            retryExec.execute(request, scope, chain);
-        } catch (final IOException ex) {
-            Mockito.verify(chain, Mockito.times(1)).proceed(
-                    Mockito.same(request),
-                    Mockito.same(scope));
-
-            throw ex;
-        }
-    }
-
-}