diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthOption.java b/httpclient/src/main/java/org/apache/http/auth/AuthOption.java new file mode 100644 index 000000000..97c0c58b4 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/auth/AuthOption.java @@ -0,0 +1,66 @@ +/* + * ==================================================================== + * + * 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 + * . + * + */ + +package org.apache.http.auth; + +import org.apache.http.annotation.Immutable; + +/** + * @since 4.2 + */ +@Immutable +public final class AuthOption { + + private final AuthScheme authScheme; + private final Credentials creds; + + public AuthOption(final AuthScheme authScheme, final Credentials creds) { + super(); + if (authScheme == null) { + throw new IllegalArgumentException("Auth scheme may not be null"); + } + if (creds == null) { + throw new IllegalArgumentException("User credentials may not be null"); + } + this.authScheme = authScheme; + this.creds = creds; + } + + public AuthScheme getAuthScheme() { + return this.authScheme; + } + + public Credentials getCredentials() { + return this.creds; + } + + @Override + public String toString() { + return this.authScheme.toString(); + } + +} + diff --git a/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java index 2d0da4217..afce2838c 100644 --- a/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationHandler.java @@ -47,7 +47,10 @@ import org.apache.http.protocol.HttpContext; * from multiple threads. * * @since 4.0 + * + * @deprecated use {@link AuthenticationStrategy} */ +@Deprecated public interface AuthenticationHandler { /** diff --git a/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java new file mode 100644 index 000000000..e43bbc43d --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java @@ -0,0 +1,97 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.client; + +import java.util.Map; + +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.auth.AuthOption; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.protocol.HttpContext; + +/** +/** + * A handler for determining if an HTTP response represents an authentication challenge that was + * sent back to the client as a result of authentication failure. + *

+ * Implementations of this interface must be thread-safe. Access to shared data must be + * synchronized as methods of this interface may be executed from multiple threads. + * + * @since 4.2 + */ +public interface AuthenticationStrategy { + + /** + * Determines if the given HTTP response response represents + * an authentication challenge that was sent back as a result + * of authentication failure + * @param response HTTP response. + * @param context HTTP context. + * @return true if user authentication is required, + * false otherwise. + */ + boolean isAuthenticationRequested( + HttpResponse response, + HttpContext context); + + /** + * Extracts from the given HTTP response a collection of authentication + * challenges, each of which represents an authentication scheme supported + * by the authentication host. + * + * @param response HTTP response. + * @param context HTTP context. + * @return a collection of challenges keyed by names of corresponding + * authentication schemes. + * @throws MalformedChallengeException if one of the authentication + * challenges is not valid or malformed. + */ + Map getChallenges( + HttpResponse response, + HttpContext context) throws MalformedChallengeException; + + /** + * Selects one authentication challenge out of all available and + * creates and generates {@link AuthOption} instance capable of + * processing that challenge. + * @param challenges collection of challenges. + * @param response HTTP response. + * @param context HTTP context. + * @return authentication scheme to use for authentication. + * @throws MalformedChallengeException if one of the authentication + * challenges is not valid or malformed. + */ + AuthOption select( + Map challenges, + HttpHost authhost, + HttpResponse response, + HttpContext context) throws MalformedChallengeException; + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java index a347ef5d3..038fdbeb8 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AbstractAuthenticationHandler.java @@ -46,6 +46,7 @@ import org.apache.http.auth.AuthSchemeRegistry; import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.client.AuthenticationHandler; +import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.params.AuthPolicy; import org.apache.http.client.protocol.ClientContext; import org.apache.http.protocol.HTTP; @@ -56,7 +57,10 @@ import org.apache.http.util.CharArrayBuffer; * Base class for {@link AuthenticationHandler} implementations. * * @since 4.0 + * + * @deprecated use {@link AuthenticationStrategy} */ +@Deprecated @Immutable public abstract class AbstractAuthenticationHandler implements AuthenticationHandler { diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java new file mode 100644 index 000000000..99cc3a2fa --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java @@ -0,0 +1,122 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.client; + +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AuthOption; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.client.AuthenticationHandler; +import org.apache.http.client.AuthenticationStrategy; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.protocol.ClientContext; +import org.apache.http.protocol.HttpContext; + +/** + * @since 4.2 + */ +@SuppressWarnings("deprecation") +@Immutable +@Deprecated +class AuthenticationStrategyAdaptor implements AuthenticationStrategy { + + private final Log log = LogFactory.getLog(getClass()); + + private final AuthenticationHandler handler; + + @Deprecated + public AuthenticationStrategyAdaptor(final AuthenticationHandler handler) { + super(); + this.handler = handler; + } + + public boolean isAuthenticationRequested(final HttpResponse response, final HttpContext context) { + return this.handler.isAuthenticationRequested(response, context); + } + + public Map getChallenges( + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + return this.handler.getChallenges(response, context); + } + + public AuthOption select( + final Map challenges, + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( + ClientContext.CREDS_PROVIDER); + if (credsProvider == null) { + this.log.debug("Credentials provider not set in the context"); + return null; + } + + AuthScheme authScheme; + try { + authScheme = this.handler.selectScheme(challenges, response, context); + } catch (AuthenticationException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn(ex.getMessage(), ex); + } + return null; + } + String id = authScheme.getSchemeName(); + Header challenge = challenges.get(id.toLowerCase(Locale.US)); + authScheme.processChallenge(challenge); + + AuthScope authScope = new AuthScope( + authhost.getHostName(), + authhost.getPort(), + authScheme.getRealm(), + authScheme.getSchemeName()); + + Credentials credentials = credsProvider.getCredentials(authScope); + if (credentials != null) { + return new AuthOption(authScheme, credentials); + } else { + return null; + } + } + + public AuthenticationHandler getHandler() { + return this.handler; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java new file mode 100644 index 000000000..2c4df0fa2 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java @@ -0,0 +1,200 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.client; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.FormattedHeader; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.HttpResponse; +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AuthOption; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.AuthSchemeRegistry; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.client.AuthenticationStrategy; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.params.AuthPolicy; +import org.apache.http.client.protocol.ClientContext; +import org.apache.http.protocol.HTTP; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.CharArrayBuffer; + +@Immutable +class AuthenticationStrategyImpl implements AuthenticationStrategy { + + private final Log log = LogFactory.getLog(getClass()); + + private static final List DEFAULT_SCHEME_PRIORITY = + Collections.unmodifiableList(Arrays.asList(new String[] { + AuthPolicy.SPNEGO, + AuthPolicy.NTLM, + AuthPolicy.DIGEST, + AuthPolicy.BASIC + })); + + private final int challengeCode; + private final String headerName; + private final String prefParamName; + + AuthenticationStrategyImpl(int challengeCode, final String headerName, final String prefParamName) { + super(); + this.challengeCode = challengeCode; + this.headerName = headerName; + this.prefParamName = prefParamName; + } + + public boolean isAuthenticationRequested(final HttpResponse response, final HttpContext context) { + if (response == null) { + throw new IllegalArgumentException("HTTP response may not be null"); + } + int status = response.getStatusLine().getStatusCode(); + return status == this.challengeCode; + } + + public Map getChallenges( + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + if (response == null) { + throw new IllegalArgumentException("HTTP response may not be null"); + } + Header[] headers = response.getHeaders(this.headerName); + Map map = new HashMap(headers.length); + for (Header header : headers) { + CharArrayBuffer buffer; + int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + String s = header.getValue(); + if (s == null) { + throw new MalformedChallengeException("Header value is null"); + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + int beginIndex = pos; + while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { + pos++; + } + int endIndex = pos; + String s = buffer.substring(beginIndex, endIndex); + map.put(s.toLowerCase(Locale.US), header); + } + return map; + } + + public AuthOption select( + final Map challenges, + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) throws MalformedChallengeException { + if (challenges == null) { + throw new IllegalArgumentException("Map of auth challenges may not be null"); + } + if (authhost == null) { + throw new IllegalArgumentException("Host may not be null"); + } + if (response == null) { + throw new IllegalArgumentException("HTTP response may not be null"); + } + if (context == null) { + throw new IllegalArgumentException("HTTP context may not be null"); + } + + AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute( + ClientContext.AUTHSCHEME_REGISTRY); + if (registry == null) { + this.log.debug("Auth scheme registry not set in the context"); + return null; + } + CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( + ClientContext.CREDS_PROVIDER); + if (credsProvider == null) { + this.log.debug("Credentials provider not set in the context"); + return null; + } + + @SuppressWarnings("unchecked") + List authPrefs = (List) response.getParams().getParameter(this.prefParamName); + if (authPrefs == null) { + authPrefs = DEFAULT_SCHEME_PRIORITY; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Authentication schemes in the order of preference: " + authPrefs); + } + + for (String id: authPrefs) { + Header challenge = challenges.get(id.toLowerCase(Locale.US)); + if (challenge != null) { + try { + AuthScheme authScheme = registry.getAuthScheme(id, response.getParams()); + authScheme.processChallenge(challenge); + + AuthScope authScope = new AuthScope( + authhost.getHostName(), + authhost.getPort(), + authScheme.getRealm(), + authScheme.getSchemeName()); + + Credentials credentials = credsProvider.getCredentials(authScope); + if (credentials != null) { + return new AuthOption(authScheme, credentials); + } + } catch (IllegalStateException e) { + if (this.log.isWarnEnabled()) { + this.log.warn("Authentication scheme " + id + " not supported"); + // Try again + } + } + } else { + if (this.log.isDebugEnabled()) { + this.log.debug("Challenge for " + id + " authentication scheme not available"); + // Try again + } + } + } + return null; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java index e3a96d189..72f191ad6 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultProxyAuthenticationHandler.java @@ -46,7 +46,10 @@ import org.apache.http.protocol.HttpContext; * authentication. * * @since 4.0 + * + * @deprecated use {@link ProxyAuthenticationStrategy} */ +@Deprecated @Immutable public class DefaultProxyAuthenticationHandler extends AbstractAuthenticationHandler { diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java index a4da7fd43..f7254e8ba 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRedirectStrategyAdaptor.java @@ -33,6 +33,7 @@ import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.ProtocolException; import org.apache.http.annotation.Immutable; +import org.apache.http.client.RedirectHandler; import org.apache.http.client.RedirectStrategy; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpHead; @@ -42,14 +43,15 @@ import org.apache.http.protocol.HttpContext; /** * @since 4.1 */ +@SuppressWarnings("deprecation") @Immutable @Deprecated class DefaultRedirectStrategyAdaptor implements RedirectStrategy { - private final org.apache.http.client.RedirectHandler handler; + private final RedirectHandler handler; @Deprecated - public DefaultRedirectStrategyAdaptor(final org.apache.http.client.RedirectHandler handler) { + public DefaultRedirectStrategyAdaptor(final RedirectHandler handler) { super(); this.handler = handler; } @@ -74,4 +76,8 @@ class DefaultRedirectStrategyAdaptor implements RedirectStrategy { } } + public RedirectHandler getHandler() { + return this.handler; + } + } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java index 4e9104bad..2ace5b4d8 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultRequestDirector.java @@ -49,10 +49,11 @@ import org.apache.http.auth.AuthChallengeState; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthState; import org.apache.http.client.AuthenticationHandler; -import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.RedirectException; +import org.apache.http.client.RedirectHandler; import org.apache.http.client.RedirectStrategy; import org.apache.http.client.RequestDirector; import org.apache.http.client.UserTokenHandler; @@ -124,6 +125,7 @@ import org.apache.http.util.EntityUtils; * * @since 4.0 */ +@SuppressWarnings("deprecation") @NotThreadSafe // e.g. managedConn public class DefaultRequestDirector implements RequestDirector { @@ -152,17 +154,25 @@ public class DefaultRequestDirector implements RequestDirector { /** The redirect handler. */ @Deprecated - protected final org.apache.http.client.RedirectHandler redirectHandler = null; + protected final RedirectHandler redirectHandler; /** The redirect strategy. */ protected final RedirectStrategy redirectStrategy; /** The target authentication handler. */ + @Deprecated protected final AuthenticationHandler targetAuthHandler; + /** The target authentication handler. */ + protected final AuthenticationStrategy targetAuthStrategy; + /** The proxy authentication handler. */ + @Deprecated protected final AuthenticationHandler proxyAuthHandler; + /** The proxy authentication handler. */ + protected final AuthenticationStrategy proxyAuthStrategy; + /** The user token handler. */ protected final UserTokenHandler userTokenHandler; @@ -195,7 +205,7 @@ public class DefaultRequestDirector implements RequestDirector { final HttpRoutePlanner rouplan, final HttpProcessor httpProcessor, final HttpRequestRetryHandler retryHandler, - final org.apache.http.client.RedirectHandler redirectHandler, + final RedirectHandler redirectHandler, final AuthenticationHandler targetAuthHandler, final AuthenticationHandler proxyAuthHandler, final UserTokenHandler userTokenHandler, @@ -203,13 +213,14 @@ public class DefaultRequestDirector implements RequestDirector { this(LogFactory.getLog(DefaultRequestDirector.class), requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler, new DefaultRedirectStrategyAdaptor(redirectHandler), - targetAuthHandler, proxyAuthHandler, userTokenHandler, params); + new AuthenticationStrategyAdaptor(targetAuthHandler), + new AuthenticationStrategyAdaptor(proxyAuthHandler), + userTokenHandler, + params); } - /** - * @since 4.1 - */ + @Deprecated public DefaultRequestDirector( final Log log, final HttpRequestExecutor requestExec, @@ -224,6 +235,32 @@ public class DefaultRequestDirector implements RequestDirector { final AuthenticationHandler proxyAuthHandler, final UserTokenHandler userTokenHandler, final HttpParams params) { + this(LogFactory.getLog(DefaultRequestDirector.class), + requestExec, conman, reustrat, kastrat, rouplan, httpProcessor, retryHandler, + redirectStrategy, + new AuthenticationStrategyAdaptor(targetAuthHandler), + new AuthenticationStrategyAdaptor(proxyAuthHandler), + userTokenHandler, + params); + } + + /** + * @since 4.2 + */ + public DefaultRequestDirector( + final Log log, + final HttpRequestExecutor requestExec, + final ClientConnectionManager conman, + final ConnectionReuseStrategy reustrat, + final ConnectionKeepAliveStrategy kastrat, + final HttpRoutePlanner rouplan, + final HttpProcessor httpProcessor, + final HttpRequestRetryHandler retryHandler, + final RedirectStrategy redirectStrategy, + final AuthenticationStrategy targetAuthStrategy, + final AuthenticationStrategy proxyAuthStrategy, + final UserTokenHandler userTokenHandler, + final HttpParams params) { if (log == null) { throw new IllegalArgumentException @@ -261,13 +298,13 @@ public class DefaultRequestDirector implements RequestDirector { throw new IllegalArgumentException ("Redirect strategy may not be null."); } - if (targetAuthHandler == null) { + if (targetAuthStrategy == null) { throw new IllegalArgumentException - ("Target authentication handler may not be null."); + ("Target authentication strategy may not be null."); } - if (proxyAuthHandler == null) { + if (proxyAuthStrategy == null) { throw new IllegalArgumentException - ("Proxy authentication handler may not be null."); + ("Proxy authentication strategy may not be null."); } if (userTokenHandler == null) { throw new IllegalArgumentException @@ -279,27 +316,43 @@ public class DefaultRequestDirector implements RequestDirector { } this.log = log; this.authenticator = new HttpAuthenticator(log); - this.requestExec = requestExec; - this.connManager = conman; - this.reuseStrategy = reustrat; - this.keepAliveStrategy = kastrat; - this.routePlanner = rouplan; - this.httpProcessor = httpProcessor; - this.retryHandler = retryHandler; - this.redirectStrategy = redirectStrategy; - this.targetAuthHandler = targetAuthHandler; - this.proxyAuthHandler = proxyAuthHandler; - this.userTokenHandler = userTokenHandler; - this.params = params; + this.requestExec = requestExec; + this.connManager = conman; + this.reuseStrategy = reustrat; + this.keepAliveStrategy = kastrat; + this.routePlanner = rouplan; + this.httpProcessor = httpProcessor; + this.retryHandler = retryHandler; + this.redirectStrategy = redirectStrategy; + this.targetAuthStrategy = targetAuthStrategy; + this.proxyAuthStrategy = proxyAuthStrategy; + this.userTokenHandler = userTokenHandler; + this.params = params; - this.managedConn = null; + if (redirectStrategy instanceof DefaultRedirectStrategyAdaptor) { + this.redirectHandler = ((DefaultRedirectStrategyAdaptor) redirectStrategy).getHandler(); + } else { + this.redirectHandler = null; + } + if (targetAuthStrategy instanceof AuthenticationStrategyAdaptor) { + this.targetAuthHandler = ((AuthenticationStrategyAdaptor) targetAuthStrategy).getHandler(); + } else { + this.targetAuthHandler = null; + } + if (proxyAuthStrategy instanceof AuthenticationStrategyAdaptor) { + this.proxyAuthHandler = ((AuthenticationStrategyAdaptor) proxyAuthStrategy).getHandler(); + } else { + this.proxyAuthHandler = null; + } + + this.managedConn = null; this.execCount = 0; this.redirectCount = 0; this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100); this.targetAuthState = new AuthState(); this.proxyAuthState = new AuthState(); - } // constructor + } private RequestWrapper wrapRequest( @@ -842,16 +895,11 @@ public class DefaultRequestDirector implements RequestDirector { response.getStatusLine()); } - CredentialsProvider credsProvider = (CredentialsProvider) - context.getAttribute(ClientContext.CREDS_PROVIDER); - - if (credsProvider != null && HttpClientParams.isAuthenticating(this.params)) { + if (HttpClientParams.isAuthenticating(this.params)) { if (this.authenticator.isAuthenticationRequested(response, - this.proxyAuthHandler, this.proxyAuthState, context)) { - if (this.authenticator.authenticate( - proxy, response, - this.proxyAuthHandler, this.proxyAuthState, - credsProvider, context)) { + this.proxyAuthStrategy, this.proxyAuthState, context)) { + if (this.authenticator.authenticate(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { // Retry request if (this.reuseStrategy.keepAlive(response, context)) { this.log.debug("Connection kept alive"); @@ -1046,13 +1094,9 @@ public class DefaultRequestDirector implements RequestDirector { return newRequest; } - CredentialsProvider credsProvider = (CredentialsProvider) - context.getAttribute(ClientContext.CREDS_PROVIDER); - - if (credsProvider != null && HttpClientParams.isAuthenticating(params)) { - + if (HttpClientParams.isAuthenticating(params)) { if (this.authenticator.isAuthenticationRequested(response, - this.targetAuthHandler, this.targetAuthState, context)) { + this.targetAuthStrategy, this.targetAuthState, context)) { HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); @@ -1064,10 +1108,8 @@ public class DefaultRequestDirector implements RequestDirector { target = new HttpHost( target.getHostName(), scheme.getDefaultPort(), target.getSchemeName()); } - if (this.authenticator.authenticate( - target, response, - this.targetAuthHandler, this.targetAuthState, - credsProvider, context)) { + if (this.authenticator.authenticate(target, response, + this.targetAuthStrategy, this.targetAuthState, context)) { // Re-try the same request via the same route return roureq; } else { @@ -1076,12 +1118,10 @@ public class DefaultRequestDirector implements RequestDirector { } if (this.authenticator.isAuthenticationRequested(response, - this.proxyAuthHandler, this.proxyAuthState, context)) { + this.proxyAuthStrategy, this.proxyAuthState, context)) { HttpHost proxy = route.getProxyHost(); - if (this.authenticator.authenticate( - proxy, response, - this.proxyAuthHandler, this.proxyAuthState, - credsProvider, context)) { + if (this.authenticator.authenticate(proxy, response, + this.proxyAuthStrategy, this.proxyAuthState, context)) { // Re-try the same request via the same route return roureq; } else { diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java index 1850c153c..127253409 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultTargetAuthenticationHandler.java @@ -46,7 +46,10 @@ import org.apache.http.protocol.HttpContext; * authentication. * * @since 4.0 + * + * @deprecated use {@link TargetAuthenticationStrategy} */ +@Deprecated @Immutable public class DefaultTargetAuthenticationHandler extends AbstractAuthenticationHandler { diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java index fc1f54f1c..7d66eb322 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpAuthenticator.java @@ -36,14 +36,11 @@ import org.apache.http.Header; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthChallengeState; +import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthScheme; -import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; -import org.apache.http.auth.AuthenticationException; -import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.client.AuthenticationHandler; -import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.AuthenticationStrategy; import org.apache.http.protocol.HttpContext; public class HttpAuthenticator { @@ -61,10 +58,10 @@ public class HttpAuthenticator { public boolean isAuthenticationRequested( final HttpResponse response, - final AuthenticationHandler authHandler, + final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context) { - if (authHandler.isAuthenticationRequested(response, context)) { + if (authStrategy.isAuthenticationRequested(response, context)) { return true; } else { if (authState.getChallengeState() == AuthChallengeState.CHALLENGED) { @@ -79,79 +76,53 @@ public class HttpAuthenticator { public boolean authenticate( final HttpHost host, final HttpResponse response, - final AuthenticationHandler authHandler, + final AuthenticationStrategy authStrategy, final AuthState authState, - final CredentialsProvider credsProvider, final HttpContext context) { try { if (this.log.isDebugEnabled()) { this.log.debug(host.toHostString() + " requested authentication"); } - Map challenges = authHandler.getChallenges(response, context); + Map challenges = authStrategy.getChallenges(response, context); if (challenges.isEmpty()) { this.log.debug("Response contains no authentication challenges"); return false; } AuthScheme authScheme = authState.getAuthScheme(); - if (authScheme == null) { - // Authentication not attempted before - authScheme = authHandler.selectScheme(challenges, response, context); - authState.setAuthScheme(authScheme); - } - String id = authScheme.getSchemeName(); - Header challenge = challenges.get(id.toLowerCase(Locale.US)); - if (challenge == null) { - // Retry authentication with a different scheme - authState.invalidate(); - authScheme = authHandler.selectScheme(challenges, response, context); - authState.setAuthScheme(authScheme); - id = authScheme.getSchemeName(); - challenge = challenges.get(id.toLowerCase(Locale.US)); - } - authState.setChallengeState(AuthChallengeState.CHALLENGED); - authScheme.processChallenge(challenge); - this.log.debug("Authorization challenge processed"); - - AuthScope authScope = new AuthScope( - host.getHostName(), - host.getPort(), - authScheme.getRealm(), - authScheme.getSchemeName()); - - if (this.log.isDebugEnabled()) { - this.log.debug("Authentication scope: " + authScope); - } - Credentials creds = authState.getCredentials(); - if (creds == null) { - creds = credsProvider.getCredentials(authScope); - if (this.log.isDebugEnabled()) { - if (creds != null) { - this.log.debug("Found credentials"); + if (authScheme != null) { + String id = authScheme.getSchemeName(); + Header challenge = challenges.get(id.toLowerCase(Locale.US)); + if (challenge != null) { + this.log.debug("Authorization challenge processed"); + authScheme.processChallenge(challenge); + if (authScheme.isComplete()) { + this.log.debug("Authentication failed"); + authState.setChallengeState(AuthChallengeState.FAILURE); + authState.setCredentials(null); + return false; } else { - this.log.debug("Credentials not found"); + authState.setChallengeState(AuthChallengeState.CHALLENGED); + return true; } - } - } else { - if (authScheme.isComplete()) { - this.log.debug("Authentication failed"); - authState.setChallengeState(AuthChallengeState.FAILURE); - creds = null; + } else { + authState.invalidate(); + // Retry authentication with a different scheme } } - authState.setCredentials(creds); - return creds != null; + AuthOption authOption = authStrategy.select(challenges, host, response, context); + if (authOption == null) { + return false; + } + authState.setAuthScheme(authOption.getAuthScheme()); + authState.setCredentials(authOption.getCredentials()); + authState.setChallengeState(AuthChallengeState.CHALLENGED); + return true; } catch (MalformedChallengeException ex) { if (this.log.isWarnEnabled()) { this.log.warn("Malformed challenge: " + ex.getMessage()); } authState.invalidate(); return false; - } catch (AuthenticationException ex) { - if (this.log.isWarnEnabled()) { - this.log.warn("Authentication error: " + ex.getMessage()); - } - authState.invalidate(); - return false; } } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java new file mode 100644 index 000000000..ff74c3988 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.client; + +import org.apache.http.HttpStatus; +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AUTH; +import org.apache.http.auth.params.AuthPNames; +import org.apache.http.client.AuthenticationStrategy; + +/** + * Default {@link AuthenticationStrategy} implementation for proxy host authentication. + * + * @since 4.2 + */ +@Immutable +public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl { + + public ProxyAuthenticationStrategy() { + super(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, AUTH.PROXY_AUTH, AuthPNames.PROXY_AUTH_PREF); + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java new file mode 100644 index 000000000..eb2cc8dd2 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java @@ -0,0 +1,48 @@ +/* + * ==================================================================== + * 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 + * . + * + */ + +package org.apache.http.impl.client; + +import org.apache.http.HttpStatus; +import org.apache.http.annotation.Immutable; +import org.apache.http.auth.AUTH; +import org.apache.http.auth.params.AuthPNames; +import org.apache.http.client.AuthenticationStrategy; + +/** + * Default {@link AuthenticationStrategy} implementation for proxy host authentication. + * + * @since 4.2 + */ +@Immutable +public class TargetAuthenticationStrategy extends AuthenticationStrategyImpl { + + public TargetAuthenticationStrategy() { + super(HttpStatus.SC_UNAUTHORIZED, AUTH.WWW_AUTH, AuthPNames.TARGET_AUTH_PREF); + } + +}