in httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/AuthenticationHandler.java [231:363]
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;
}