From 732095943fed4ee49ddda668c6ba77e60774d125 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Wed, 12 Oct 2011 18:33:15 +0000 Subject: [PATCH] Redesigned auth caching git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1182515 13f79535-47bb-0310-9956-ffa450edef68 --- .../http/client/AuthenticationStrategy.java | 34 +++- .../client/protocol/RequestAuthCache.java | 1 + .../client/protocol/ResponseAuthCache.java | 4 + .../client/AuthenticationStrategyAdaptor.java | 46 +++++- .../client/AuthenticationStrategyImpl.java | 62 ++++++- .../http/impl/client/DefaultHttpClient.java | 3 - .../impl/client/DefaultRequestDirector.java | 45 ++--- .../http/impl/client/HttpAuthenticator.java | 8 +- .../apache/http/impl/client/ProxyClient.java | 2 +- .../protocol/TestResponseAuthCache.java | 1 + .../client/TestAuthenticationStrategy.java | 155 +++++++++++++++++- .../impl/client/TestClientAuthentication.java | 3 +- .../impl/client/TestHttpAuthenticator.java | 62 +++++-- 13 files changed, 370 insertions(+), 56 deletions(-) diff --git a/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java index b003feb53..4f4ef9696 100644 --- a/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java +++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java @@ -34,6 +34,7 @@ import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.auth.AuthOption; +import org.apache.http.auth.AuthScheme; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.protocol.HttpContext; @@ -52,13 +53,16 @@ public interface AuthenticationStrategy { /** * Determines if the given HTTP response response represents * an authentication challenge that was sent back as a result - * of authentication failure + * of authentication failure. + * + * @param authhost authentication host. * @param response HTTP response. * @param context HTTP context. * @return true if user authentication is required, * false otherwise. */ boolean isAuthenticationRequested( + HttpHost authhost, HttpResponse response, HttpContext context); @@ -67,6 +71,7 @@ boolean isAuthenticationRequested( * challenges, each of which represents an authentication scheme supported * by the authentication host. * + * @param authhost authentication host. * @param response HTTP response. * @param context HTTP context. * @return a collection of challenges keyed by names of corresponding @@ -75,6 +80,7 @@ boolean isAuthenticationRequested( * challenges is not valid or malformed. */ Map getChallenges( + HttpHost authhost, HttpResponse response, HttpContext context) throws MalformedChallengeException; @@ -82,7 +88,9 @@ Map getChallenges( * 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 authhost authentication host. * @param response HTTP response. * @param context HTTP context. * @return authentication auth schemes that can be used for authentication. Can be empty. @@ -95,4 +103,28 @@ Queue select( HttpResponse response, HttpContext context) throws MalformedChallengeException; + /** + * Callback invoked in case of successful authentication. + * + * @param authhost authentication host. + * @param authScheme authentication scheme used. + * @param context HTTP context. + */ + void authSucceeded( + HttpHost authhost, + AuthScheme authScheme, + HttpContext context); + + /** + * Callback invoked in case of unsuccessful authentication. + * + * @param authhost authentication host. + * @param authScheme authentication scheme used. + * @param context HTTP context. + */ + void authFailed( + HttpHost authhost, + AuthScheme authScheme, + HttpContext context); + } diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java index fdb374f85..efa23e1d9 100644 --- a/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java +++ b/httpclient/src/main/java/org/apache/http/client/protocol/RequestAuthCache.java @@ -127,6 +127,7 @@ private void doPreemptiveAuth( Credentials creds = credsProvider.getCredentials(authScope); if (creds != null) { + authState.setState(AuthProtocolState.SUCCESS); authState.update(authScheme, creds); } else { this.log.debug("No credentials for preemptive authentication"); diff --git a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java index 4ac0d8ada..349f72dfc 100644 --- a/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java +++ b/httpclient/src/main/java/org/apache/http/client/protocol/ResponseAuthCache.java @@ -39,6 +39,7 @@ import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthState; import org.apache.http.client.AuthCache; +import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.params.AuthPolicy; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.scheme.SchemeRegistry; @@ -53,8 +54,11 @@ * additional authentication round-trips. * * @since 4.1 + * + * @deprecated use {@link AuthenticationStrategy} */ @Immutable +@Deprecated public class ResponseAuthCache implements HttpResponseInterceptor { private final Log log = LogFactory.getLog(getClass()); 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 index f70838afc..5c4a7768e 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyAdaptor.java @@ -44,9 +44,11 @@ import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.client.AuthCache; import org.apache.http.client.AuthenticationHandler; 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.HttpContext; @@ -68,11 +70,15 @@ public AuthenticationStrategyAdaptor(final AuthenticationHandler handler) { this.handler = handler; } - public boolean isAuthenticationRequested(final HttpResponse response, final HttpContext context) { + public boolean isAuthenticationRequested( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) { return this.handler.isAuthenticationRequested(response, context); } public Map getChallenges( + final HttpHost authhost, final HttpResponse response, final HttpContext context) throws MalformedChallengeException { return this.handler.getChallenges(response, context); @@ -130,6 +136,44 @@ public Queue select( return options; } + public void authSucceeded( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (isCachable(authScheme)) { + if (authCache == null) { + authCache = new BasicAuthCache(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.put(authhost, authScheme); + } + } + + public void authFailed( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (authCache == null) { + return; + } + if (this.log.isDebugEnabled()) { + this.log.debug("Removing from cache '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.remove(authhost); + } + + private boolean isCachable(final AuthScheme authScheme) { + if (authScheme == null || !authScheme.isComplete()) { + return false; + } + String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) || + schemeName.equalsIgnoreCase(AuthPolicy.DIGEST); + } + 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 index 8433a65ed..79de9792c 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java @@ -49,6 +49,7 @@ import org.apache.http.auth.AuthScope; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.client.AuthCache; import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.params.AuthPolicy; @@ -81,7 +82,10 @@ class AuthenticationStrategyImpl implements AuthenticationStrategy { this.prefParamName = prefParamName; } - public boolean isAuthenticationRequested(final HttpResponse response, final HttpContext context) { + public boolean isAuthenticationRequested( + final HttpHost authhost, + final HttpResponse response, + final HttpContext context) { if (response == null) { throw new IllegalArgumentException("HTTP response may not be null"); } @@ -90,6 +94,7 @@ public boolean isAuthenticationRequested(final HttpResponse response, final Http } public Map getChallenges( + final HttpHost authhost, final HttpResponse response, final HttpContext context) throws MalformedChallengeException { if (response == null) { @@ -200,4 +205,59 @@ public Queue select( return options; } + public void authSucceeded( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + if (authhost == null) { + throw new IllegalArgumentException("Host may not be null"); + } + if (authScheme == null) { + throw new IllegalArgumentException("Auth scheme may not be null"); + } + if (context == null) { + throw new IllegalArgumentException("HTTP context may not be null"); + } + if (isCachable(authScheme)) { + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (authCache == null) { + authCache = new BasicAuthCache(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + } + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.put(authhost, authScheme); + } + } + + protected boolean isCachable(final AuthScheme authScheme) { + if (authScheme == null || !authScheme.isComplete()) { + return false; + } + String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) || + schemeName.equalsIgnoreCase(AuthPolicy.DIGEST); + } + + public void authFailed( + final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { + if (authhost == null) { + throw new IllegalArgumentException("Host may not be null"); + } + if (authScheme == null) { + throw new IllegalArgumentException("Auth scheme may not be null"); + } + if (context == null) { + throw new IllegalArgumentException("HTTP context may not be null"); + } + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + if (authCache != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Removing from cache '" + authScheme.getSchemeName() + + "' auth scheme for " + authhost); + } + authCache.remove(authhost); + } + } + } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java index b7055487a..391e0e2cf 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultHttpClient.java @@ -36,7 +36,6 @@ import org.apache.http.client.protocol.RequestDefaultHeaders; import org.apache.http.client.protocol.RequestProxyAuthentication; import org.apache.http.client.protocol.RequestTargetAuthentication; -import org.apache.http.client.protocol.ResponseAuthCache; import org.apache.http.client.protocol.ResponseProcessCookies; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.params.CoreConnectionPNames; @@ -198,7 +197,6 @@ public static void setDefaultHttpParams(HttpParams params) { *
  • {@link RequestAddCookies}
  • *
  • {@link ResponseProcessCookies}
  • *
  • {@link RequestAuthCache}
  • - *
  • {@link ResponseAuthCache}
  • *
  • {@link RequestTargetAuthentication}
  • *
  • {@link RequestProxyAuthentication}
  • * @@ -221,7 +219,6 @@ protected BasicHttpProcessor createHttpProcessor() { httpproc.addInterceptor(new ResponseProcessCookies()); // HTTP authentication interceptors httpproc.addInterceptor(new RequestAuthCache()); - httpproc.addInterceptor(new ResponseAuthCache()); httpproc.addInterceptor(new RequestTargetAuthentication()); httpproc.addInterceptor(new RequestProxyAuthentication()); return httpproc; 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 4834758b6..bd612c519 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 @@ -184,9 +184,9 @@ public class DefaultRequestDirector implements RequestDirector { /** The currently allocated connection. */ protected ManagedClientConnection managedConn; - protected AuthState targetAuthState; + protected final AuthState targetAuthState; - protected AuthState proxyAuthState; + protected final AuthState proxyAuthState; private final HttpAuthenticator authenticator; @@ -351,6 +351,8 @@ public DefaultRequestDirector( this.execCount = 0; this.redirectCount = 0; + this.targetAuthState = new AuthState(); + this.proxyAuthState = new AuthState(); this.maxRedirects = this.params.getIntParameter(ClientPNames.MAX_REDIRECTS, 100); } @@ -400,16 +402,8 @@ public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws HttpException, IOException { - targetAuthState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); - if (targetAuthState == null) { - targetAuthState = new AuthState(); - context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState); - } - proxyAuthState = (AuthState) context.getAttribute(ClientContext.PROXY_AUTH_STATE); - if (proxyAuthState == null) { - proxyAuthState = new AuthState(); - context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState); - } + context.setAttribute(ClientContext.TARGET_AUTH_STATE, targetAuthState); + context.setAttribute(ClientContext.PROXY_AUTH_STATE, proxyAuthState); HttpRequest orig = request; RequestWrapper origWrapper = wrapRequest(orig); @@ -910,7 +904,7 @@ protected boolean createTunnelToTarget(HttpRoute route, } if (HttpClientParams.isAuthenticating(this.params)) { - if (this.authenticator.isAuthenticationRequested(response, + if (this.authenticator.isAuthenticationRequested(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { if (this.authenticator.authenticate(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { @@ -1107,19 +1101,16 @@ protected RoutedRequest handleResponse(RoutedRequest roureq, } if (HttpClientParams.isAuthenticating(params)) { - if (this.authenticator.isAuthenticationRequested(response, + HttpHost target = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); + if (target == null) { + target = route.getTargetHost(); + } + if (target.getPort() < 0) { + Scheme scheme = connManager.getSchemeRegistry().getScheme(target); + target = new HttpHost(target.getHostName(), scheme.getDefaultPort(), target.getSchemeName()); + } + if (this.authenticator.isAuthenticationRequested(target, response, this.targetAuthStrategy, this.targetAuthState, context)) { - - HttpHost target = (HttpHost) - context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); - if (target == null) { - target = route.getTargetHost(); - } - if (target.getPort() < 0) { - Scheme scheme = connManager.getSchemeRegistry().getScheme(target); - target = new HttpHost( - target.getHostName(), scheme.getDefaultPort(), target.getSchemeName()); - } if (this.authenticator.authenticate(target, response, this.targetAuthStrategy, this.targetAuthState, context)) { // Re-try the same request via the same route @@ -1129,9 +1120,9 @@ protected RoutedRequest handleResponse(RoutedRequest roureq, } } - if (this.authenticator.isAuthenticationRequested(response, + HttpHost proxy = route.getProxyHost(); + if (this.authenticator.isAuthenticationRequested(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { - HttpHost proxy = route.getProxyHost(); if (this.authenticator.authenticate(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { // Re-try the same request via the same route 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 3c8b8d22e..8bf419b3d 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 @@ -58,17 +58,19 @@ public HttpAuthenticator() { } public boolean isAuthenticationRequested( + final HttpHost host, final HttpResponse response, final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context) { - if (authStrategy.isAuthenticationRequested(response, context)) { + if (authStrategy.isAuthenticationRequested(host, response, context)) { return true; } else { switch (authState.getState()) { case CHALLENGED: case HANDSHAKE: authState.setState(AuthProtocolState.SUCCESS); + authStrategy.authSucceeded(host, authState.getAuthScheme(), context); break; case SUCCESS: break; @@ -89,7 +91,7 @@ public boolean authenticate( if (this.log.isDebugEnabled()) { this.log.debug(host.toHostString() + " requested authentication"); } - Map challenges = authStrategy.getChallenges(response, context); + Map challenges = authStrategy.getChallenges(host, response, context); if (challenges.isEmpty()) { this.log.debug("Response contains no authentication challenges"); return false; @@ -105,6 +107,7 @@ public boolean authenticate( case CHALLENGED: if (authScheme == null) { this.log.debug("Auth scheme is null"); + authStrategy.authFailed(host, authState.getAuthScheme(), context); authState.reset(); authState.setState(AuthProtocolState.FAILURE); return false; @@ -118,6 +121,7 @@ public boolean authenticate( authScheme.processChallenge(challenge); if (authScheme.isComplete()) { this.log.debug("Authentication failed"); + authStrategy.authFailed(host, authState.getAuthScheme(), context); authState.reset(); authState.setState(AuthProtocolState.FAILURE); return false; diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java index 9c51a8869..41ff8892f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java @@ -176,7 +176,7 @@ public Socket tunnel( } if (HttpClientParams.isAuthenticating(this.params)) { - if (this.authenticator.isAuthenticationRequested(response, + if (this.authenticator.isAuthenticationRequested(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { if (this.authenticator.authenticate(proxy, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { diff --git a/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java b/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java index 2445194ad..4a49d810c 100644 --- a/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java +++ b/httpclient/src/test/java/org/apache/http/client/protocol/TestResponseAuthCache.java @@ -48,6 +48,7 @@ import org.junit.Before; import org.junit.Test; +@Deprecated public class TestResponseAuthCache { private HttpHost target; diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java b/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java index 3e5dbbead..03096377d 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java @@ -37,10 +37,12 @@ import org.apache.http.HttpVersion; import org.apache.http.auth.AUTH; 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.UsernamePasswordCredentials; import org.apache.http.auth.params.AuthPNames; +import org.apache.http.client.AuthCache; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.params.AuthPolicy; import org.apache.http.client.protocol.ClientContext; @@ -54,6 +56,7 @@ import org.apache.http.protocol.HttpContext; import org.junit.Assert; import org.junit.Test; +import org.mockito.Mockito; /** * Simple tests for {@link AuthenticationStrategyImpl}. @@ -63,37 +66,42 @@ public class TestAuthenticationStrategy { @Test(expected=IllegalArgumentException.class) public void testIsAuthenticationRequestedInvalidInput() throws Exception { TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost host = new HttpHost("localhost", 80); HttpContext context = new BasicHttpContext(); - authStrategy.isAuthenticationRequested(null, context); + authStrategy.isAuthenticationRequested(host, null, context); } @Test public void testTargetAuthRequested() throws Exception { TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + HttpHost host = new HttpHost("localhost", 80); HttpContext context = new BasicHttpContext(); - Assert.assertTrue(authStrategy.isAuthenticationRequested(response, context)); + Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context)); } @Test public void testProxyAuthRequested() throws Exception { ProxyAuthenticationStrategy authStrategy = new ProxyAuthenticationStrategy(); HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, "UNAUTHORIZED"); + HttpHost host = new HttpHost("localhost", 80); HttpContext context = new BasicHttpContext(); - Assert.assertTrue(authStrategy.isAuthenticationRequested(response, context)); + Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context)); } @Test(expected=IllegalArgumentException.class) public void testGetChallengesInvalidInput() throws Exception { TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost host = new HttpHost("localhost", 80); HttpContext context = new BasicHttpContext(); - authStrategy.getChallenges(null, context); + authStrategy.getChallenges(host, null, context); } @Test public void testGetChallenges() throws Exception { TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); HttpContext context = new BasicHttpContext(); + HttpHost host = new HttpHost("localhost", 80); HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); Header h1 = new BasicHeader(AUTH.WWW_AUTH, " Basic realm=\"test\""); Header h2 = new BasicHeader(AUTH.WWW_AUTH, "\t\tDigest realm=\"realm1\", nonce=\"1234\""); @@ -102,7 +110,7 @@ public void testGetChallenges() throws Exception { response.addHeader(h2); response.addHeader(h3); - Map challenges = authStrategy.getChallenges(response, context); + Map challenges = authStrategy.getChallenges(host, response, context); Assert.assertNotNull(challenges); Assert.assertEquals(3, challenges.size()); @@ -290,4 +298,141 @@ public void testCustomAuthPreference() throws Exception { Assert.assertTrue(option1.getAuthScheme() instanceof BasicScheme); } + @Test + public void testAuthSucceededInvalidInput() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("locahost", 80); + BasicScheme authScheme = new BasicScheme(); + HttpContext context = new BasicHttpContext(); + try { + authStrategy.authSucceeded(null, authScheme, context); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + try { + authStrategy.authSucceeded(authhost, null, context); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + try { + authStrategy.authSucceeded(authhost, authScheme, null); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + } + + @Test + public void testAuthSucceeded() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + BasicScheme authScheme = new BasicScheme(); + authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); + + AuthCache authCache = Mockito.mock(AuthCache.class); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + + authStrategy.authSucceeded(authhost, authScheme, context); + Mockito.verify(authCache).put(authhost, authScheme); + } + + @Test + public void testAuthSucceededNoCache() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + BasicScheme authScheme = new BasicScheme(); + authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, null); + + authStrategy.authSucceeded(authhost, authScheme, context); + AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE); + Assert.assertNotNull(authCache); + } + + @Test + public void testAuthScemeNotCompleted() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + BasicScheme authScheme = new BasicScheme(); + + AuthCache authCache = Mockito.mock(AuthCache.class); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + + authStrategy.authSucceeded(authhost, authScheme, context); + Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme); + } + + @Test + public void testAuthScemeNonCacheable() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + AuthScheme authScheme = Mockito.mock(AuthScheme.class); + Mockito.when(authScheme.isComplete()).thenReturn(true); + Mockito.when(authScheme.getSchemeName()).thenReturn("whatever"); + + AuthCache authCache = Mockito.mock(AuthCache.class); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + + authStrategy.authSucceeded(authhost, authScheme, context); + Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme); + } + + @Test + public void testAuthFailedInvalidInput() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("locahost", 80); + BasicScheme authScheme = new BasicScheme(); + HttpContext context = new BasicHttpContext(); + try { + authStrategy.authFailed(null, authScheme, context); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + try { + authStrategy.authFailed(authhost, null, context); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + try { + authStrategy.authFailed(authhost, authScheme, null); + Assert.fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException ex) { + } + } + + @Test + public void testAuthFailed() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + BasicScheme authScheme = new BasicScheme(); + authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); + + AuthCache authCache = Mockito.mock(AuthCache.class); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, authCache); + + authStrategy.authFailed(authhost, authScheme, context); + Mockito.verify(authCache).remove(authhost); + } + + @Test + public void testAuthFailedNoCache() throws Exception { + TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + HttpHost authhost = new HttpHost("somehost", 80); + BasicScheme authScheme = new BasicScheme(); + + HttpContext context = new BasicHttpContext(); + context.setAttribute(ClientContext.AUTH_CACHE, null); + + authStrategy.authFailed(authhost, authScheme, context); + } + } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java index e758dd83c..cd5b61bbc 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/TestClientAuthentication.java @@ -345,9 +345,10 @@ public TestTargetAuthenticationStrategy() { @Override public boolean isAuthenticationRequested( + final HttpHost host, final HttpResponse response, final HttpContext context) { - boolean res = super.isAuthenticationRequested(response, context); + boolean res = super.isAuthenticationRequested(host, response, context); if (res == true) { synchronized (this) { this.count++; diff --git a/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java b/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java index 777f83ac6..91c5899a6 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/TestHttpAuthenticator.java @@ -36,11 +36,13 @@ import org.apache.http.auth.AUTH; import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthProtocolState; +import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthSchemeRegistry; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.client.AuthCache; import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.protocol.ClientContext; import org.apache.http.impl.auth.BasicScheme; @@ -51,6 +53,7 @@ import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpResponse; import org.apache.http.protocol.BasicHttpContext; +import org.apache.http.protocol.ExecutionContext; import org.apache.http.protocol.HttpContext; import org.junit.Assert; import org.junit.Before; @@ -61,17 +64,27 @@ public class TestHttpAuthenticator { private AuthenticationStrategy authStrategy; private AuthState authState; + private AuthScheme authScheme; private HttpContext context; + private HttpHost host; + private HttpHost proxy; private Credentials credentials; private BasicCredentialsProvider credentialsProvider; private AuthSchemeRegistry authSchemeRegistry; + private AuthCache authCache; private HttpAuthenticator httpAuthenticator; @Before - public void setUp() { + public void setUp() throws Exception { this.authStrategy = Mockito.mock(AuthenticationStrategy.class); this.authState = new AuthState(); + this.authScheme = new BasicScheme(); + this.authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); this.context = new BasicHttpContext(); + this.host = new HttpHost("localhost", 80); + this.proxy = new HttpHost("localhost", 8888); + this.context.setAttribute(ExecutionContext.HTTP_TARGET_HOST, this.host); + this.context.setAttribute(ExecutionContext.HTTP_PROXY_HOST, this.proxy); this.credentials = Mockito.mock(Credentials.class); this.credentialsProvider = new BasicCredentialsProvider(); this.credentialsProvider.setCredentials(AuthScope.ANY, this.credentials); @@ -81,6 +94,8 @@ public void setUp() { this.authSchemeRegistry.register("digest", new DigestSchemeFactory()); this.authSchemeRegistry.register("ntlm", new NTLMSchemeFactory()); this.context.setAttribute(ClientContext.AUTHSCHEME_REGISTRY, this.authSchemeRegistry); + this.authCache = Mockito.mock(AuthCache.class); + this.context.setAttribute(ClientContext.AUTH_CACHE, this.authCache); this.httpAuthenticator = new HttpAuthenticator(); } @@ -88,53 +103,65 @@ public void setUp() { public void testAuthenticationRequested() throws Exception { HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); Mockito.when(this.authStrategy.isAuthenticationRequested( - Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class))).thenReturn(Boolean.TRUE); + Mockito.any(HttpHost.class), + Mockito.any(HttpResponse.class), + Mockito.any(HttpContext.class))).thenReturn(Boolean.TRUE); Assert.assertTrue(this.httpAuthenticator.isAuthenticationRequested( - response, this.authStrategy, this.authState, this.context)); + this.host, response, this.authStrategy, this.authState, this.context)); - Mockito.verify(this.authStrategy).isAuthenticationRequested(response, this.context); + Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context); } @Test public void testAuthenticationNotRequestedUnchallenged() throws Exception { HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); Mockito.when(this.authStrategy.isAuthenticationRequested( - Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); + Mockito.any(HttpHost.class), + Mockito.any(HttpResponse.class), + Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested( - response, this.authStrategy, this.authState, this.context)); + this.host, response, this.authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState()); - Mockito.verify(this.authStrategy).isAuthenticationRequested(response, this.context); + Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context); } @Test public void testAuthenticationNotRequestedSuccess1() throws Exception { HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); Mockito.when(this.authStrategy.isAuthenticationRequested( - Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); + Mockito.any(HttpHost.class), + Mockito.any(HttpResponse.class), + Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); + this.authState.update(this.authScheme, this.credentials); this.authState.setState(AuthProtocolState.CHALLENGED); Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested( - response, this.authStrategy, this.authState, this.context)); + this.host, response, this.authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState()); - Mockito.verify(this.authStrategy).isAuthenticationRequested(response, this.context); + Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context); + Mockito.verify(this.authStrategy).authSucceeded(this.host, this.authScheme, this.context); } @Test public void testAuthenticationNotRequestedSuccess2() throws Exception { HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); Mockito.when(this.authStrategy.isAuthenticationRequested( - Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); + Mockito.any(HttpHost.class), + Mockito.any(HttpResponse.class), + Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); + this.authState.update(this.authScheme, this.credentials); this.authState.setState(AuthProtocolState.HANDSHAKE); Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested( - response, this.authStrategy, this.authState, this.context)); + this.host, response, this.authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState()); - Mockito.verify(this.authStrategy).isAuthenticationRequested(response, this.context); + Mockito.verify(this.authStrategy).isAuthenticationRequested(this.host, response, this.context); + Mockito.verify(this.authStrategy).authSucceeded(this.host, this.authScheme, this.context); } @Test @@ -168,6 +195,7 @@ public void testAuthenticationNoChallenges() throws Exception { HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); Mockito.when(this.authStrategy.getChallenges( + Mockito.any(HttpHost.class), Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class))).thenReturn(new HashMap()); @@ -228,6 +256,7 @@ public void testAuthenticationNoAuthScheme() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); this.authState.setState(AuthProtocolState.CHALLENGED); + this.authState.update(this.authScheme, this.credentials); TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); @@ -235,6 +264,8 @@ public void testAuthenticationNoAuthScheme() throws Exception { response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState()); + + Mockito.verify(this.authCache).remove(host); } @Test @@ -271,6 +302,7 @@ public void testAuthenticationHandshaking() throws Exception { Assert.assertTrue(this.httpAuthenticator.authenticate(host, response, authStrategy, this.authState, this.context)); + Assert.assertEquals(AuthProtocolState.HANDSHAKE, this.authState.getState()); } @@ -306,7 +338,9 @@ public void testAuthenticationException() throws Exception { this.authState.setState(AuthProtocolState.CHALLENGED); Mockito.doThrow(new MalformedChallengeException()).when(this.authStrategy).getChallenges( - Mockito.any(HttpResponse.class), Mockito.any(HttpContext.class)); + Mockito.any(HttpHost.class), + Mockito.any(HttpResponse.class), + Mockito.any(HttpContext.class)); Assert.assertFalse(this.httpAuthenticator.authenticate(host, response, this.authStrategy, this.authState, this.context));