@Override
            public AsyncDataConsumer handleResponse(
                    final HttpResponse response,
                    final EntityDetails entityDetails) throws HttpException, IOException {

                state.redirectURI = null;
                final RequestConfig config = clientContext.getRequestConfigOrDefault();
                if (config.isRedirectsEnabled() && redirectStrategy.isRedirected(request, response, clientContext)) {
                    if (state.redirectCount >= state.maxRedirects) {
                        throw new RedirectException("Maximum redirects (" + state.maxRedirects + ") exceeded");
                    }

                    state.redirectCount++;

                    final URI redirectUri = redirectStrategy.getLocationURI(request, response, clientContext);
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("{} redirect requested to location '{}'", exchangeId, redirectUri);
                    }
                    if (!config.isCircularRedirectsAllowed()) {
                        if (state.redirectLocations.contains(redirectUri)) {
                            throw new CircularRedirectException("Circular redirect to '" + redirectUri + "'");
                        }
                    }

                    final AsyncExecChain.Scope currentScope = state.currentScope;
                    final HttpHost newTarget = URIUtils.extractHost(redirectUri);
                    if (newTarget == null) {
                        throw new ProtocolException("Redirect URI does not specify a valid host name: " + redirectUri);
                    }

                    final int statusCode = response.getCode();
                    final BasicRequestBuilder redirectBuilder;
                    switch (statusCode) {
                        case HttpStatus.SC_MOVED_PERMANENTLY:
                        case HttpStatus.SC_MOVED_TEMPORARILY:
                            if (Method.POST.isSame(request.getMethod())) {
                                redirectBuilder = BasicRequestBuilder.get();
                                state.currentEntityProducer = null;
                            } else {
                                redirectBuilder = BasicRequestBuilder.copy(currentScope.originalRequest);
                            }
                            break;
                        case HttpStatus.SC_SEE_OTHER:
                            if (!Method.GET.isSame(request.getMethod()) && !Method.HEAD.isSame(request.getMethod())) {
                                redirectBuilder = BasicRequestBuilder.get();
                                state.currentEntityProducer = null;
                            } else {
                                redirectBuilder = BasicRequestBuilder.copy(currentScope.originalRequest);
                            }
                            break;
                        default:
                            redirectBuilder = BasicRequestBuilder.copy(currentScope.originalRequest);
                    }
                    redirectBuilder.setUri(redirectUri);
                    final BasicHttpRequest redirect = redirectBuilder.build();

                    final HttpRoute currentRoute = currentScope.route;
                    final HttpHost currentHost = currentRoute.getTargetHost();

                    if (!redirectStrategy.isRedirectAllowed(currentHost, newTarget, redirect, clientContext)) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("{} cannot redirect due to safety restrictions", exchangeId);
                        }
                        return asyncExecCallback.handleResponse(response, entityDetails);
                    }

                    state.redirectLocations.add(redirectUri);

                    state.reroute = false;
                    state.redirectURI = redirectUri;
                    state.currentRequest = redirect;

                    final HttpRoute newRoute;
                    if (!Objects.equals(currentHost, newTarget)) {
                        newRoute = routePlanner.determineRoute(newTarget, clientContext);
                        if (!Objects.equals(currentRoute, newRoute)) {
                            state.reroute = true;
                            final AuthExchange targetAuthExchange = clientContext.getAuthExchange(currentHost);
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("{} resetting target auth state", exchangeId);
                            }
                            targetAuthExchange.reset();
                            if (currentRoute.getProxyHost() != null) {
                                final AuthExchange proxyAuthExchange = clientContext.getAuthExchange(currentRoute.getProxyHost());
                                if (proxyAuthExchange.isConnectionBased()) {
                                    if (LOG.isDebugEnabled()) {
                                        LOG.debug("{} resetting proxy auth state", exchangeId);
                                    }
                                    proxyAuthExchange.reset();
                                }
                            }
                        }
                    } else {
                        newRoute = currentRoute;
                    }
                    state.currentScope = new AsyncExecChain.Scope(
                            scope.exchangeId,
                            newRoute,
                            BasicRequestBuilder.copy(redirect).build(),
                            scope.cancellableDependency,
                            scope.clientContext,
                            scope.execRuntime,
                            scope.scheduler,
                            scope.execCount);
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("{} redirecting to '{}' via {}", exchangeId, redirectUri, newRoute);
                    }
                    return null;
                }
                return asyncExecCallback.handleResponse(response, entityDetails);
            }
Request execution handler in the asynchronous request execution chain
responsible for handling of request redirects.
<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
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncRedirectExec.java