public boolean handleResponse(
            final HttpHost host,
            final ChallengeType challengeType,
            final HttpResponse response,
            final AuthenticationStrategy authStrategy,
            final AuthExchange authExchange,
            final HttpContext context) throws AuthenticationException, MalformedChallengeException {

    final HttpClientContext clientContext = HttpClientContext.cast(context);
        final String exchangeId = clientContext.getExchangeId();
        final boolean challenged = checkChallenged(challengeType, response, clientContext);
        final boolean isChallengeExpected = isChallengeExpected(authExchange);

        if (LOG.isDebugEnabled()) {
            LOG.debug("{} {} requested authentication", exchangeId, host.toHostString());
        }

        final Map<String, AuthChallenge> challengeMap = extractChallengeMap(challengeType, response, clientContext);

        if (challengeMap.isEmpty()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} Response contains no valid authentication challenges", exchangeId);
            }
            if (!isChallengeExpected) {
                authExchange.reset();
                return false;
            }
        }

        switch (authExchange.getState()) {
            case FAILURE:
                return false;
            case SUCCESS:
                if (!isChallengeExpected) {
                    authExchange.reset();
                    break;
                }
                // otherwise fall through
            case CHALLENGED:
                // fall through
            case HANDSHAKE:
                Asserts.notNull(authExchange.getAuthScheme(), "AuthScheme");
                // fall through
            case UNCHALLENGED:
                final AuthScheme authScheme = authExchange.getAuthScheme();
                // AuthScheme is only set if we have already sent an auth response, either
                // because we have received a challenge for it, or preemptively.
                if (authScheme != null) {
                    final String schemeName = authScheme.getName();
                    final AuthChallenge challenge = challengeMap.get(schemeName.toLowerCase(Locale.ROOT));
                    if (challenge != null || isChallengeExpected) {
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("{} Processing authentication challenge {}", exchangeId, challenge);
                        }
                        try {
                            authScheme.processChallenge(host, challenged, challenge, clientContext);
                        } catch (final AuthenticationException | MalformedChallengeException ex) {
                            if (LOG.isWarnEnabled()) {
                                LOG.warn("Exception processing challenge {}", exchangeId, ex);
                            }
                            authExchange.reset();
                            authExchange.setState(AuthExchange.State.FAILURE);
                            if (isChallengeExpected) {
                                throw ex;
                            }
                        }
                        if (authScheme.isChallengeComplete()) {
                            if (LOG.isDebugEnabled()) {
                                LOG.debug("{} Authentication failed", exchangeId);
                            }
                            authExchange.reset();
                            authExchange.setState(AuthExchange.State.FAILURE);
                            return false;
                        }
                        if (!challenged) {
                            // There are no more challanges sent after the 200 message,
                            // and if we get here, then the mutual auth phase has succeeded.
                            authExchange.setState(AuthExchange.State.SUCCESS);
                            return false;
                        } else {
                            authExchange.setState(AuthExchange.State.HANDSHAKE);
                        }
                        return true;
                    }
                    authExchange.reset();
                    // Retry authentication with a different scheme
                }
        }

        // We reach this if we fell through above because the authScheme has not yet been set, or if
        // we receive a 401/407 response for an unexpected scheme. Normally this processes the first
        // 401/407 response
        final List<AuthScheme> preferredSchemes = authStrategy.select(challengeType, challengeMap, clientContext);
        final CredentialsProvider credsProvider = clientContext.getCredentialsProvider();
        if (credsProvider == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} Credentials provider not set in the context", exchangeId);
            }
            return false;
        }

        final Queue<AuthScheme> authOptions = new LinkedList<>();
        if (LOG.isDebugEnabled()) {
            LOG.debug("{} Selecting authentication options", exchangeId);
        }
        for (final AuthScheme authScheme: preferredSchemes) {
            // We only respond to the first successfully processed challenge. However, the
            // original AuthScheme API does not really process the challenge at this point,
            // so we need to process/store each challenge here anyway.
            try {
                final String schemeName = authScheme.getName();
                final AuthChallenge challenge = challengeMap.get(schemeName.toLowerCase(Locale.ROOT));
                authScheme.processChallenge(host, challenged, challenge, clientContext);
                if (authScheme.isResponseReady(host, credsProvider, clientContext)) {
                    authOptions.add(authScheme);
                }
            } catch (final AuthenticationException | MalformedChallengeException ex) {
                if (LOG.isWarnEnabled()) {
                    LOG.warn("Exception while processing Challange", ex);
                }
            }
        }
        if (!authOptions.isEmpty()) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} Selected authentication options: {}", exchangeId, authOptions);
            }
            authExchange.reset();
            authExchange.setState(AuthExchange.State.CHALLENGED);
            authExchange.setOptions(authOptions);
            return true;
        }
        return false;
    }
Handles the response from the opposite endpoint and updates the {@link AuthExchange}
state based on the challenge presented in the response message using the given
{@link AuthenticationStrategy}.

@param host the hostname of the opposite endpoint.
@param challengeType the challenge type (target or proxy).
@param response the response message head.
@param authStrategy the authentication strategy.
@param authExchange the current authentication exchange state.
@param context the current execution context.
@return {@code true} if the request needs-to be re-sent,
  {@code false} if the authentication is complete (successful or not).

@throws AuthenticationException if the AuthScheme throws one. In most cases this indicates a
client side problem, as final server error responses are simply returned.
@throws MalformedChallengeException if the AuthScheme throws one. In most cases this indicates a
client side problem, as final server error responses are simply returned.
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/AuthenticationHandler.java