private void doExecute0(ClientRequestContext ctx, HttpRequestDuplicator rootReqDuplicator,
                            HttpRequest originalReq, HttpResponse returnedRes,
                            CompletableFuture<HttpResponse> future) {
        final int totalAttempts = getTotalAttempts(ctx);
        final boolean initialAttempt = totalAttempts <= 1;
        // The request or response has been aborted by the client before it receives a response,
        // so stop retrying.
        if (originalReq.whenComplete().isCompletedExceptionally()) {
            originalReq.whenComplete().handle((unused, cause) -> {
                handleException(ctx, rootReqDuplicator, future, cause, initialAttempt);
                return null;
            });
            return;
        }
        if (returnedRes.isComplete()) {
            returnedRes.whenComplete().handle((result, cause) -> {
                final Throwable abortCause;
                if (cause != null) {
                    abortCause = cause;
                } else {
                    abortCause = AbortedStreamException.get();
                }
                handleException(ctx, rootReqDuplicator, future, abortCause, initialAttempt);
                return null;
            });
            return;
        }

        if (!setResponseTimeout(ctx)) {
            handleException(ctx, rootReqDuplicator, future, ResponseTimeoutException.get(), initialAttempt);
            return;
        }

        final HttpRequest duplicateReq;
        if (initialAttempt) {
            duplicateReq = rootReqDuplicator.duplicate();
        } else {
            final RequestHeadersBuilder newHeaders = originalReq.headers().toBuilder();
            newHeaders.setInt(ARMERIA_RETRY_COUNT, totalAttempts - 1);
            duplicateReq = rootReqDuplicator.duplicate(newHeaders.build());
        }

        final ClientRequestContext derivedCtx;
        try {
            derivedCtx = newDerivedContext(ctx, duplicateReq, ctx.rpcRequest(), initialAttempt);
        } catch (Throwable t) {
            handleException(ctx, rootReqDuplicator, future, t, initialAttempt);
            return;
        }

        final HttpRequest ctxReq = derivedCtx.request();
        assert ctxReq != null;
        final HttpResponse response;
        final ClientRequestContextExtension ctxExtension = derivedCtx.as(ClientRequestContextExtension.class);
        if (!initialAttempt && ctxExtension != null && derivedCtx.endpoint() == null) {
            // clear the pending throwable to retry endpoint selection
            ClientPendingThrowableUtil.removePendingThrowable(derivedCtx);
            // if the endpoint hasn't been selected, try to initialize the ctx with a new endpoint/event loop
            response = initContextAndExecuteWithFallback(
                    unwrap(), ctxExtension, HttpResponse::of,
                    (context, cause) -> HttpResponse.ofFailure(cause), ctxReq, false);
        } else {
            response = executeWithFallback(unwrap(), derivedCtx,
                                           (context, cause) -> HttpResponse.ofFailure(cause), ctxReq, false);
        }

        final RetryConfig<HttpResponse> config = mappedRetryConfig(ctx);
        if (!ctx.exchangeType().isResponseStreaming() || config.requiresResponseTrailers()) {
            response.aggregate().handle((aggregated, cause) -> {
                if (cause != null) {
                    derivedCtx.logBuilder().endRequest(cause);
                    derivedCtx.logBuilder().endResponse(cause);
                    handleResponseWithoutContent(config, ctx, rootReqDuplicator, originalReq, returnedRes,
                                                 future, derivedCtx, HttpResponse.ofFailure(cause), cause);
                } else {
                    completeLogIfBytesNotTransferred(aggregated, derivedCtx);
                    derivedCtx.log().whenAvailable(RequestLogProperty.RESPONSE_END_TIME).thenRun(() -> {
                        handleAggregatedResponse(config, ctx, rootReqDuplicator, originalReq, returnedRes,
                                                 future, derivedCtx, aggregated);
                    });
                }
                return null;
            });
        } else {
            handleStreamingResponse(config, ctx, rootReqDuplicator, originalReq, returnedRes,
                                    future, derivedCtx, response);
        }
    }
Creates a new instance that decorates the specified {@link HttpClient}.
core/src/main/java/com/linecorp/armeria/client/retry/RetryingClient.java