stoty commented on code in PR #577:
URL:
https://github.com/apache/httpcomponents-client/pull/577#discussion_r1814851771
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultAuthenticationStrategy.java:
##########
@@ -95,7 +99,7 @@ public List<AuthScheme> select(
Collection<String> authPrefs = challengeType == ChallengeType.TARGET ?
config.getTargetPreferredAuthSchemes() :
config.getProxyPreferredAuthSchemes();
if (authPrefs == null) {
- authPrefs = DEFAULT_SCHEME_PRIORITY;
+ authPrefs = getSchemePriority();
Review Comment:
This is a convenience change, to make it easier to re-add the SpnegoScheme
without having to fully duplicate this class.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthScheme2.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * ====================================================================
+ * 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.auth;
+
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * This is an improved version of the {@link AuthScheme} interface, amended to
be able to handle
+ * a conversation involving multiple challenge-response transactions and
adding the ability to check
+ * the results of a final token sent together with the successful HTTP request
as required by
+ * RFC 4559 and RFC 7546.
+ *
+ * @since 5.5
+ */
+public interface AuthScheme2 extends AuthScheme {
+
+ /**
+ * Processes the given auth challenge. Some authentication schemes may
involve multiple
+ * challenge-response exchanges. Such schemes must be able to maintain
internal state
+ * when dealing with sequential challenges.
+ *
+ * The {@link AuthScheme} interface implicitly assumes that that the
token passed here is
+ * simply stored in this method, and the actual authentication takes place
in
+ * {@link
org.apache.hc.client5.http.auth.AuthScheme#generateAuthResponse(HttpHost,
HttpRequest, HttpContext) generateAuthResponse }
+ * and/or {@link
org.apache.hc.client5.http.auth.AuthScheme#isResponseReady(HttpHost,
HttpRequest, HttpContext) generateAuthResponse },
+ * as only those methods receive the HttpHost, and only those can throw an
+ * AuthenticationException.
+ *
+ * This new methods signature makes it possible to process the token and
throw an
+ * AuthenticationException immediately even when no response is sent (i.e.
processing the mutual
+ * authentication response)
+ *
+ * When {@link isChallengeExpected} returns true, but no challenge was
sent, then this method must
+ * be called with a null {@link AuthChallenge} so that the Scheme can
handle this situation.
+ *
+ * @param host HTTP host
+ * @param authChallenge the auth challenge or null if no challenge was
received
+ * @param context HTTP context
+ * @param challenged true if the response was unauthorised (401/407)
+ * @throws AuthenticationException in case the authentication process is
unsuccessful.
+ * @since 5.5
+ */
+ void processChallenge(
Review Comment:
When processing a response with mutual a auth token, we don't send a new
request, hence we must be able to signal the authentication failiure to the
caller, and MalformedChallengeException is not semantically correct for that.
We also need additional information, the HttpContext, and the challenged
flag to be able to verify the received mutual auth token.
The old method did not verify anything, it just stored the token, an
deferred processing to the generateAuthResponse() method.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java:
##########
@@ -515,10 +517,11 @@ private boolean needAuthentication(
final AuthExchange proxyAuthExchange,
final HttpHost proxy,
final HttpResponse response,
- final HttpClientContext context) {
+ final HttpClientContext context) throws AuthenticationException,
MalformedChallengeException {
Review Comment:
This is corresponds to to the processChallenge() change in AuthScheme2, we
need to be able to signal the authentication failure to the caller even when
the server considers the authentication complete, and does not challenge us.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java:
##########
@@ -305,11 +307,12 @@ private boolean needAuthentication(
final HttpHost target,
final String pathPrefix,
final HttpResponse response,
- final HttpClientContext context) {
+ final HttpClientContext context) throws AuthenticationException,
MalformedChallengeException {
Review Comment:
This is corresponds to to the processChallenge() change in AuthScheme2, as
discussed above.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java:
##########
@@ -330,7 +334,7 @@ private boolean needAuthentication(
}
}
- if (targetAuthRequested) {
+ if (targetAuthRequested || targetMutualAuthRequired) {
Review Comment:
make sure we call the authenticatior if we expect a challenge, if the server
didn't send it.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java:
##########
@@ -305,11 +307,12 @@ private boolean needAuthentication(
final HttpHost target,
final String pathPrefix,
final HttpResponse response,
- final HttpClientContext context) {
+ final HttpClientContext context) throws AuthenticationException,
MalformedChallengeException {
final RequestConfig config = context.getRequestConfigOrDefault();
if (config.isAuthenticationEnabled()) {
final boolean targetAuthRequested = authenticator.isChallenged(
target, ChallengeType.TARGET, response,
targetAuthExchange, context);
+ final boolean targetMutualAuthRequired =
authenticator.isChallengeExpected(targetAuthExchange);
Review Comment:
The ..mutualAuthRequired variables are set if we still need a challenge with
the mutual auth response.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ final HttpHost host,
+ final ChallengeType challengeType,
+ final HttpResponse response,
+ final AuthenticationStrategy authStrategy,
+ final AuthExchange authExchange,
+ final HttpContext context) throws AuthenticationException,
MalformedChallengeException {
Review Comment:
Tha same pattern, we add the new exceptions because we need to evaluate the
challenge early, possibly even when there is no challenge, and throw an
exception if we didn't get the correct mutual auth token.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ final boolean isChallengeExpected = isChallengeExpected(authExchange);
Review Comment:
The same logic as in the ...Exec classes, only one level lower, we need to
know if the server MUST send a response with the mutual auth.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ 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);
}
- authExchange.reset();
- return false;
+ if (!isChallengeExpected) {
+ authExchange.reset();
+ return false;
+ }
}
switch (authExchange.getState()) {
case FAILURE:
return false;
case SUCCESS:
- authExchange.reset();
- break;
+ 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) {
+ if (challenge != null || isChallengeExpected) {
Review Comment:
Same old change, in mutual auth mode we must make sure to push down the
non-existent challenge to AuthScheme2, which is going to throw an exception.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ 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);
}
- authExchange.reset();
- return false;
+ if (!isChallengeExpected) {
+ authExchange.reset();
Review Comment:
Previously, if there was no challenge, then we could return here indicating
that the authentication process has finished.
However, if we have mutual auth enabled, but didn't get a challenge, then
the authentication is not finished, and must continue processing.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java:
##########
@@ -515,10 +517,11 @@ private boolean needAuthentication(
final AuthExchange proxyAuthExchange,
final HttpHost proxy,
final HttpResponse response,
- final HttpClientContext context) {
+ final HttpClientContext context) throws AuthenticationException,
MalformedChallengeException {
Review Comment:
I am bit hazy on Connection oriented Auth schemes, but I think that in
reality, there is no valid combination where a connection oriented scheme is
used with Spengo, so this is only about conforming to the interface change.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -135,37 +124,64 @@ public boolean isChallenged(
}
/**
- * Updates the {@link AuthExchange} state based on the challenge presented
in the response message
- * using the given {@link AuthenticationStrategy}.
+ * Determines whether the given response represents an authentication
challenge, without
+ * changing the AuthExchange state.
*
- * @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 authentication state has been updated,
- * {@code false} if unchanged.
+ * @return {@code true} if the response message represents an
authentication challenge,
+ * {@code false} otherwise.
*/
- public boolean updateAuthState(
- final HttpHost host,
- final ChallengeType challengeType,
- final HttpResponse response,
- final AuthenticationStrategy authStrategy,
- final AuthExchange authExchange,
- final HttpContext context) {
+ private boolean checkChallenged(final ChallengeType challengeType, final
HttpResponse response, final HttpContext context) {
+ final int challengeCode;
+ switch (challengeType) {
+ case TARGET:
+ challengeCode = HttpStatus.SC_UNAUTHORIZED;
+ break;
+ case PROXY:
+ challengeCode = HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
+ break;
+ default:
+ throw new IllegalStateException("Unexpected challenge type: "
+ challengeType);
+ }
- final HttpClientContext clientContext =
HttpClientContext.cast(context);
- final String exchangeId = clientContext.getExchangeId();
+ if (response.getCode() == challengeCode) {
+ if (LOG.isDebugEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
+ LOG.debug("{} Authentication required", exchangeId);
+ }
+ return true;
+ }
+ return false;
+ }
- if (LOG.isDebugEnabled()) {
- LOG.debug("{} {} requested authentication", exchangeId,
host.toHostString());
+ /**
+ * Determines if the scheme requires an auth challenge for responses that
do not
+ * have challenge HTTP code. (i.e whether it needs a mutual authentication
token)
+ *
+ * @param authExchange
+ * @return true is authExchange's scheme is AuthScheme2, which currently
expects
+ * a WWW-Authenticate header even for authorized HTTP responses
+ */
+ public boolean isChallengeExpected(final AuthExchange authExchange) {
+ final AuthScheme authScheme = authExchange.getAuthScheme();
+ if (authScheme != null && authScheme instanceof AuthScheme2) {
+ return ((AuthScheme2)authScheme).isChallengeExpected();
+ } else {
+ return false;
}
+ }
- final Header[] headers = response.getHeaders(
- challengeType == ChallengeType.PROXY ?
HttpHeaders.PROXY_AUTHENTICATE : HttpHeaders.WWW_AUTHENTICATE);
+ public Map<String, AuthChallenge> extractChallengeMap(final ChallengeType
challengeType,
Review Comment:
This is just factored out from updateAuthState for readability, it can be
reverted if needed.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/auth/KerberosConfig.java:
##########
@@ -151,11 +159,17 @@ public Builder setRequestDelegCreds(final Option
requestDelegCreds) {
return this;
}
+ public Builder setRequestMutualAuth(final Option requestMutualAuth) {
Review Comment:
This the new option to enable mutual auth.
Michal suggested changing this API, which is certainly possible, especially
now, that we have duplicated the Scheme objects, and require changes in callers
anyway.
However, this configuration API is indenpendent of the deeper changes.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthScheme2.java:
##########
@@ -0,0 +1,102 @@
+/*
+ * ====================================================================
+ * 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.auth;
+
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * This is an improved version of the {@link AuthScheme} interface, amended to
be able to handle
+ * a conversation involving multiple challenge-response transactions and
adding the ability to check
+ * the results of a final token sent together with the successful HTTP request
as required by
+ * RFC 4559 and RFC 7546.
+ *
+ * @since 5.5
+ */
+public interface AuthScheme2 extends AuthScheme {
+
+ /**
+ * Processes the given auth challenge. Some authentication schemes may
involve multiple
+ * challenge-response exchanges. Such schemes must be able to maintain
internal state
+ * when dealing with sequential challenges.
+ *
+ * The {@link AuthScheme} interface implicitly assumes that that the
token passed here is
+ * simply stored in this method, and the actual authentication takes place
in
+ * {@link
org.apache.hc.client5.http.auth.AuthScheme#generateAuthResponse(HttpHost,
HttpRequest, HttpContext) generateAuthResponse }
+ * and/or {@link
org.apache.hc.client5.http.auth.AuthScheme#isResponseReady(HttpHost,
HttpRequest, HttpContext) generateAuthResponse },
+ * as only those methods receive the HttpHost, and only those can throw an
+ * AuthenticationException.
+ *
+ * This new methods signature makes it possible to process the token and
throw an
+ * AuthenticationException immediately even when no response is sent (i.e.
processing the mutual
+ * authentication response)
+ *
+ * When {@link isChallengeExpected} returns true, but no challenge was
sent, then this method must
+ * be called with a null {@link AuthChallenge} so that the Scheme can
handle this situation.
+ *
+ * @param host HTTP host
+ * @param authChallenge the auth challenge or null if no challenge was
received
+ * @param context HTTP context
+ * @param challenged true if the response was unauthorised (401/407)
+ * @throws AuthenticationException in case the authentication process is
unsuccessful.
+ * @since 5.5
+ */
+ void processChallenge(
+ HttpHost host,
+ AuthChallenge authChallenge,
+ HttpContext context,
+ boolean challenged) throws AuthenticationException;
+
+ /**
+ * The old processChallenge signature is unfit for use in AuthScheme2.
+ * If the old signature is sufficient for a scheme, then it should
implement {@link AuthScheme}
+ * instead AuthScheme2.
+ */
+ @Override
+ default void processChallenge(
+ AuthChallenge authChallenge,
+ HttpContext context) throws MalformedChallengeException {
+ throw new UnsupportedOperationException("on AuthScheme2
implementations only the four "
+ + "argument processChallenge method can be called");
+ }
+
+ /**
+ * Indicates that the even authorized (i.e. not 401 or 407) responses must
be processed
+ * by this Scheme.
+ *
+ * The original AuthScheme interface only processes unauthorised responses.
+ * This method indicates that non unauthorised responses are expected to
contain challenges
+ * and must be processed by the Scheme.
+ * This is required to implement the SPENGO RFC and Kerberos mutual
authentication.
+ *
+ * @return true if responses with non 401/407 response codes must be
processed by the scheme.
+ * @since 5.5
+ */
+ boolean isChallengeExpected();
Review Comment:
This is the other half of the mutual auth handling.
If mutual auth is requested, then the server MUST send a challenge with the
mutual auth token (which is not really a "challenge", as a response is not
expected).
If this returns true, but the server does not send a challenge, then mutual
authentication failed, and an Exception is thrown to the user.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java:
##########
@@ -528,7 +531,7 @@ private boolean needAuthentication(
}
}
- if (proxyAuthRequested) {
+ if (proxyAuthRequested || proxyMutualAuthRequired) {
Review Comment:
We must call the authenticator if we except a mutual auth response.
Again, I don't think this can happen in this specific class.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ 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);
}
- authExchange.reset();
- return false;
+ if (!isChallengeExpected) {
+ authExchange.reset();
+ return false;
+ }
}
switch (authExchange.getState()) {
case FAILURE:
return false;
case SUCCESS:
- authExchange.reset();
- break;
+ 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) {
+ if (challenge != null || isChallengeExpected) {
if (LOG.isDebugEnabled()) {
- LOG.debug("{} Authorization challenge processed",
exchangeId);
+ LOG.debug("{} Processing authorization challenge
{}", exchangeId, challenge);
}
try {
- authScheme.processChallenge(challenge, context);
- } catch (final MalformedChallengeException ex) {
+ if (authScheme instanceof AuthScheme2) {
+
((AuthScheme2)authScheme).processChallenge(host, challenge, context,
challenged);
Review Comment:
Extra info for AuthScheme2
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -97,32 +101,17 @@ public boolean isChallenged(
final HttpResponse response,
final AuthExchange authExchange,
final HttpContext context) {
- final int challengeCode;
- switch (challengeType) {
- case TARGET:
- challengeCode = HttpStatus.SC_UNAUTHORIZED;
- break;
- case PROXY:
- challengeCode = HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
- break;
- default:
- throw new IllegalStateException("Unexpected challenge type: "
+ challengeType);
- }
-
- final HttpClientContext clientContext =
HttpClientContext.cast(context);
- final String exchangeId = clientContext.getExchangeId();
-
- if (response.getCode() == challengeCode) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("{} Authentication required", exchangeId);
- }
+ if (checkChallenged(challengeType, response, context)) {
return true;
}
switch (authExchange.getState()) {
case CHALLENGED:
case HANDSHAKE:
if (LOG.isDebugEnabled()) {
- LOG.debug("{} Authentication succeeded", exchangeId);
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
Review Comment:
Getting exchangeId lazily for the debug message, because of the refactor
above.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -135,37 +124,64 @@ public boolean isChallenged(
}
/**
- * Updates the {@link AuthExchange} state based on the challenge presented
in the response message
- * using the given {@link AuthenticationStrategy}.
+ * Determines whether the given response represents an authentication
challenge, without
+ * changing the AuthExchange state.
*
- * @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 authentication state has been updated,
- * {@code false} if unchanged.
+ * @return {@code true} if the response message represents an
authentication challenge,
+ * {@code false} otherwise.
*/
- public boolean updateAuthState(
- final HttpHost host,
- final ChallengeType challengeType,
- final HttpResponse response,
- final AuthenticationStrategy authStrategy,
- final AuthExchange authExchange,
- final HttpContext context) {
+ private boolean checkChallenged(final ChallengeType challengeType, final
HttpResponse response, final HttpContext context) {
+ final int challengeCode;
+ switch (challengeType) {
+ case TARGET:
+ challengeCode = HttpStatus.SC_UNAUTHORIZED;
+ break;
+ case PROXY:
+ challengeCode = HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
+ break;
+ default:
+ throw new IllegalStateException("Unexpected challenge type: "
+ challengeType);
+ }
- final HttpClientContext clientContext =
HttpClientContext.cast(context);
- final String exchangeId = clientContext.getExchangeId();
+ if (response.getCode() == challengeCode) {
+ if (LOG.isDebugEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
+ LOG.debug("{} Authentication required", exchangeId);
+ }
+ return true;
+ }
+ return false;
+ }
- if (LOG.isDebugEnabled()) {
- LOG.debug("{} {} requested authentication", exchangeId,
host.toHostString());
+ /**
+ * Determines if the scheme requires an auth challenge for responses that
do not
+ * have challenge HTTP code. (i.e whether it needs a mutual authentication
token)
+ *
+ * @param authExchange
+ * @return true is authExchange's scheme is AuthScheme2, which currently
expects
+ * a WWW-Authenticate header even for authorized HTTP responses
+ */
+ public boolean isChallengeExpected(final AuthExchange authExchange) {
Review Comment:
This is glue code so that both AuthScheme and AuthScheme2 can be handled,
always return false for AuthScheme, which does not support mutual auth.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -97,32 +101,17 @@ public boolean isChallenged(
final HttpResponse response,
final AuthExchange authExchange,
final HttpContext context) {
- final int challengeCode;
- switch (challengeType) {
- case TARGET:
- challengeCode = HttpStatus.SC_UNAUTHORIZED;
- break;
- case PROXY:
- challengeCode = HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED;
- break;
- default:
- throw new IllegalStateException("Unexpected challenge type: "
+ challengeType);
- }
-
- final HttpClientContext clientContext =
HttpClientContext.cast(context);
- final String exchangeId = clientContext.getExchangeId();
-
- if (response.getCode() == challengeCode) {
- if (LOG.isDebugEnabled()) {
- LOG.debug("{} Authentication required", exchangeId);
- }
+ if (checkChallenged(challengeType, response, context)) {
Review Comment:
same code factored out to the new checkChallenged method, no change in
operation.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -135,37 +124,64 @@ public boolean isChallenged(
}
/**
- * Updates the {@link AuthExchange} state based on the challenge presented
in the response message
- * using the given {@link AuthenticationStrategy}.
+ * Determines whether the given response represents an authentication
challenge, without
+ * changing the AuthExchange state.
*
- * @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 authentication state has been updated,
- * {@code false} if unchanged.
+ * @return {@code true} if the response message represents an
authentication challenge,
+ * {@code false} otherwise.
*/
- public boolean updateAuthState(
- final HttpHost host,
- final ChallengeType challengeType,
- final HttpResponse response,
- final AuthenticationStrategy authStrategy,
- final AuthExchange authExchange,
- final HttpContext context) {
+ private boolean checkChallenged(final ChallengeType challengeType, final
HttpResponse response, final HttpContext context) {
Review Comment:
This is factored out from isChallenged, but does the same.
We need a way to check if we are challenged, without doing the state updates
that isChallenged() does, this is the non state-changing check part.
We call this from isChallenged(), which originally included this code, and
also from updateAuthState, where we need to determine if the server sent a
challenged, but do not want to change the Auth state.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
Review Comment:
Did the server send a challenge ?
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ 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);
}
- authExchange.reset();
- return false;
+ if (!isChallengeExpected) {
+ authExchange.reset();
+ return false;
+ }
}
switch (authExchange.getState()) {
case FAILURE:
return false;
case SUCCESS:
- authExchange.reset();
- break;
+ if (!isChallengeExpected) {
Review Comment:
AuthChallenge.SUCCESS only indicates that the server hasn't sent a response.
If the server should have sent a response, then we must continue processing.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ 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);
}
- authExchange.reset();
- return false;
+ if (!isChallengeExpected) {
+ authExchange.reset();
+ return false;
+ }
}
switch (authExchange.getState()) {
case FAILURE:
return false;
case SUCCESS:
- authExchange.reset();
- break;
+ 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) {
+ if (challenge != null || isChallengeExpected) {
if (LOG.isDebugEnabled()) {
- LOG.debug("{} Authorization challenge processed",
exchangeId);
+ LOG.debug("{} Processing authorization challenge
{}", exchangeId, challenge);
}
try {
- authScheme.processChallenge(challenge, context);
- } catch (final MalformedChallengeException ex) {
+ if (authScheme instanceof AuthScheme2) {
+
((AuthScheme2)authScheme).processChallenge(host, challenge, context,
challenged);
+ } else {
+ authScheme.processChallenge(challenge,
context);
+ }
+ } catch (final AuthenticationException |
MalformedChallengeException ex) {
if (LOG.isWarnEnabled()) {
- LOG.warn("{} {}", exchangeId, ex.getMessage());
+ LOG.warn("Exception processing Challange {}",
exchangeId, ex);
}
authExchange.reset();
authExchange.setState(AuthExchange.State.FAILURE);
- return false;
+ if (isChallengeExpected) {
+ throw ex;
Review Comment:
Mutual auth case, throw exception, because the server has sent a 200 or
equvalent code, and we will simply return the response to the client otherwise,
even if the AuthExchange.STATE is Failure.
##########
httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java:
##########
@@ -186,52 +202,109 @@ public boolean updateAuthState(
authChallenges = parser.parse(challengeType, buffer, cursor);
} catch (final ParseException ex) {
if (LOG.isWarnEnabled()) {
+ final HttpClientContext clientContext =
HttpClientContext.cast(context);
+ final String exchangeId = clientContext.getExchangeId();
LOG.warn("{} Malformed challenge: {}", exchangeId,
header.getValue());
}
continue;
}
- for (final AuthChallenge authChallenge: authChallenges) {
+ for (final AuthChallenge authChallenge : authChallenges) {
final String schemeName =
authChallenge.getSchemeName().toLowerCase(Locale.ROOT);
if (!challengeMap.containsKey(schemeName)) {
challengeMap.put(schemeName, authChallenge);
}
}
}
+ return challengeMap;
+ }
+
+ /**
+ * 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.
+ */
+ public boolean updateAuthState(
+ 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,
context);
+ final boolean isChallengeExpected = isChallengeExpected(authExchange);
Review Comment:
For old AuthChallenge schemes, this is always false.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]