From 2f66271de984350eb52cd9a0bf51d2d54dbb1ef9 Mon Sep 17 00:00:00 2001 From: Oleg Kalnichevski Date: Sat, 4 Jul 2015 13:42:40 +0000 Subject: [PATCH] RFC 7231: redesign of HTTP authenticator and related classes git-svn-id: https://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk@1689155 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/http/client/fluent/Executor.java | 10 +- .../impl/auth/win/WindowsNegotiateScheme.java | 39 +-- .../org/apache/http/auth/AuthChallenge.java | 5 + .../java/org/apache/http/auth/AuthScheme.java | 46 +-- ...ChallengeState.java => ChallengeType.java} | 4 +- .../http/client/AuthenticationStrategy.java | 80 +---- .../http/impl/auth/AuthChallengeParser.java | 4 +- .../apache/http/impl/auth/AuthSchemeBase.java | 143 --------- .../apache/http/impl/auth/BasicScheme.java | 98 +++--- .../apache/http/impl/auth/DigestScheme.java | 87 +---- .../http/impl/auth/DigestSchemeFactory.java | 18 +- .../apache/http/impl/auth/GGSSchemeBase.java | 43 +-- .../http/impl/auth/HttpAuthenticator.java | 129 ++++++-- .../apache/http/impl/auth/KerberosScheme.java | 53 ---- .../org/apache/http/impl/auth/NTLMScheme.java | 35 +- .../NonStandardAuthScheme.java} | 51 +-- .../apache/http/impl/auth/RFC2617Scheme.java | 156 --------- .../apache/http/impl/auth/SPNegoScheme.java | 53 ---- .../http/impl/auth/StandardAuthScheme.java | 111 +++++++ ...ava => DefaultAuthenticationStrategy.java} | 147 +-------- .../http/impl/client/HttpClientBuilder.java | 4 +- .../apache/http/impl/client/ProxyClient.java | 11 +- .../client/TargetAuthenticationStrategy.java | 57 ---- .../http/impl/execchain/MainClientExec.java | 19 +- .../impl/auth/TestAuthChallengeParser.java | 11 + .../http/impl/auth/TestBasicScheme.java | 37 ++- .../http/impl/auth/TestDigestScheme.java | 148 ++++----- .../http/impl/auth/TestHttpAuthenticator.java | 136 +++----- .../impl/auth/TestNonStandardHttpScheme.java | 87 +++++ .../http/impl/auth/TestRFC2617Scheme.java | 175 ---------- .../impl/auth/TestStandardHttpScheme.java | 95 ++++++ .../client/TestAuthenticationStrategy.java | 299 ++++-------------- .../integration/TestClientAuthentication.java | 25 +- .../TestClientReauthentication.java | 23 +- .../impl/execchain/TestMainClientExec.java | 108 +++---- 35 files changed, 905 insertions(+), 1642 deletions(-) rename httpclient/src/main/java/org/apache/http/auth/{ChallengeState.java => ChallengeType.java} (95%) delete mode 100644 httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java rename httpclient/src/main/java/org/apache/http/impl/{client/ProxyAuthenticationStrategy.java => auth/NonStandardAuthScheme.java} (50%) delete mode 100644 httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java create mode 100644 httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java rename httpclient/src/main/java/org/apache/http/impl/client/{AuthenticationStrategyImpl.java => DefaultAuthenticationStrategy.java} (50%) delete mode 100644 httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java create mode 100644 httpclient/src/test/java/org/apache/http/impl/auth/TestNonStandardHttpScheme.java delete mode 100644 httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java create mode 100644 httpclient/src/test/java/org/apache/http/impl/auth/TestStandardHttpScheme.java diff --git a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java index 6ef7e75cd..96fe823ee 100644 --- a/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java +++ b/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java @@ -29,13 +29,16 @@ import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; +import java.util.Collections; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; -import org.apache.http.auth.AUTH; +import org.apache.http.NameValuePair; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthScope; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.NTCredentials; @@ -58,7 +61,6 @@ import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.apache.http.message.BasicHeader; /** * An Executor for fluent requests. @@ -151,7 +153,7 @@ public Executor auth(final String host, final Credentials creds) { public Executor authPreemptive(final HttpHost host) { final BasicScheme basicScheme = new BasicScheme(); try { - basicScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "BASIC ")); + basicScheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("basic", null, Collections.emptyList())); } catch (final MalformedChallengeException ignore) { } this.authCache.put(host, basicScheme); @@ -168,7 +170,7 @@ public Executor authPreemptive(final String host) { public Executor authPreemptiveProxy(final HttpHost proxy) { final BasicScheme basicScheme = new BasicScheme(); try { - basicScheme.processChallenge(new BasicHeader(AUTH.PROXY_AUTH, "BASIC ")); + basicScheme.processChallenge(ChallengeType.PROXY, new AuthChallenge("basic", null, Collections.emptyList())); } catch (final MalformedChallengeException ignore) { } this.authCache.put(proxy, basicScheme); diff --git a/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java b/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java index e8d25b9b5..02f21cefb 100644 --- a/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java +++ b/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java @@ -34,14 +34,16 @@ import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.InvalidCredentialsException; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.conn.routing.RouteInfo; -import org.apache.http.impl.auth.AuthSchemeBase; +import org.apache.http.impl.auth.NonStandardAuthScheme; import org.apache.http.message.BufferedHeader; import org.apache.http.protocol.HttpContext; import org.apache.http.util.CharArrayBuffer; @@ -68,7 +70,7 @@ * @since 4.4 */ @NotThreadSafe -public class WindowsNegotiateScheme extends AuthSchemeBase { +public class WindowsNegotiateScheme extends NonStandardAuthScheme { private final Log log = LogFactory.getLog(getClass()); @@ -79,13 +81,11 @@ public class WindowsNegotiateScheme extends AuthSchemeBase { private CredHandle clientCred; private CtxtHandle sspiContext; private boolean continueNeeded; - private String challenge; public WindowsNegotiateScheme(final String scheme, final String servicePrincipalName) { super(); this.scheme = (scheme == null) ? AuthSchemes.SPNEGO : scheme; - this.challenge = null; this.continueNeeded = true; this.servicePrincipalName = servicePrincipalName; @@ -123,35 +123,21 @@ public String getSchemeName() { return scheme; } - // String parameters not supported - @Override - public String getParameter(final String name) { - return null; - } - - // NTLM/Negotiate do not support authentication realms - @Override - public String getRealm() { - return null; - } - @Override public boolean isConnectionBased() { return true; } @Override - protected void parseChallenge( - final CharArrayBuffer buffer, - final int beginIndex, - final int endIndex) throws MalformedChallengeException { - this.challenge = buffer.substringTrimmed(beginIndex, endIndex); - - if (this.challenge.isEmpty()) { + public void processChallenge( + final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { + update(challengeType, authChallenge); + final String challenge = getChallenge(); + if (challenge.isEmpty()) { if (clientCred != null) { dispose(); // run cleanup first before throwing an exception otherwise can leak OS resources if (continueNeeded) { - throw new RuntimeException("Unexpected token"); + throw new IllegalStateException("Unexpected token"); } } } @@ -163,6 +149,7 @@ public Header authenticate( final HttpRequest request, final HttpContext context) throws AuthenticationException { + final String challenge = getChallenge(); final String response; if (clientCred == null) { // ?? We don't use the credentials, should we allow anything? @@ -196,12 +183,12 @@ public Header authenticate( throw ex; } } - } else if (this.challenge == null || this.challenge.isEmpty()) { + } else if (challenge == null || challenge.isEmpty()) { failAuthCleanup(); throw new AuthenticationException("Authentication Failed"); } else { try { - final byte[] continueTokenBytes = Base64.decodeBase64(this.challenge); + final byte[] continueTokenBytes = Base64.decodeBase64(challenge); final SecBufferDesc continueTokenBuffer = new SecBufferDesc( Sspi.SECBUFFER_TOKEN, continueTokenBytes); final String targetName = getServicePrincipalName(context); diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java b/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java index c6bbb073c..7c6ed9f46 100644 --- a/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java +++ b/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java @@ -27,6 +27,7 @@ package org.apache.http.auth; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -55,6 +56,10 @@ public AuthChallenge(final String scheme, final String value, final List(params)) : null; } + public AuthChallenge(final String scheme, final NameValuePair... params) { + this(scheme, null, Arrays.asList(params)); + } + public String getScheme() { return scheme; } diff --git a/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java b/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java index 2959c61f3..e888a88cc 100644 --- a/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java +++ b/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java @@ -60,9 +60,32 @@ public interface AuthScheme { * may involve multiple challenge-response exchanges. Such schemes must be able * to maintain the state information when dealing with sequential challenges * - * @param header the challenge header + * @param challengeType the challenge type + * @param authChallenge the auth challenge + * + * @since 5.0 */ - void processChallenge(final Header header) throws MalformedChallengeException; + void processChallenge( + ChallengeType challengeType, + AuthChallenge authChallenge) throws MalformedChallengeException; + + /** + * Produces an authorization string for the given set of {@link Credentials}. + * + * @param credentials The credentials to be used for authentication + * @param request The request being authenticated + * @param context HTTP context + * @throws AuthenticationException if authorization string cannot + * be generated due to an authentication failure + * + * @return authorization header + * + * @since 5.0 + */ + Header authenticate( + Credentials credentials, + HttpRequest request, + HttpContext context) throws AuthenticationException; /** * Returns textual designation of the given authentication scheme. @@ -109,23 +132,4 @@ public interface AuthScheme { */ boolean isComplete(); - /** - * Produces an authorization string for the given set of - * {@link Credentials}. - * - * @param credentials The set of credentials to be used for athentication - * @param request The request being authenticated - * @param context HTTP context - * @throws AuthenticationException if authorization string cannot - * be generated due to an authentication failure - * - * @return the authorization string - * - * @since 5.0 - */ - Header authenticate( - Credentials credentials, - HttpRequest request, - HttpContext context) throws AuthenticationException; - } diff --git a/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java b/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java similarity index 95% rename from httpclient/src/main/java/org/apache/http/auth/ChallengeState.java rename to httpclient/src/main/java/org/apache/http/auth/ChallengeType.java index 33c48b0bb..9795c13bc 100644 --- a/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java +++ b/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java @@ -27,11 +27,11 @@ package org.apache.http.auth; /** - * Challenge mode (TARGET or PROXY) + * Challenge type (TARGET or PROXY) * * @since 4.2 */ -public enum ChallengeState { +public enum ChallengeType { TARGET, PROXY 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 501959bbf..4fe700c74 100644 --- a/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java +++ b/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java @@ -30,18 +30,16 @@ import java.util.Map; import java.util.Queue; -import org.apache.http.Header; import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthOption; -import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.ChallengeType; 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. + * Strategy to select auth schemes in order of preference based on auth challenges + * presented by the opposite endpoint (target server or a proxy). *

* 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. @@ -50,81 +48,25 @@ */ 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 authhost authentication host. - * @param response HTTP response. - * @param context HTTP context. - * @return {@code true} if user authentication is required, - * {@code false} otherwise. - */ - boolean isAuthenticationRequested( - HttpHost authhost, - 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 authhost 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( - HttpHost authhost, - 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 challengeType challenge type. + * @param host authentication host. * @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. * @throws MalformedChallengeException if one of the authentication * challenges is not valid or malformed. + * + * @since 5.0 */ Queue select( - Map challenges, - HttpHost authhost, - HttpResponse response, + ChallengeType challengeType, + HttpHost host, + Map challenges, 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/impl/auth/AuthChallengeParser.java b/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java index e64fdb01f..9511f4614 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java @@ -41,6 +41,8 @@ public class AuthChallengeParser { + public static final AuthChallengeParser INSTANCE = new AuthChallengeParser(); + private final TokenParser tokenParser = TokenParser.INSTANCE; private final static char BLANK = ' '; @@ -60,7 +62,7 @@ NameValuePair parseTokenOrParameter(final CharArrayBuffer buffer, final ParserCu if (buffer.charAt(cursor.getPos()) == BLANK) { tokenParser.skipWhiteSpace(buffer, cursor); } - if (buffer.charAt(cursor.getPos()) == EQUAL_CHAR) { + if (!cursor.atEnd() && buffer.charAt(cursor.getPos()) == EQUAL_CHAR) { cursor.updatePos(cursor.getPos() + 1); final String value = tokenParser.parseValue(buffer, cursor, DELIMITER); return new BasicNameValuePair(token, value); diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java b/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java deleted file mode 100644 index abaa38edc..000000000 --- a/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * ==================================================================== - * 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.auth; - -import java.util.Locale; - -import org.apache.http.FormattedHeader; -import org.apache.http.Header; -import org.apache.http.annotation.NotThreadSafe; -import org.apache.http.auth.AUTH; -import org.apache.http.auth.AuthScheme; -import org.apache.http.auth.ChallengeState; -import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.protocol.HTTP; -import org.apache.http.util.Args; -import org.apache.http.util.CharArrayBuffer; - -/** - * Abstract authentication scheme class that serves as a basis - * for all authentication schemes supported by HttpClient. This class - * defines the generic way of parsing an authentication challenge. It - * does not make any assumptions regarding the format of the challenge - * nor does it impose any specific way of responding to that challenge. - * - * - * @since 4.0 - */ -@NotThreadSafe -public abstract class AuthSchemeBase implements AuthScheme { - - protected ChallengeState challengeState; - - public AuthSchemeBase() { - super(); - } - - /** - * Processes the given challenge token. Some authentication schemes - * may involve multiple challenge-response exchanges. Such schemes must be able - * to maintain the state information when dealing with sequential challenges - * - * @param header the challenge header - * - * @throws MalformedChallengeException is thrown if the authentication challenge - * is malformed - */ - @Override - public void processChallenge(final Header header) throws MalformedChallengeException { - Args.notNull(header, "Header"); - final String authheader = header.getName(); - if (authheader.equalsIgnoreCase(AUTH.WWW_AUTH)) { - this.challengeState = ChallengeState.TARGET; - } else if (authheader.equalsIgnoreCase(AUTH.PROXY_AUTH)) { - this.challengeState = ChallengeState.PROXY; - } else { - throw new MalformedChallengeException("Unexpected header name: " + authheader); - } - - final CharArrayBuffer buffer; - int pos; - if (header instanceof FormattedHeader) { - buffer = ((FormattedHeader) header).getBuffer(); - pos = ((FormattedHeader) header).getValuePos(); - } else { - final 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++; - } - final int beginIndex = pos; - while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { - pos++; - } - final int endIndex = pos; - final String s = buffer.substring(beginIndex, endIndex); - if (!s.equalsIgnoreCase(getSchemeName())) { - throw new MalformedChallengeException("Invalid scheme identifier: " + s); - } - - parseChallenge(buffer, pos, buffer.length()); - } - - protected abstract void parseChallenge( - CharArrayBuffer buffer, int beginIndex, int endIndex) throws MalformedChallengeException; - - /** - * Returns {@code true} if authenticating against a proxy, {@code false} - * otherwise. - */ - public boolean isProxy() { - return this.challengeState != null && this.challengeState == ChallengeState.PROXY; - } - - /** - * Returns {@link ChallengeState} value or {@code null} if unchallenged. - * - * @since 4.2 - */ - public ChallengeState getChallengeState() { - return this.challengeState; - } - - @Override - public String toString() { - final String name = getSchemeName(); - if (name != null) { - return name.toUpperCase(Locale.ROOT); - } else { - return super.toString(); - } - } - -} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java index 28b97e187..0817784c7 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java @@ -26,6 +26,10 @@ */ package org.apache.http.impl.auth; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamException; import java.nio.charset.Charset; import org.apache.commons.codec.binary.Base64; @@ -34,13 +38,16 @@ import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.message.BufferedHeader; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; import org.apache.http.util.CharArrayBuffer; +import org.apache.http.util.CharsetUtils; import org.apache.http.util.EncodingUtils; /** @@ -49,18 +56,18 @@ * @since 4.0 */ @NotThreadSafe -public class BasicScheme extends RFC2617Scheme { +public class BasicScheme extends StandardAuthScheme { private static final long serialVersionUID = -1931571557597830536L; - /** Whether the basic authentication process is complete */ + private transient Charset charset; private boolean complete; /** * @since 4.3 */ - public BasicScheme(final Charset credentialsCharset) { - super(credentialsCharset); + public BasicScheme(final Charset charset) { + this.charset = charset != null ? charset : Consts.ASCII; this.complete = false; } @@ -68,64 +75,28 @@ public BasicScheme() { this(Consts.ASCII); } - /** - * Returns textual designation of the basic authentication scheme. - * - * @return {@code basic} - */ @Override public String getSchemeName() { return "basic"; } - /** - * Processes the Basic challenge. - * - * @param header the challenge header - * - * @throws MalformedChallengeException is thrown if the authentication challenge - * is malformed - */ - @Override public void processChallenge( - final Header header) throws MalformedChallengeException { - super.processChallenge(header); + final ChallengeType challengeType, + final AuthChallenge authChallenge) throws MalformedChallengeException { + update(challengeType, authChallenge); this.complete = true; } - /** - * Tests if the Basic authentication process has been completed. - * - * @return {@code true} if Basic authorization has been processed, - * {@code false} otherwise. - */ @Override public boolean isComplete() { return this.complete; } - /** - * Returns {@code false}. Basic authentication scheme is request based. - * - * @return {@code false}. - */ @Override public boolean isConnectionBased() { return false; } - /** - * Produces basic authorization header for the given set of {@link Credentials}. - * - * @param credentials The set of credentials to be used for authentication - * @param request The request being authenticated - * @throws org.apache.http.auth.InvalidCredentialsException if authentication - * credentials are not valid or not applicable for this authentication scheme - * @throws AuthenticationException if authorization string cannot - * be generated due to an authentication failure - * - * @return a basic authorization string - */ @Override public Header authenticate( final Credentials credentials, @@ -134,15 +105,6 @@ public Header authenticate( Args.notNull(credentials, "Credentials"); Args.notNull(request, "HTTP request"); - final StringBuilder tmp = new StringBuilder(); - tmp.append(credentials.getUserPrincipal().getName()); - tmp.append(":"); - tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); - - final Base64 base64codec = new Base64(0); - final byte[] base64password = base64codec.encode( - EncodingUtils.getBytes(tmp.toString(), getCredentialsCharset(request))); - final CharArrayBuffer buffer = new CharArrayBuffer(32); if (isProxy()) { buffer.append(AUTH.PROXY_AUTH_RESP); @@ -150,16 +112,34 @@ public Header authenticate( buffer.append(AUTH.WWW_AUTH_RESP); } buffer.append(": Basic "); - buffer.append(base64password, 0, base64password.length); + final StringBuilder tmp = new StringBuilder(); + tmp.append(credentials.getUserPrincipal().getName()); + tmp.append(":"); + tmp.append((credentials.getPassword() == null) ? "null" : credentials.getPassword()); + + final Base64 base64codec = new Base64(0); + final byte[] base64password = base64codec.encode(EncodingUtils.getBytes(tmp.toString(), charset.name())); + + buffer.append(base64password, 0, base64password.length); return new BufferedHeader(buffer); } - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("BASIC [complete=").append(complete) - .append("]"); - return builder.toString(); + private void writeObject(final ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + out.writeUTF(this.charset.name()); } + + @SuppressWarnings("unchecked") + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + this.charset = CharsetUtils.get(in.readUTF()); + if (this.charset == null) { + this.charset = Consts.ASCII; + } + } + + private void readObjectNoData() throws ObjectStreamException { + } + } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java index 8c91b9edc..9c9f8b3bc 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java @@ -27,7 +27,6 @@ package org.apache.http.impl.auth; import java.io.IOException; -import java.nio.charset.Charset; import java.security.MessageDigest; import java.security.SecureRandom; import java.util.ArrayList; @@ -38,14 +37,15 @@ import java.util.Set; import java.util.StringTokenizer; -import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.message.BasicHeaderValueFormatter; @@ -71,7 +71,7 @@ * @since 4.0 */ @NotThreadSafe -public class DigestScheme extends RFC2617Scheme { +public class DigestScheme extends StandardAuthScheme { private static final long serialVersionUID = 3883908186234566916L; @@ -100,42 +100,23 @@ public class DigestScheme extends RFC2617Scheme { private String a1; private String a2; - /** - * @since 4.3 - */ - public DigestScheme(final Charset credentialsCharset) { - super(credentialsCharset); + public DigestScheme() { this.complete = false; } - public DigestScheme() { - this(Consts.ASCII); - } - - /** - * Processes the Digest challenge. - * - * @param header the challenge header - * - * @throws MalformedChallengeException is thrown if the authentication challenge - * is malformed - */ @Override public void processChallenge( - final Header header) throws MalformedChallengeException { - super.processChallenge(header); - this.complete = true; + final ChallengeType challengeType, + final AuthChallenge authChallenge) throws MalformedChallengeException { + Args.notNull(challengeType, "ChallengeType"); + Args.notNull(authChallenge, "AuthChallenge"); + update(challengeType, authChallenge); if (getParameters().isEmpty()) { - throw new MalformedChallengeException("Authentication challenge is empty"); + throw new MalformedChallengeException("Missing digest auth parameters"); } + this.complete = true; } - /** - * Tests if the Digest authentication process has been completed. - * - * @return {@code true} if Digest authorization has been processed, - * {@code false} otherwise. - */ @Override public boolean isComplete() { final String s = getParameter("stale"); @@ -146,21 +127,11 @@ public boolean isComplete() { } } - /** - * Returns textual designation of the digest authentication scheme. - * - * @return {@code digest} - */ @Override public String getSchemeName() { return "digest"; } - /** - * Returns {@code false}. Digest authentication scheme is request based. - * - * @return {@code false}. - */ @Override public boolean isConnectionBased() { return false; @@ -170,20 +141,6 @@ public void overrideParamter(final String name, final String value) { getParameters().put(name, value); } - /** - * Produces a digest authorization string for the given set of - * {@link Credentials}, method name and URI. - * - * @param credentials A set of credentials to be used for athentication - * @param request The request being authenticated - * - * @throws org.apache.http.auth.InvalidCredentialsException if authentication credentials - * are not valid or not applicable for this authentication scheme - * @throws AuthenticationException if authorization string cannot - * be generated due to an authentication failure - * - * @return a digest authorization string - */ @Override public Header authenticate( final Credentials credentials, @@ -201,10 +158,6 @@ public Header authenticate( // Add method name and request-URI to the parameter map getParameters().put("methodname", request.getRequestLine().getMethod()); getParameters().put("uri", request.getRequestLine().getUri()); - final String charset = getParameter("charset"); - if (charset == null) { - getParameters().put("charset", getCredentialsCharset(request)); - } return createDigestHeader(credentials, request); } @@ -219,13 +172,6 @@ private static MessageDigest createMessageDigest( } } - /** - * Creates digest-response header as defined in RFC2617. - * - * @param credentials User credentials - * - * @return The digest-response as String. - */ private Header createDigestHeader( final Credentials credentials, final HttpRequest request) throws AuthenticationException { @@ -447,7 +393,6 @@ static String encode(final byte[] binaryData) { return new String(buffer); } - /** * Creates a random cnonce value based on the current time. * @@ -460,14 +405,4 @@ public static String createCnonce() { return encode(tmp); } - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("DIGEST [complete=").append(complete) - .append(", nonce=").append(lastNonce) - .append(", nc=").append(nounceCount) - .append("]"); - return builder.toString(); - } - } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java b/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java index a374138c9..545fe5b67 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java @@ -27,8 +27,6 @@ package org.apache.http.impl.auth; -import java.nio.charset.Charset; - import org.apache.http.annotation.Immutable; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthSchemeProvider; @@ -43,23 +41,9 @@ @Immutable public class DigestSchemeFactory implements AuthSchemeProvider { - private final Charset charset; - - /** - * @since 4.3 - */ - public DigestSchemeFactory(final Charset charset) { - super(); - this.charset = charset; - } - - public DigestSchemeFactory() { - this(null); - } - @Override public AuthScheme create(final HttpContext context) { - return new DigestScheme(this.charset); + return new DigestScheme(); } } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java index 9450b293f..35ac3766f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java @@ -37,7 +37,9 @@ import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.InvalidCredentialsException; import org.apache.http.auth.KerberosCredentials; @@ -59,7 +61,7 @@ * @since 4.2 */ @NotThreadSafe -public abstract class GGSSchemeBase extends AuthSchemeBase { +public abstract class GGSSchemeBase extends NonStandardAuthScheme { enum State { UNINITIATED, @@ -70,7 +72,6 @@ enum State { private final Log log = LogFactory.getLog(getClass()); - private final Base64 base64codec; private final boolean stripPort; private final boolean useCanonicalHostname; @@ -82,7 +83,6 @@ enum State { GGSSchemeBase(final boolean stripPort, final boolean useCanonicalHostname) { super(); - this.base64codec = new Base64(0); this.stripPort = stripPort; this.useCanonicalHostname = useCanonicalHostname; this.state = State.UNINITIATED; @@ -96,6 +96,23 @@ enum State { this(true,true); } + public void processChallenge( + final ChallengeType challengeType, + final AuthChallenge authChallenge) throws MalformedChallengeException { + update(challengeType, authChallenge); + if (state == State.UNINITIATED) { + final String challenge = getChallenge(); + token = Base64.decodeBase64(challenge.getBytes()); + if (log.isDebugEnabled()) { + log.debug("Received token '" + token + "' from the auth server"); + } + state = State.CHALLENGE_RECEIVED; + } else { + log.debug("Authentication already attempted"); + state = State.FAILED; + } + } + protected GSSManager getManager() { return GSSManager.getInstance(); } @@ -211,7 +228,8 @@ public Header authenticate( throw new AuthenticationException(gsse.getMessage()); } case TOKEN_GENERATED: - final String tokenstr = new String(base64codec.encode(token)); + final Base64 codec = new Base64(0); + final String tokenstr = new String(codec.encode(token)); if (log.isDebugEnabled()) { log.debug("Sending response '" + tokenstr + "' back to the auth server"); } @@ -229,23 +247,6 @@ public Header authenticate( } } - @Override - protected void parseChallenge( - final CharArrayBuffer buffer, - final int beginIndex, final int endIndex) throws MalformedChallengeException { - final String challenge = buffer.substringTrimmed(beginIndex, endIndex); - if (log.isDebugEnabled()) { - log.debug("Received challenge '" + challenge + "' from the auth server"); - } - if (state == State.UNINITIATED) { - token = Base64.decodeBase64(challenge.getBytes()); - state = State.CHALLENGE_RECEIVED; - } else { - log.debug("Authentication already attempted"); - state = State.FAILED; - } - } - private String resolveCanonicalHostname(final String host) throws UnknownHostException { final InetAddress in = InetAddress.getByName(host); final String canonicalServer = in.getCanonicalHostName(); diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java b/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java index f44778e59..b73f047a4 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java @@ -28,27 +28,40 @@ package org.apache.http.impl.auth; import java.io.IOException; +import java.util.HashMap; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Queue; 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.HttpException; +import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.ParseException; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; 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.config.AuthSchemes; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.message.ParserCursor; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Asserts; +import org.apache.http.util.CharArrayBuffer; /** * @since 4.3 @@ -56,26 +69,39 @@ public class HttpAuthenticator { private final Log log; + private final AuthChallengeParser parser; public HttpAuthenticator(final Log log) { super(); this.log = log != null ? log : LogFactory.getLog(getClass()); + this.parser = new AuthChallengeParser(); } public HttpAuthenticator() { this(null); } - public boolean isAuthenticationRequested( + public boolean updateAuthState( final HttpHost host, + final ChallengeType challengeType, final HttpResponse response, - final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context) { - if (authStrategy.isAuthenticationRequested(host, response, 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); + } + if (response.getStatusLine().getStatusCode() == challengeCode) { this.log.debug("Authentication required"); if (authState.getState() == AuthProtocolState.SUCCESS) { - authStrategy.authFailed(host, authState.getAuthScheme(), context); + clearCache(host, context); } return true; } else { @@ -84,7 +110,7 @@ public boolean isAuthenticationRequested( case HANDSHAKE: this.log.debug("Authentication succeeded"); authState.setState(AuthProtocolState.SUCCESS); - authStrategy.authSucceeded(host, authState.getAuthScheme(), context); + updateCache(host, authState.getAuthScheme(), context); break; case SUCCESS: break; @@ -97,17 +123,55 @@ public boolean isAuthenticationRequested( public boolean handleAuthChallenge( final HttpHost host, + final ChallengeType challengeType, final HttpResponse response, final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context) { + + if (this.log.isDebugEnabled()) { + this.log.debug(host.toHostString() + " requested authentication"); + } try { - if (this.log.isDebugEnabled()) { - this.log.debug(host.toHostString() + " requested authentication"); + final Header[] headers = response.getHeaders( + challengeType == ChallengeType.PROXY ? HttpHeaders.PROXY_AUTHENTICATE : HttpHeaders.WWW_AUTHENTICATE); + final Map challengeMap = new HashMap<>(); + for (Header header: headers) { + final CharArrayBuffer buffer; + final int pos; + if (header instanceof FormattedHeader) { + buffer = ((FormattedHeader) header).getBuffer(); + pos = ((FormattedHeader) header).getValuePos(); + } else { + final String s = header.getValue(); + if (s == null) { + continue; + } + buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + pos = 0; + } + final ParserCursor cursor = new ParserCursor(pos, buffer.length()); + final List authChallenges; + try { + authChallenges = parser.parse(buffer, cursor); + } catch (ParseException ex) { + if (this.log.isWarnEnabled()) { + this.log.warn("Malformed challenge: " + header.getValue()); + } + continue; + } + for (AuthChallenge authChallenge: authChallenges) { + final String scheme = authChallenge.getScheme().toLowerCase(Locale.ROOT); + if (!challengeMap.containsKey(scheme)) { + challengeMap.put(scheme, authChallenge); + } + } } - final Map challenges = authStrategy.getChallenges(host, response, context); - if (challenges.isEmpty()) { - this.log.debug("Response contains no authentication challenges"); + if (challengeMap.isEmpty()) { + this.log.debug("Response contains no valid authentication challenges"); + clearCache(host, context); + authState.reset(); return false; } @@ -122,7 +186,7 @@ public boolean handleAuthChallenge( case HANDSHAKE: if (authScheme == null) { this.log.debug("Auth scheme is null"); - authStrategy.authFailed(host, null, context); + clearCache(host, context); authState.reset(); authState.setState(AuthProtocolState.FAILURE); return false; @@ -130,13 +194,13 @@ public boolean handleAuthChallenge( case UNCHALLENGED: if (authScheme != null) { final String id = authScheme.getSchemeName(); - final Header challenge = challenges.get(id.toLowerCase(Locale.ROOT)); + final AuthChallenge challenge = challengeMap.get(id.toLowerCase(Locale.ROOT)); if (challenge != null) { this.log.debug("Authorization challenge processed"); - authScheme.processChallenge(challenge); + authScheme.processChallenge(challengeType, challenge); if (authScheme.isComplete()) { this.log.debug("Authentication failed"); - authStrategy.authFailed(host, authState.getAuthScheme(), context); + clearCache(host, context); authState.reset(); authState.setState(AuthProtocolState.FAILURE); return false; @@ -150,7 +214,7 @@ public boolean handleAuthChallenge( } } } - final Queue authOptions = authStrategy.select(challenges, host, response, context); + final Queue authOptions = authStrategy.select(challengeType, host, challengeMap, context); if (authOptions != null && !authOptions.isEmpty()) { if (this.log.isDebugEnabled()) { this.log.debug("Selected authentication options: " + authOptions); @@ -180,7 +244,7 @@ public void generateAuthResponse( case FAILURE: return; case SUCCESS: - ensureAuthScheme(authScheme); + Asserts.notNull(authScheme, "AuthScheme"); if (authScheme.isConnectionBased()) { return; } @@ -209,7 +273,7 @@ public void generateAuthResponse( } return; } else { - ensureAuthScheme(authScheme); + Asserts.notNull(authScheme, "AuthScheme"); } } if (authScheme != null) { @@ -224,8 +288,35 @@ public void generateAuthResponse( } } - private void ensureAuthScheme(final AuthScheme authScheme) { - Asserts.notNull(authScheme, "Auth scheme"); + private boolean isCachable(final AuthScheme authScheme) { + final String schemeName = authScheme.getSchemeName(); + return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) || + schemeName.equalsIgnoreCase(AuthSchemes.DIGEST); + } + + private void updateCache(final HttpHost host, final AuthScheme authScheme, final HttpContext context) { + if (isCachable(authScheme)) { + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final AuthCache authCache = clientContext.getAuthCache(); + if (authCache != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Caching '" + authScheme.getSchemeName() + "' auth scheme for " + host); + } + authCache.put(host, authScheme); + } + } + } + + private void clearCache(final HttpHost host, final HttpContext context) { + + final HttpClientContext clientContext = HttpClientContext.adapt(context); + final AuthCache authCache = clientContext.getAuthCache(); + if (authCache != null) { + if (this.log.isDebugEnabled()) { + this.log.debug("Clearing cached auth scheme for " + host); + } + authCache.remove(host); + } } private Header doAuth( diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java index 40deb310b..c787fcc27 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java @@ -26,13 +26,8 @@ */ package org.apache.http.impl.auth; -import org.apache.http.Header; -import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; -import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.Credentials; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.Args; import org.ietf.jgss.GSSException; import org.ietf.jgss.Oid; @@ -66,59 +61,11 @@ public String getSchemeName() { return "Kerberos"; } - /** - * Produces KERBEROS authorization Header based on token created by - * processChallenge. - * - * @param credentials not used by the KERBEROS scheme. - * @param request The request being authenticated - * - * @throws AuthenticationException if authentication string cannot - * be generated due to an authentication failure - * - * @return KERBEROS authentication Header - */ - @Override - public Header authenticate( - final Credentials credentials, - final HttpRequest request, - final HttpContext context) throws AuthenticationException { - return super.authenticate(credentials, request, context); - } - @Override protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { return generateGSSToken(input, new Oid(KERBEROS_OID), authServer, credentials); } - /** - * There are no valid parameters for KERBEROS authentication so this - * method always returns {@code null}. - * - * @return {@code null} - */ - @Override - public String getParameter(final String name) { - Args.notNull(name, "Parameter name"); - return null; - } - - /** - * The concept of an authentication realm is not supported by the Negotiate - * authentication scheme. Always returns {@code null}. - * - * @return {@code null} - */ - @Override - public String getRealm() { - return null; - } - - /** - * Returns {@code true}. KERBEROS authentication scheme is connection based. - * - * @return {@code true}. - */ @Override public boolean isConnectionBased() { return true; diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java index bf448c5be..3e509e71f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java @@ -30,7 +30,9 @@ import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.InvalidCredentialsException; import org.apache.http.auth.MalformedChallengeException; @@ -47,7 +49,7 @@ * @since 4.0 */ @NotThreadSafe -public class NTLMScheme extends AuthSchemeBase { +public class NTLMScheme extends NonStandardAuthScheme { enum State { UNINITIATED, @@ -61,14 +63,12 @@ enum State { private final NTLMEngine engine; private State state; - private String challenge; public NTLMScheme(final NTLMEngine engine) { super(); Args.notNull(engine, "NTLM engine"); this.engine = engine; this.state = State.UNINITIATED; - this.challenge = null; } /** @@ -83,29 +83,18 @@ public String getSchemeName() { return "ntlm"; } - @Override - public String getParameter(final String name) { - // String parameters not supported - return null; - } - - @Override - public String getRealm() { - // NTLM does not support the concept of an authentication realm - return null; - } - @Override public boolean isConnectionBased() { return true; } @Override - protected void parseChallenge( - final CharArrayBuffer buffer, - final int beginIndex, final int endIndex) throws MalformedChallengeException { - this.challenge = buffer.substringTrimmed(beginIndex, endIndex); - if (this.challenge.isEmpty()) { + public void processChallenge( + final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { + Args.notNull(challengeType, "ChallengeType"); + Args.notNull(authChallenge, "AuthChallenge"); + final String value = authChallenge.getValue(); + if (value == null || value.isEmpty()) { if (this.state == State.UNINITIATED) { this.state = State.CHALLENGE_RECEIVED; } else { @@ -126,7 +115,7 @@ public Header authenticate( final Credentials credentials, final HttpRequest request, final HttpContext context) throws AuthenticationException { - NTCredentials ntcredentials = null; + final NTCredentials ntcredentials; try { ntcredentials = (NTCredentials) credentials; } catch (final ClassCastException e) { @@ -134,7 +123,7 @@ public Header authenticate( "Credentials cannot be used for NTLM authentication: " + credentials.getClass().getName()); } - String response = null; + final String response; if (this.state == State.FAILED) { throw new AuthenticationException("NTLM authentication failed"); } else if (this.state == State.CHALLENGE_RECEIVED) { @@ -148,7 +137,7 @@ public Header authenticate( ntcredentials.getPassword(), ntcredentials.getNetbiosDomain(), ntcredentials.getWorkstation(), - this.challenge); + getChallenge()); this.state = State.MSG_TYPE3_GENERATED; } else { throw new AuthenticationException("Unexpected state: " + this.state); diff --git a/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java b/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java similarity index 50% rename from httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java rename to httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java index 49e6e9122..be3b13d2f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java @@ -24,34 +24,47 @@ * . * */ +package org.apache.http.impl.auth; -package org.apache.http.impl.client; +import org.apache.http.auth.AuthChallenge; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.ChallengeType; +import org.apache.http.auth.MalformedChallengeException; -import java.util.Collection; +public abstract class NonStandardAuthScheme implements AuthScheme { -import org.apache.http.HttpStatus; -import org.apache.http.annotation.Immutable; -import org.apache.http.auth.AUTH; -import org.apache.http.client.config.RequestConfig; + private ChallengeType challengeType; + private String challenge; -/** - * Default {@link org.apache.http.client.AuthenticationStrategy} implementation - * for proxy host authentication. - * - * @since 4.2 - */ -@Immutable -public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl { + public boolean isProxy() { + return this.challengeType != null && this.challengeType == ChallengeType.PROXY; + } - public static final ProxyAuthenticationStrategy INSTANCE = new ProxyAuthenticationStrategy(); + protected void update(final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException{ + if (authChallenge.getValue() == null) { + throw new MalformedChallengeException("Missing auth challenge"); + } + this.challengeType = challengeType; + this.challenge = authChallenge.getValue(); + } - public ProxyAuthenticationStrategy() { - super(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, AUTH.PROXY_AUTH); + protected String getChallenge() { + return this.challenge; } @Override - Collection getPreferredAuthSchemes(final RequestConfig config) { - return config.getProxyPreferredAuthSchemes(); + public String getParameter(final String name) { + return null; + } + + @Override + public String getRealm() { + return null; + } + + @Override + public String toString() { + return getSchemeName() + "(" + this.challengeType + ") " + this.challenge; } } diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java deleted file mode 100644 index a241a09e8..000000000 --- a/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java +++ /dev/null @@ -1,156 +0,0 @@ -/* - * ==================================================================== - * 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.auth; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.ObjectStreamException; -import java.io.Serializable; -import java.nio.charset.Charset; -import java.util.HashMap; -import java.util.Locale; -import java.util.Map; - -import org.apache.http.Consts; -import org.apache.http.HeaderElement; -import org.apache.http.HttpRequest; -import org.apache.http.annotation.NotThreadSafe; -import org.apache.http.auth.ChallengeState; -import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.message.BasicHeaderValueParser; -import org.apache.http.message.HeaderValueParser; -import org.apache.http.message.ParserCursor; -import org.apache.http.util.CharArrayBuffer; -import org.apache.http.util.CharsetUtils; - -/** - * Abstract authentication scheme class that lays foundation for all - * RFC 2617 compliant authentication schemes and provides capabilities common - * to all authentication schemes defined in RFC 2617. - * - * @since 4.0 - */ -@NotThreadSafe // AuthSchemeBase, params -public abstract class RFC2617Scheme extends AuthSchemeBase implements Serializable { - - private static final long serialVersionUID = -2845454858205884623L; - - private final Map params; - private transient Charset credentialsCharset; - - /** - * @since 4.3 - */ - public RFC2617Scheme(final Charset credentialsCharset) { - super(); - this.params = new HashMap<>(); - this.credentialsCharset = credentialsCharset != null ? credentialsCharset : Consts.ASCII; - } - - public RFC2617Scheme() { - this(Consts.ASCII); - } - - - /** - * @since 4.3 - */ - public Charset getCredentialsCharset() { - return credentialsCharset != null ? credentialsCharset : Consts.ASCII; - } - - String getCredentialsCharset(final HttpRequest request) { - return getCredentialsCharset().name(); - } - - @Override - protected void parseChallenge( - final CharArrayBuffer buffer, final int pos, final int len) throws MalformedChallengeException { - final HeaderValueParser parser = BasicHeaderValueParser.INSTANCE; - final ParserCursor cursor = new ParserCursor(pos, buffer.length()); - final HeaderElement[] elements = parser.parseElements(buffer, cursor); - this.params.clear(); - for (final HeaderElement element : elements) { - this.params.put(element.getName().toLowerCase(Locale.ROOT), element.getValue()); - } - } - - /** - * Returns authentication parameters map. Keys in the map are lower-cased. - * - * @return the map of authentication parameters - */ - protected Map getParameters() { - return this.params; - } - - /** - * Returns authentication parameter with the given name, if available. - * - * @param name The name of the parameter to be returned - * - * @return the parameter with the given name - */ - @Override - public String getParameter(final String name) { - if (name == null) { - return null; - } - return this.params.get(name.toLowerCase(Locale.ROOT)); - } - - /** - * Returns authentication realm. The realm may not be null. - * - * @return the authentication realm - */ - @Override - public String getRealm() { - return getParameter("realm"); - } - - private void writeObject(final ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeUTF(this.credentialsCharset.name()); - out.writeObject(this.challengeState); - } - - @SuppressWarnings("unchecked") - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - this.credentialsCharset = CharsetUtils.get(in.readUTF()); - if (this.credentialsCharset == null) { - this.credentialsCharset = Consts.ASCII; - } - this.challengeState = (ChallengeState) in.readObject(); - } - - private void readObjectNoData() throws ObjectStreamException { - } - -} diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java index a16e43bf8..5d479264a 100644 --- a/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java +++ b/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java @@ -26,13 +26,8 @@ */ package org.apache.http.impl.auth; -import org.apache.http.Header; -import org.apache.http.HttpRequest; import org.apache.http.annotation.NotThreadSafe; -import org.apache.http.auth.AuthenticationException; import org.apache.http.auth.Credentials; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.Args; import org.ietf.jgss.GSSException; import org.ietf.jgss.Oid; @@ -67,59 +62,11 @@ public String getSchemeName() { return "Negotiate"; } - /** - * Produces SPNEGO authorization Header based on token created by - * processChallenge. - * - * @param credentials not used by the SPNEGO scheme. - * @param request The request being authenticated - * - * @throws AuthenticationException if authentication string cannot - * be generated due to an authentication failure - * - * @return SPNEGO authentication Header - */ - @Override - public Header authenticate( - final Credentials credentials, - final HttpRequest request, - final HttpContext context) throws AuthenticationException { - return super.authenticate(credentials, request, context); - } - @Override protected byte[] generateToken(final byte[] input, final String authServer, final Credentials credentials) throws GSSException { return generateGSSToken(input, new Oid(SPNEGO_OID), authServer, credentials); } - /** - * There are no valid parameters for SPNEGO authentication so this - * method always returns {@code null}. - * - * @return {@code null} - */ - @Override - public String getParameter(final String name) { - Args.notNull(name, "Parameter name"); - return null; - } - - /** - * The concept of an authentication realm is not supported by the Negotiate - * authentication scheme. Always returns {@code null}. - * - * @return {@code null} - */ - @Override - public String getRealm() { - return null; - } - - /** - * Returns {@code true}. SPNEGO authentication scheme is connection based. - * - * @return {@code true}. - */ @Override public boolean isConnectionBased() { return true; diff --git a/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java b/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java new file mode 100644 index 000000000..bcab410d1 --- /dev/null +++ b/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * 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.auth; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import org.apache.http.NameValuePair; +import org.apache.http.annotation.NotThreadSafe; +import org.apache.http.auth.AuthChallenge; +import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.ChallengeType; +import org.apache.http.auth.MalformedChallengeException; + +/** + * Abstract authentication scheme class that lays foundation for standard HTTP authentication schemes and + * provides capabilities common to all authentication schemes defined in the HTTP specification. + * + * @since 4.0 + */ +@NotThreadSafe +public abstract class StandardAuthScheme implements AuthScheme, Serializable { + + private static final long serialVersionUID = -2845454858205884623L; + + private final Map paramMap; + private ChallengeType challengeType; + + /** + * @since 4.3 + */ + public StandardAuthScheme() { + super(); + this.paramMap = new LinkedHashMap<>(); + } + + protected void update(final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { + final List params = authChallenge.getParams(); + this.challengeType = challengeType; + if (params != null) { + for (NameValuePair param: params) { + this.paramMap.put(param.getName().toLowerCase(Locale.ROOT), param.getValue()); + } + } + } + + @Override + public String getRealm() { + return getParameter("realm"); + } + + protected Map getParameters() { + return this.paramMap; + } + + /** + * Returns authentication parameter with the given name, if available. + * + * @param name The name of the parameter to be returned + * + * @return the parameter with the given name + */ + @Override + public String getParameter(final String name) { + if (name == null) { + return null; + } + return this.paramMap.get(name.toLowerCase(Locale.ROOT)); + } + + /** + * Returns {@code true} if authenticating against a proxy, {@code false} + * otherwise. + */ + public boolean isProxy() { + return this.challengeType != null && this.challengeType == ChallengeType.PROXY; + } + + @Override + public String toString() { + return getSchemeName() + "(" + this.challengeType + ") " + this.paramMap; + } + +} diff --git a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java b/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java similarity index 50% rename from httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java rename to httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java index a6a65b5c9..70922b41f 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java @@ -30,7 +30,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -39,34 +38,37 @@ 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.AuthChallenge; import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthScope; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.client.AuthCache; import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Lookup; -import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; -import org.apache.http.util.CharArrayBuffer; +/** + * Default implementation of {@link AuthenticationStrategy} + * + * @since 5.0 + */ @Immutable -abstract class AuthenticationStrategyImpl implements AuthenticationStrategy { +public class DefaultAuthenticationStrategy implements AuthenticationStrategy { private final Log log = LogFactory.getLog(getClass()); + public static final DefaultAuthenticationStrategy INSTANCE = new DefaultAuthenticationStrategy(); + private static final List DEFAULT_SCHEME_PRIORITY = Collections.unmodifiableList(Arrays.asList( AuthSchemes.SPNEGO, @@ -75,82 +77,15 @@ abstract class AuthenticationStrategyImpl implements AuthenticationStrategy { AuthSchemes.DIGEST, AuthSchemes.BASIC)); - private final int challengeCode; - private final String headerName; - - /** - * @param challengeCode for example SC_PROXY_AUTHENTICATION_REQUIRED or SC_UNAUTHORIZED - * @param headerName for example "Proxy-Authenticate" or "WWW-Authenticate" - */ - AuthenticationStrategyImpl(final int challengeCode, final String headerName) { - super(); - this.challengeCode = challengeCode; - this.headerName = headerName; - } - - @Override - public boolean isAuthenticationRequested( - final HttpHost authhost, - final HttpResponse response, - final HttpContext context) { - Args.notNull(response, "HTTP response"); - final int status = response.getStatusLine().getStatusCode(); - return status == this.challengeCode; - } - - /** - * Generates a map of challenge auth-scheme => Header entries. - * - * @return map: key=lower-cased auth-scheme name, value=Header that contains the challenge - */ - @Override - public Map getChallenges( - final HttpHost authhost, - final HttpResponse response, - final HttpContext context) throws MalformedChallengeException { - Args.notNull(response, "HTTP response"); - final Header[] headers = response.getHeaders(this.headerName); - final Map map = new HashMap<>(headers.length); - for (final Header header : headers) { - final CharArrayBuffer buffer; - int pos; - if (header instanceof FormattedHeader) { - buffer = ((FormattedHeader) header).getBuffer(); - pos = ((FormattedHeader) header).getValuePos(); - } else { - final 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++; - } - final int beginIndex = pos; - while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { - pos++; - } - final int endIndex = pos; - final String s = buffer.substring(beginIndex, endIndex); - map.put(s.toLowerCase(Locale.ROOT), header); - } - return map; - } - - abstract Collection getPreferredAuthSchemes(RequestConfig config); - @Override public Queue select( - final Map challenges, + final ChallengeType challengeType, final HttpHost authhost, - final HttpResponse response, + final Map challenges, final HttpContext context) throws MalformedChallengeException { + Args.notNull(challengeType, "ChallengeType"); Args.notNull(challenges, "Map of auth challenges"); Args.notNull(authhost, "Host"); - Args.notNull(response, "HTTP response"); Args.notNull(context, "HTTP context"); final HttpClientContext clientContext = HttpClientContext.adapt(context); @@ -166,7 +101,8 @@ public Queue select( return options; } final RequestConfig config = clientContext.getRequestConfig(); - Collection authPrefs = getPreferredAuthSchemes(config); + Collection authPrefs = challengeType == ChallengeType.TARGET ? + config.getTargetPreferredAuthSchemes() : config.getProxyPreferredAuthSchemes(); if (authPrefs == null) { authPrefs = DEFAULT_SCHEME_PRIORITY; } @@ -175,7 +111,7 @@ public Queue select( } for (final String id: authPrefs) { - final Header challenge = challenges.get(id.toLowerCase(Locale.ROOT)); + final AuthChallenge challenge = challenges.get(id.toLowerCase(Locale.ROOT)); if (challenge != null) { final AuthSchemeProvider authSchemeProvider = registry.lookup(id); if (authSchemeProvider == null) { @@ -186,7 +122,7 @@ public Queue select( continue; } final AuthScheme authScheme = authSchemeProvider.create(context); - authScheme.processChallenge(challenge); + authScheme.processChallenge(challengeType, challenge); final AuthScope authScope = new AuthScope( authhost.getHostName(), @@ -208,53 +144,4 @@ public Queue select( return options; } - @Override - public void authSucceeded( - final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { - Args.notNull(authhost, "Host"); - Args.notNull(authScheme, "Auth scheme"); - Args.notNull(context, "HTTP context"); - - final HttpClientContext clientContext = HttpClientContext.adapt(context); - - if (isCachable(authScheme)) { - AuthCache authCache = clientContext.getAuthCache(); - if (authCache == null) { - authCache = new BasicAuthCache(); - clientContext.setAuthCache(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; - } - final String schemeName = authScheme.getSchemeName(); - return schemeName.equalsIgnoreCase(AuthSchemes.BASIC) || - schemeName.equalsIgnoreCase(AuthSchemes.DIGEST); - } - - @Override - public void authFailed( - final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) { - Args.notNull(authhost, "Host"); - Args.notNull(context, "HTTP context"); - - final HttpClientContext clientContext = HttpClientContext.adapt(context); - - final AuthCache authCache = clientContext.getAuthCache(); - if (authCache != null) { - if (this.log.isDebugEnabled()) { - this.log.debug("Clearing cached auth scheme for " + authhost); - } - authCache.remove(authhost); - } - } - } diff --git a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java index 9b59a4f6b..1e4dd603e 100644 --- a/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java +++ b/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java @@ -949,11 +949,11 @@ public CloseableHttpClient build() { } AuthenticationStrategy targetAuthStrategyCopy = this.targetAuthStrategy; if (targetAuthStrategyCopy == null) { - targetAuthStrategyCopy = TargetAuthenticationStrategy.INSTANCE; + targetAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } AuthenticationStrategy proxyAuthStrategyCopy = this.proxyAuthStrategy; if (proxyAuthStrategyCopy == null) { - proxyAuthStrategyCopy = ProxyAuthenticationStrategy.INSTANCE; + proxyAuthStrategyCopy = DefaultAuthenticationStrategy.INSTANCE; } UserTokenHandler userTokenHandlerCopy = this.userTokenHandler; if (userTokenHandlerCopy == null) { 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 f876167a0..dc2dc3178 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 @@ -41,7 +41,9 @@ import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; +import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.protocol.HttpClientContext; @@ -86,7 +88,7 @@ public class ProxyClient { private final RequestConfig requestConfig; private final HttpProcessor httpProcessor; private final HttpRequestExecutor requestExec; - private final ProxyAuthenticationStrategy proxyAuthStrategy; + private final AuthenticationStrategy proxyAuthStrategy; private final HttpAuthenticator authenticator; private final AuthState proxyAuthState; private final Lookup authSchemeRegistry; @@ -106,7 +108,7 @@ public ProxyClient( this.httpProcessor = new ImmutableHttpProcessor( new RequestTargetHost(), new RequestClientConnControl(), new RequestUserAgent()); this.requestExec = new HttpRequestExecutor(); - this.proxyAuthStrategy = new ProxyAuthenticationStrategy(); + this.proxyAuthStrategy = new DefaultAuthenticationStrategy(); this.authenticator = new HttpAuthenticator(); this.proxyAuthState = new AuthState(); this.authSchemeRegistry = RegistryBuilder.create() @@ -184,9 +186,8 @@ public Socket tunnel( throw new HttpException("Unexpected response to CONNECT request: " + response.getStatusLine()); } - if (this.authenticator.isAuthenticationRequested(proxy, response, - this.proxyAuthStrategy, this.proxyAuthState, context)) { - if (this.authenticator.handleAuthChallenge(proxy, response, + if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, this.proxyAuthState, context)) { + if (this.authenticator.handleAuthChallenge(proxy, ChallengeType.PROXY, response, this.proxyAuthStrategy, this.proxyAuthState, context)) { // Retry request if (this.reuseStrategy.keepAlive(response, context)) { 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 deleted file mode 100644 index dad2eb712..000000000 --- a/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * ==================================================================== - * 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.Collection; - -import org.apache.http.HttpStatus; -import org.apache.http.annotation.Immutable; -import org.apache.http.auth.AUTH; -import org.apache.http.client.config.RequestConfig; - -/** - * Default {@link org.apache.http.client.AuthenticationStrategy} implementation - * for proxy host authentication. - * - * @since 4.2 - */ -@Immutable -public class TargetAuthenticationStrategy extends AuthenticationStrategyImpl { - - public static final TargetAuthenticationStrategy INSTANCE = new TargetAuthenticationStrategy(); - - public TargetAuthenticationStrategy() { - super(HttpStatus.SC_UNAUTHORIZED, AUTH.WWW_AUTH); - } - - @Override - Collection getPreferredAuthSchemes(final RequestConfig config) { - return config.getTargetPreferredAuthSchemes(); - } - -} diff --git a/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java b/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java index a3f5c988d..7bf79fcb0 100644 --- a/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java +++ b/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java @@ -46,6 +46,7 @@ import org.apache.http.auth.AUTH; import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthState; +import org.apache.http.auth.ChallengeType; import org.apache.http.client.AuthenticationStrategy; import org.apache.http.client.NonRepeatableRequestException; import org.apache.http.client.UserTokenHandler; @@ -465,9 +466,9 @@ private boolean createTunnelToTarget( } if (config.isAuthenticationEnabled()) { - if (this.authenticator.isAuthenticationRequested(proxy, response, - this.proxyAuthStrategy, proxyAuthState, context)) { - if (this.authenticator.handleAuthChallenge(proxy, response, + if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response, + proxyAuthState, context)) { + if (this.authenticator.handleAuthChallenge(proxy, ChallengeType.PROXY, response, this.proxyAuthStrategy, proxyAuthState, context)) { // Retry request if (this.reuseStrategy.keepAlive(response, context)) { @@ -546,23 +547,23 @@ private boolean needAuthentication( route.getTargetHost().getPort(), target.getSchemeName()); } - final boolean targetAuthRequested = this.authenticator.isAuthenticationRequested( - target, response, this.targetAuthStrategy, targetAuthState, context); + final boolean targetAuthRequested = this.authenticator.updateAuthState( + target, ChallengeType.TARGET, response, targetAuthState, context); HttpHost proxy = route.getProxyHost(); // if proxy is not set use target host instead if (proxy == null) { proxy = route.getTargetHost(); } - final boolean proxyAuthRequested = this.authenticator.isAuthenticationRequested( - proxy, response, this.proxyAuthStrategy, proxyAuthState, context); + final boolean proxyAuthRequested = this.authenticator.updateAuthState( + proxy, ChallengeType.PROXY, response, proxyAuthState, context); if (targetAuthRequested) { - return this.authenticator.handleAuthChallenge(target, response, + return this.authenticator.handleAuthChallenge(target, ChallengeType.TARGET, response, this.targetAuthStrategy, targetAuthState, context); } if (proxyAuthRequested) { - return this.authenticator.handleAuthChallenge(proxy, response, + return this.authenticator.handleAuthChallenge(proxy, ChallengeType.PROXY, response, this.proxyAuthStrategy, proxyAuthState, context); } } diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestAuthChallengeParser.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestAuthChallengeParser.java index 65984d9ac..ac7259452 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestAuthChallengeParser.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestAuthChallengeParser.java @@ -58,6 +58,17 @@ public void testParseBasicToken() throws Exception { Assert.assertEquals(null, nvp.getValue()); } + @Test + public void testParseTokenWithBlank() throws Exception { + final CharArrayBuffer buffer = new CharArrayBuffer(64); + buffer.append("blah "); + final ParserCursor cursor = new ParserCursor(0, buffer.length()); + final NameValuePair nvp = parser.parseTokenOrParameter(buffer, cursor); + Assert.assertNotNull(nvp); + Assert.assertEquals("blah", nvp.getName()); + Assert.assertEquals(null, nvp.getValue()); + } + @Test public void testParseTokenWithBlanks() throws Exception { final CharArrayBuffer buffer = new CharArrayBuffer(64); diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java index 8b5d05737..99c3ad77e 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java @@ -30,18 +30,22 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.List; import org.apache.commons.codec.binary.Base64; import org.apache.http.Consts; import org.apache.http.Header; import org.apache.http.HttpRequest; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthScheme; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpRequest; +import org.apache.http.message.ParserCursor; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HttpContext; +import org.apache.http.util.CharArrayBuffer; import org.apache.http.util.EncodingUtils; import org.junit.Assert; import org.junit.Test; @@ -51,12 +55,21 @@ */ public class TestBasicScheme { + private static AuthChallenge parse(final String s) { + final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + final ParserCursor cursor = new ParserCursor(0, buffer.length()); + final List authChallenges = AuthChallengeParser.INSTANCE.parse(buffer, cursor); + Assert.assertEquals(1, authChallenges.size()); + return authChallenges.get(0); + } + @Test public void testBasicAuthenticationEmptyChallenge() throws Exception { final String challenge = "Basic"; - final Header header = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final AuthScheme authscheme = new BasicScheme(); - authscheme.processChallenge(header); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertNull(authscheme.getRealm()); } @@ -79,13 +92,12 @@ public void testBasicAuthenticationWith88591Chars() throws Exception { @Test public void testBasicAuthentication() throws Exception { - final UsernamePasswordCredentials creds = - new UsernamePasswordCredentials("testuser", "testpass"); + final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass"); - final Header challenge = new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\""); + final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final BasicScheme authscheme = new BasicScheme(); - authscheme.processChallenge(challenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); @@ -102,13 +114,12 @@ public void testBasicAuthentication() throws Exception { @Test public void testBasicProxyAuthentication() throws Exception { - final UsernamePasswordCredentials creds = - new UsernamePasswordCredentials("testuser", "testpass"); + final UsernamePasswordCredentials creds = new UsernamePasswordCredentials("testuser", "testpass"); - final Header challenge = new BasicHeader(AUTH.PROXY_AUTH, "Basic realm=\"test\""); + final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final BasicScheme authscheme = new BasicScheme(); - authscheme.processChallenge(challenge); + authscheme.processChallenge(ChallengeType.PROXY, authChallenge); final HttpRequest request = new BasicHttpRequest("GET", "/"); final HttpContext context = new BasicHttpContext(); @@ -125,10 +136,10 @@ public void testBasicProxyAuthentication() throws Exception { @Test public void testSerialization() throws Exception { - final Header challenge = new BasicHeader(AUTH.PROXY_AUTH, "Basic realm=\"test\""); + final AuthChallenge authChallenge = parse("Basic realm=\"test\""); final BasicScheme basicScheme = new BasicScheme(); - basicScheme.processChallenge(challenge); + basicScheme.processChallenge(ChallengeType.PROXY, authChallenge); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(buffer); diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java index f72f9d14b..351f8b151 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java @@ -33,27 +33,30 @@ import java.io.ObjectOutputStream; import java.security.MessageDigest; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.http.Header; import org.apache.http.HeaderElement; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpRequest; -import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.entity.InputStreamEntity; import org.apache.http.entity.StringEntity; -import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHeaderValueParser; import org.apache.http.message.BasicHttpEntityEnclosingRequest; import org.apache.http.message.BasicHttpRequest; +import org.apache.http.message.ParserCursor; import org.apache.http.protocol.BasicHttpContext; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; +import org.apache.http.util.CharArrayBuffer; import org.junit.Assert; import org.junit.Test; @@ -62,29 +65,38 @@ */ public class TestDigestScheme { + private static AuthChallenge parse(final String s) { + final CharArrayBuffer buffer = new CharArrayBuffer(s.length()); + buffer.append(s); + final ParserCursor cursor = new ParserCursor(0, buffer.length()); + final List authChallenges = AuthChallengeParser.INSTANCE.parse(buffer, cursor); + Assert.assertEquals(1, authChallenges.size()); + return authChallenges.get(0); + } + @Test(expected=MalformedChallengeException.class) public void testDigestAuthenticationEmptyChallenge1() throws Exception { - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest"); + final AuthChallenge authChallenge = parse("Digest"); final AuthScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); } @Test(expected=MalformedChallengeException.class) public void testDigestAuthenticationEmptyChallenge2() throws Exception { - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, "Digest "); + final AuthChallenge authChallenge = parse("Digest "); final AuthScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); } @Test public void testDigestAuthenticationWithDefaultCreds() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertTrue(authscheme.isComplete()); Assert.assertFalse(authscheme.isConnectionBased()); @@ -100,12 +112,12 @@ public void testDigestAuthenticationWithDefaultCreds() throws Exception { @Test public void testDigestAuthentication() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); final Map table = parseAuthResponse(authResponse); @@ -119,12 +131,12 @@ public void testDigestAuthentication() throws Exception { @Test public void testDigestAuthenticationInvalidInput() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); try { authscheme.authenticate(null, request, context); Assert.fail("IllegalArgumentException should have been thrown"); @@ -137,37 +149,17 @@ public void testDigestAuthenticationInvalidInput() throws Exception { } } - @Test - public void testDigestAuthenticationOverrideParameter() throws Exception { - final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); - final HttpRequest request = new BasicHttpRequest("Simple", "/"); - final Credentials cred = new UsernamePasswordCredentials("username","password"); - final DigestScheme authscheme = new DigestScheme(); - final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); - authscheme.overrideParamter("realm", "other realm"); - final Header authResponse = authscheme.authenticate(cred, request, context); - - final Map table = parseAuthResponse(authResponse); - Assert.assertEquals("username", table.get("username")); - Assert.assertEquals("other realm", table.get("realm")); - Assert.assertEquals("/", table.get("uri")); - Assert.assertEquals("f2a3f18799759d4f1a1c068b92b573cb", table.get("nonce")); - Assert.assertEquals("3f211de10463cbd055ab4cd9c5158eac", table.get("response")); - } - @Test public void testDigestAuthenticationWithSHA() throws Exception { final String challenge = "Digest realm=\"realm1\", " + "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "algorithm=SHA"; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); final Map table = parseAuthResponse(authResponse); @@ -181,12 +173,12 @@ public void testDigestAuthenticationWithSHA() throws Exception { @Test public void testDigestAuthenticationWithQueryStringInDigestURI() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("Simple", "/?param=value"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); final Map table = parseAuthResponse(authResponse); @@ -204,11 +196,11 @@ public void testDigestAuthenticationWithMultipleRealms() throws Exception { final Credentials cred = new UsernamePasswordCredentials("username","password"); final Credentials cred2 = new UsernamePasswordCredentials("uname2","password2"); - Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge1); + final AuthChallenge authChallenge1 = parse(challenge1); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge1); Header authResponse = authscheme.authenticate(cred, request, context); Map table = parseAuthResponse(authResponse); @@ -218,9 +210,9 @@ public void testDigestAuthenticationWithMultipleRealms() throws Exception { Assert.assertEquals("abcde", table.get("nonce")); Assert.assertEquals("786f500303eac1478f3c2865e676ed68", table.get("response")); - authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge2); + final AuthChallenge authChallenge2 = parse(challenge2); final DigestScheme authscheme2 = new DigestScheme(); - authscheme2.processChallenge(authChallenge); + authscheme2.processChallenge(ChallengeType.TARGET, authChallenge2); authResponse = authscheme2.authenticate(cred2, request, context); table = parseAuthResponse(authResponse); @@ -234,10 +226,10 @@ public void testDigestAuthenticationWithMultipleRealms() throws Exception { @Test(expected=AuthenticationException.class) public void testDigestAuthenticationNoRealm() throws Exception { final String challenge = "Digest no-realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpRequest request = new BasicHttpRequest("Simple", "/"); @@ -247,10 +239,10 @@ public void testDigestAuthenticationNoRealm() throws Exception { @Test(expected=AuthenticationException.class) public void testDigestAuthenticationNoNonce() throws Exception { final String challenge = "Digest realm=\"realm1\", no-nonce=\"f2a3f18799759d4f1a1c068b92b573cb\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpRequest request = new BasicHttpRequest("Simple", "/"); @@ -276,14 +268,14 @@ public void testDigestAuthenticationMD5Sess() throws Exception { + "algorithm=MD5-sess, " + "qop=\"auth,auth-int\""; // we pass both but expect auth to be used - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final Credentials cred = new UsernamePasswordCredentials(username, password); final HttpRequest request = new BasicHttpRequest("Simple", "/"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); final String response = authResponse.getValue(); @@ -322,7 +314,7 @@ public void testDigestAuthenticationMD5SessNoQop() throws Exception { + "stale=false, " + "algorithm=MD5-sess"; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final Credentials cred = new UsernamePasswordCredentials(username, password); @@ -330,7 +322,7 @@ public void testDigestAuthenticationMD5SessNoQop() throws Exception { final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); final Map table = parseAuthResponse(authResponse); @@ -365,10 +357,10 @@ public void testDigestAuthenticationMD5SessUnknownQop() throws Exception { + "algorithm=MD5-sess, " + "qop=\"stuff\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Credentials cred = new UsernamePasswordCredentials(username, password); final HttpRequest request = new BasicHttpRequest("Simple", "/"); @@ -395,10 +387,10 @@ public void testDigestAuthenticationUnknownAlgo() throws Exception { + "algorithm=stuff, " + "qop=\"auth\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Credentials cred = new UsernamePasswordCredentials(username, password); final HttpRequest request = new BasicHttpRequest("Simple", "/"); @@ -410,9 +402,9 @@ public void testDigestAuthenticationUnknownAlgo() throws Exception { public void testDigestAuthenticationWithStaleNonce() throws Exception { final String challenge = "Digest realm=\"realm1\", " + "nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", stale=\"true\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final AuthScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertFalse(authscheme.isComplete()); } @@ -433,12 +425,12 @@ private static Map parseAuthResponse(final Header authResponse) @Test public void testDigestNouceCount() throws Exception { final String challenge1 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; - final Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1); + final AuthChallenge authChallenge1 = parse(challenge1); final HttpRequest request = new BasicHttpRequest("GET", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge1); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge1); final Header authResponse1 = authscheme.authenticate(cred, request, context); final Map table1 = parseAuthResponse(authResponse1); Assert.assertEquals("00000001", table1.get("nc")); @@ -446,14 +438,14 @@ public void testDigestNouceCount() throws Exception { final Map table2 = parseAuthResponse(authResponse2); Assert.assertEquals("00000002", table2.get("nc")); final String challenge2 = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", qop=auth"; - final Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2); - authscheme.processChallenge(authChallenge2); + final AuthChallenge authChallenge2 = parse(challenge2); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge2); final Header authResponse3 = authscheme.authenticate(cred, request, context); final Map table3 = parseAuthResponse(authResponse3); Assert.assertEquals("00000003", table3.get("nc")); final String challenge3 = "Digest realm=\"realm1\", nonce=\"e273f1776275974f1a120d8b92c5b3cb\", qop=auth"; - final Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3); - authscheme.processChallenge(authChallenge3); + final AuthChallenge authChallenge3 = parse(challenge3); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge3); final Header authResponse4 = authscheme.authenticate(cred, request, context); final Map table4 = parseAuthResponse(authResponse4); Assert.assertEquals("00000001", table4.get("nc")); @@ -463,12 +455,12 @@ public void testDigestNouceCount() throws Exception { public void testDigestMD5SessA1AndCnonceConsistency() throws Exception { final String challenge1 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + "charset=utf-8, realm=\"subnet.domain.com\""; - final Header authChallenge1 = new BasicHeader(AUTH.WWW_AUTH, challenge1); + final AuthChallenge authChallenge1 = parse(challenge1); final HttpRequest request = new BasicHttpRequest("GET", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final HttpContext context = new BasicHttpContext(); final DigestScheme authscheme = new DigestScheme(); - authscheme.processChallenge(authChallenge1); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge1); final Header authResponse1 = authscheme.authenticate(cred, request, context); final Map table1 = parseAuthResponse(authResponse1); Assert.assertEquals("00000001", table1.get("nc")); @@ -486,8 +478,8 @@ public void testDigestMD5SessA1AndCnonceConsistency() throws Exception { final String challenge2 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"1234567890abcdef\", " + "charset=utf-8, realm=\"subnet.domain.com\""; - final Header authChallenge2 = new BasicHeader(AUTH.WWW_AUTH, challenge2); - authscheme.processChallenge(authChallenge2); + final AuthChallenge authChallenge2 = parse(challenge2); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge2); final Header authResponse3 = authscheme.authenticate(cred, request, context); final Map table3 = parseAuthResponse(authResponse3); Assert.assertEquals("00000003", table3.get("nc")); @@ -500,8 +492,8 @@ public void testDigestMD5SessA1AndCnonceConsistency() throws Exception { final String challenge3 = "Digest qop=\"auth\", algorithm=MD5-sess, nonce=\"fedcba0987654321\", " + "charset=utf-8, realm=\"subnet.domain.com\""; - final Header authChallenge3 = new BasicHeader(AUTH.WWW_AUTH, challenge3); - authscheme.processChallenge(authChallenge3); + final AuthChallenge authChallenge3 = parse(challenge3); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge3); final Header authResponse4 = authscheme.authenticate(cred, request, context); final Map table4 = parseAuthResponse(authResponse4); Assert.assertEquals("00000001", table4.get("nc")); @@ -543,13 +535,13 @@ public void testHttpEntityDigest() throws Exception { public void testDigestAuthenticationQopAuthInt() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/"); request.setEntity(new StringEntity("abc\u00e4\u00f6\u00fcabc", HTTP.DEF_CONTENT_CHARSET)); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertEquals("Post:/:acd2b59cd01c7737d8069015584c6cac", authscheme.getA2()); @@ -566,12 +558,12 @@ public void testDigestAuthenticationQopAuthInt() throws Exception { public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpEntityEnclosingRequest("Post", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertEquals("Post:/:d41d8cd98f00b204e9800998ecf8427e", authscheme.getA2()); @@ -588,13 +580,13 @@ public void testDigestAuthenticationQopAuthIntNullEntity() throws Exception { public void testDigestAuthenticationQopAuthOrAuthIntNonRepeatableEntity() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/"); request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1)); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); final Header authResponse = authscheme.authenticate(cred, request, context); Assert.assertEquals("Post:/", authscheme.getA2()); @@ -612,12 +604,12 @@ public void testParameterCaseSensitivity() throws Exception { final String challenge = "Digest Realm=\"-\", " + "nonce=\"YjYuNGYyYmJhMzUuY2I5ZDhlZDE5M2ZlZDM 1Mjk3NGJkNTIyYjgyNTcwMjQ=\", " + "opaque=\"98700A3D9CE17065E2246B41035C6609\", qop=\"auth\""; - final Header authChallenge = new BasicHeader(AUTH.PROXY_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpRequest request = new BasicHttpRequest("GET", "/"); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); Assert.assertEquals("-", authscheme.getRealm()); authscheme.authenticate(cred, request, context); @@ -627,13 +619,13 @@ public void testParameterCaseSensitivity() throws Exception { public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth-int\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("Post", "/"); request.setEntity(new InputStreamEntity(new ByteArrayInputStream(new byte[] {'a'}), -1)); final Credentials cred = new UsernamePasswordCredentials("username","password"); final DigestScheme authscheme = new DigestScheme(); final HttpContext context = new BasicHttpContext(); - authscheme.processChallenge(authChallenge); + authscheme.processChallenge(ChallengeType.TARGET, authChallenge); authscheme.authenticate(cred, request, context); } @@ -641,9 +633,9 @@ public void testDigestAuthenticationQopIntOnlyNonRepeatableEntity() throws Excep public void testSerialization() throws Exception { final String challenge = "Digest realm=\"realm1\", nonce=\"f2a3f18799759d4f1a1c068b92b573cb\", " + "qop=\"auth,auth-int\""; - final Header authChallenge = new BasicHeader(AUTH.WWW_AUTH, challenge); + final AuthChallenge authChallenge = parse(challenge); final DigestScheme digestScheme = new DigestScheme(); - digestScheme.processChallenge(authChallenge); + digestScheme.processChallenge(ChallengeType.TARGET, authChallenge); final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); final ObjectOutputStream out = new ObjectOutputStream(buffer); diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestHttpAuthenticator.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestHttpAuthenticator.java index db3b5c16e..227c30f25 100644 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestHttpAuthenticator.java +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestHttpAuthenticator.java @@ -26,11 +26,10 @@ */ package org.apache.http.impl.auth; -import java.util.HashMap; import java.util.LinkedList; import java.util.Queue; -import org.apache.http.Header; +import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; @@ -44,15 +43,14 @@ import org.apache.http.auth.AuthScope; import org.apache.http.auth.AuthState; import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; 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.HttpClientContext; import org.apache.http.config.Lookup; import org.apache.http.config.RegistryBuilder; import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.TargetAuthenticationStrategy; +import org.apache.http.impl.client.DefaultAuthenticationStrategy; import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpRequest; import org.apache.http.message.BasicHttpResponse; @@ -67,7 +65,6 @@ @SuppressWarnings({"boxing","static-access"}) public class TestHttpAuthenticator { - private AuthenticationStrategy defltAuthStrategy; private AuthState authState; private AuthScheme authScheme; private HttpContext context; @@ -80,7 +77,6 @@ public class TestHttpAuthenticator { @Before public void setUp() throws Exception { - this.defltAuthStrategy = Mockito.mock(AuthenticationStrategy.class); this.authState = new AuthState(); this.authScheme = Mockito.mock(AuthScheme.class); Mockito.when(this.authScheme.getSchemeName()).thenReturn("Basic"); @@ -103,86 +99,61 @@ public void setUp() throws Exception { } @Test - public void testAuthenticationRequested() throws Exception { + public void testUpdateAuthState() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); - Mockito.when(this.defltAuthStrategy.isAuthenticationRequested( - Mockito.any(HttpHost.class), - Mockito.any(HttpResponse.class), - Mockito.any(HttpContext.class))).thenReturn(Boolean.TRUE); - - Assert.assertTrue(this.httpAuthenticator.isAuthenticationRequested( - this.defaultHost, response, this.defltAuthStrategy, this.authState, this.context)); - - Mockito.verify(this.defltAuthStrategy).isAuthenticationRequested(this.defaultHost, response, this.context); + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); + Assert.assertTrue(this.httpAuthenticator.updateAuthState( + this.defaultHost, ChallengeType.TARGET, response, this.authState, this.context)); + Mockito.verifyZeroInteractions(this.authCache); } @Test public void testAuthenticationRequestedAfterSuccess() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); - Mockito.when(this.defltAuthStrategy.isAuthenticationRequested( - Mockito.any(HttpHost.class), - Mockito.any(HttpResponse.class), - Mockito.any(HttpContext.class))).thenReturn(Boolean.TRUE); + response.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); this.authState.update(this.authScheme, this.credentials); this.authState.setState(AuthProtocolState.SUCCESS); - Assert.assertTrue(this.httpAuthenticator.isAuthenticationRequested( - this.defaultHost, response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertTrue(this.httpAuthenticator.updateAuthState( + this.defaultHost, ChallengeType.TARGET, response, this.authState, this.context)); - Mockito.verify(this.defltAuthStrategy).isAuthenticationRequested(this.defaultHost, response, this.context); - Mockito.verify(this.defltAuthStrategy).authFailed(this.defaultHost, this.authScheme, this.context); + Mockito.verify(this.authCache).remove(this.defaultHost); } @Test public void testAuthenticationNotRequestedUnchallenged() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - Mockito.when(this.defltAuthStrategy.isAuthenticationRequested( - Mockito.any(HttpHost.class), - Mockito.any(HttpResponse.class), - Mockito.any(HttpContext.class))).thenReturn(Boolean.FALSE); - Assert.assertFalse(this.httpAuthenticator.isAuthenticationRequested( - this.defaultHost, response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.updateAuthState( + this.defaultHost, ChallengeType.TARGET, response, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState()); - - Mockito.verify(this.defltAuthStrategy).isAuthenticationRequested(this.defaultHost, response, this.context); } @Test public void testAuthenticationNotRequestedSuccess1() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - Mockito.when(this.defltAuthStrategy.isAuthenticationRequested( - 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( - this.defaultHost, response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.updateAuthState( + this.defaultHost, ChallengeType.TARGET, response, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState()); - Mockito.verify(this.defltAuthStrategy).isAuthenticationRequested(this.defaultHost, response, this.context); - Mockito.verify(this.defltAuthStrategy).authSucceeded(this.defaultHost, this.authScheme, this.context); + Mockito.verify(this.authCache).put(this.defaultHost, this.authScheme); } @Test public void testAuthenticationNotRequestedSuccess2() throws Exception { final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); - Mockito.when(this.defltAuthStrategy.isAuthenticationRequested( - 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( - this.defaultHost, response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.updateAuthState( + this.defaultHost, ChallengeType.TARGET, response, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.SUCCESS, this.authState.getState()); - Mockito.verify(this.defltAuthStrategy).isAuthenticationRequested(this.defaultHost, response, this.context); - Mockito.verify(this.defltAuthStrategy).authSucceeded(this.defaultHost, this.authScheme, this.context); + Mockito.verify(this.authCache).put(this.defaultHost, this.authScheme); } @Test @@ -193,10 +164,10 @@ public void testAuthentication() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\"")); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge( + host,ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState()); final Queue options = this.authState.getAuthOptions(); @@ -215,13 +186,10 @@ public void testAuthenticationNoChallenges() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); - Mockito.when(this.defltAuthStrategy.getChallenges( - Mockito.any(HttpHost.class), - Mockito.any(HttpResponse.class), - Mockito.any(HttpContext.class))).thenReturn(new HashMap()); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); } @Test @@ -231,10 +199,10 @@ public void testAuthenticationNoSupportedChallenges() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "This realm=\"test\"")); response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "That realm=\"realm1\", nonce=\"1234\"")); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); } @Test @@ -246,10 +214,10 @@ public void testAuthenticationNoCredentials() throws Exception { this.credentialsProvider.clear(); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); } @Test @@ -262,10 +230,10 @@ public void testAuthenticationFailed() throws Exception { this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.update(this.authScheme, this.credentials); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState()); @@ -281,10 +249,10 @@ public void testAuthenticationFailedPreviously() throws Exception { this.authState.setState(AuthProtocolState.FAILURE); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState()); } @@ -297,13 +265,13 @@ public void testAuthenticationFailure() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\"")); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.update(new BasicScheme(), this.credentials); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.FAILURE, this.authState.getState()); Assert.assertNull(this.authState.getCredentials()); } @@ -316,13 +284,13 @@ public void testAuthenticationHandshaking() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", stale=true, nonce=\"1234\"")); response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\"")); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.update(new DigestScheme(), this.credentials); - Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.HANDSHAKE, this.authState.getState()); } @@ -334,13 +302,13 @@ public void testAuthenticationNoMatchingChallenge() throws Exception { response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "whatever realm=\"realm1\", stuff=\"1234\"")); - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); this.authState.setState(AuthProtocolState.CHALLENGED); this.authState.update(new BasicScheme(), this.credentials); - Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge(host, - response, authStrategy, this.authState, this.context)); + Assert.assertTrue(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.CHALLENGED, this.authState.getState()); final Queue options = this.authState.getAuthOptions(); @@ -355,16 +323,14 @@ public void testAuthenticationNoMatchingChallenge() throws Exception { public void testAuthenticationException() throws Exception { final HttpHost host = new HttpHost("somehost", 80); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + response.addHeader(new BasicHeader(AUTH.WWW_AUTH, "blah blah blah")); this.authState.setState(AuthProtocolState.CHALLENGED); - Mockito.doThrow(new MalformedChallengeException()).when(this.defltAuthStrategy).getChallenges( - Mockito.any(HttpHost.class), - Mockito.any(HttpResponse.class), - Mockito.any(HttpContext.class)); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); - Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge(host, - response, this.defltAuthStrategy, this.authState, this.context)); + Assert.assertFalse(this.httpAuthenticator.handleAuthChallenge( + host, ChallengeType.TARGET, response, authStrategy, this.authState, this.context)); Assert.assertEquals(AuthProtocolState.UNCHALLENGED, this.authState.getState()); Assert.assertNull(this.authState.getAuthScheme()); diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestNonStandardHttpScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestNonStandardHttpScheme.java new file mode 100644 index 000000000..a187a5d7e --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestNonStandardHttpScheme.java @@ -0,0 +1,87 @@ +/* + * ==================================================================== + * 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.auth; + +import org.apache.http.Header; +import org.apache.http.HttpRequest; +import org.apache.http.auth.AuthChallenge; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.protocol.HttpContext; +import org.junit.Assert; +import org.junit.Test; + +public class TestNonStandardHttpScheme { + + static class TestAuthScheme extends NonStandardAuthScheme { + + @Override + public void processChallenge( + final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { + update(challengeType, authChallenge); + } + + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return null; + } + + @Override + public String getSchemeName() { + return "test"; + } + + @Override + public boolean isComplete() { + return false; + } + + @Override + public boolean isConnectionBased() { + return false; + } + + } + + @Test + public void testProcessChallenge() throws Exception { + final TestAuthScheme authscheme = new TestAuthScheme(); + authscheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("Test", "this_and_that", null)); + + Assert.assertEquals("test", authscheme.getSchemeName()); + Assert.assertEquals("test(TARGET) this_and_that", authscheme.toString()); + Assert.assertEquals("this_and_that", authscheme.getChallenge()); + } + +} + diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java deleted file mode 100644 index 31173c3fb..000000000 --- a/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java +++ /dev/null @@ -1,175 +0,0 @@ -/* - * ==================================================================== - * 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.auth; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.nio.charset.Charset; - -import org.apache.http.Consts; -import org.apache.http.Header; -import org.apache.http.HttpRequest; -import org.apache.http.auth.AUTH; -import org.apache.http.auth.AuthenticationException; -import org.apache.http.auth.Credentials; -import org.apache.http.auth.MalformedChallengeException; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BufferedHeader; -import org.apache.http.protocol.HttpContext; -import org.apache.http.util.CharArrayBuffer; -import org.junit.Assert; -import org.junit.Test; - -public class TestRFC2617Scheme { - - static class TestAuthScheme extends RFC2617Scheme { - - private static final long serialVersionUID = 1L; - - public TestAuthScheme() { - super(); - } - - public TestAuthScheme(final Charset charset) { - super(charset); - } - - @Override - public Header authenticate( - final Credentials credentials, - final HttpRequest request, - final HttpContext context) throws AuthenticationException { - return null; - } - - @Override - public String getSchemeName() { - return "test"; - } - - @Override - public boolean isComplete() { - return false; - } - - @Override - public boolean isConnectionBased() { - return false; - } - - } - - @Test - public void testProcessChallenge() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - final Header header = new BasicHeader( - AUTH.WWW_AUTH, - "Test realm=\"realm1\", test, test1 = stuff, test2 = \"stuff, stuff\", test3=\"crap"); - - authscheme.processChallenge(header); - - Assert.assertEquals("test", authscheme.getSchemeName()); - Assert.assertEquals("TEST", authscheme.toString()); - Assert.assertEquals("realm1", authscheme.getParameter("realm")); - Assert.assertEquals(null, authscheme.getParameter("test")); - Assert.assertEquals("stuff", authscheme.getParameter("test1")); - Assert.assertEquals("stuff, stuff", authscheme.getParameter("test2")); - Assert.assertEquals("crap", authscheme.getParameter("test3")); - Assert.assertEquals(null, authscheme.getParameter(null)); - } - - @Test - public void testProcessChallengeWithLotsOfBlanks() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - final CharArrayBuffer buffer = new CharArrayBuffer(32); - buffer.append(" WWW-Authenticate: Test realm=\"realm1\""); - final Header header = new BufferedHeader(buffer); - - - authscheme.processChallenge(header); - - Assert.assertEquals("test", authscheme.getSchemeName()); - Assert.assertEquals("realm1", authscheme.getParameter("realm")); - } - - @Test - public void testNullHeader() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - try { - authscheme.processChallenge(null); - Assert.fail("IllegalArgumentException should have been thrown"); - } catch (final IllegalArgumentException ex) { - } - } - - @Test(expected=MalformedChallengeException.class) - public void testInvalidHeader() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - final Header header = new BasicHeader("whatever", "Test realm=\"realm1\""); - authscheme.processChallenge(header); - } - - @Test(expected=MalformedChallengeException.class) - public void testInvalidSchemeName() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - final Header header = new BasicHeader(AUTH.WWW_AUTH, "Not-a-Test realm=\"realm1\""); - authscheme.processChallenge(header); - } - - @Test(expected=MalformedChallengeException.class) - public void testInvalidHeaderValue() throws Exception { - final TestAuthScheme authscheme = new TestAuthScheme(); - final Header header = new BasicHeader("whatever", "whatever"); - authscheme.processChallenge(header); - } - - @Test - public void testSerialization() throws Exception { - final Header challenge = new BasicHeader(AUTH.WWW_AUTH, "test realm=\"test\", blah=blah, yada=\"yada yada\""); - - final TestAuthScheme testScheme = new TestAuthScheme(Consts.ISO_8859_1); - testScheme.processChallenge(challenge); - - final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - final ObjectOutputStream out = new ObjectOutputStream(buffer); - out.writeObject(testScheme); - out.flush(); - final byte[] raw = buffer.toByteArray(); - final ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(raw)); - final TestAuthScheme authScheme = (TestAuthScheme) in.readObject(); - - Assert.assertEquals(Consts.ISO_8859_1, authScheme.getCredentialsCharset()); - Assert.assertEquals("test", authScheme.getParameter("realm")); - Assert.assertEquals("blah", authScheme.getParameter("blah")); - Assert.assertEquals("yada yada", authScheme.getParameter("yada")); - } - -} - diff --git a/httpclient/src/test/java/org/apache/http/impl/auth/TestStandardHttpScheme.java b/httpclient/src/test/java/org/apache/http/impl/auth/TestStandardHttpScheme.java new file mode 100644 index 000000000..c4b988005 --- /dev/null +++ b/httpclient/src/test/java/org/apache/http/impl/auth/TestStandardHttpScheme.java @@ -0,0 +1,95 @@ +/* + * ==================================================================== + * 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.auth; + +import java.util.Arrays; + +import org.apache.http.Header; +import org.apache.http.HttpRequest; +import org.apache.http.auth.AuthChallenge; +import org.apache.http.auth.AuthenticationException; +import org.apache.http.auth.ChallengeType; +import org.apache.http.auth.Credentials; +import org.apache.http.auth.MalformedChallengeException; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.protocol.HttpContext; +import org.junit.Assert; +import org.junit.Test; + +public class TestStandardHttpScheme { + + static class TestAuthScheme extends StandardAuthScheme { + + private static final long serialVersionUID = 1L; + + @Override + public void processChallenge( + final ChallengeType challengeType, final AuthChallenge authChallenge) throws MalformedChallengeException { + update(challengeType, authChallenge); + } + + @Override + public Header authenticate( + final Credentials credentials, + final HttpRequest request, + final HttpContext context) throws AuthenticationException { + return null; + } + + @Override + public String getSchemeName() { + return "test"; + } + + @Override + public boolean isComplete() { + return false; + } + + @Override + public boolean isConnectionBased() { + return false; + } + + } + + @Test + public void testProcessChallenge() throws Exception { + final TestAuthScheme authscheme = new TestAuthScheme(); + authscheme.processChallenge(ChallengeType.TARGET, new AuthChallenge("Test", null, Arrays.asList( + new BasicNameValuePair("realm", "realm1"), new BasicNameValuePair("this", "blah")))); + + Assert.assertEquals("test", authscheme.getSchemeName()); + Assert.assertEquals("test(TARGET) {realm=realm1, this=blah}", authscheme.toString()); + Assert.assertEquals("realm1", authscheme.getParameter("realm")); + Assert.assertEquals(null, authscheme.getParameter("test")); + Assert.assertEquals("blah", authscheme.getParameter("this")); + } + +} + 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 0d0d772bf..11af35b35 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 @@ -27,23 +27,19 @@ package org.apache.http.impl.client; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Queue; -import org.apache.http.Header; import org.apache.http.HttpHost; -import org.apache.http.HttpResponse; -import org.apache.http.HttpStatus; -import org.apache.http.HttpVersion; -import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthOption; -import org.apache.http.auth.AuthScheme; import org.apache.http.auth.AuthSchemeProvider; import org.apache.http.auth.AuthScope; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.UsernamePasswordCredentials; -import org.apache.http.client.AuthCache; import org.apache.http.client.config.AuthSchemes; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.protocol.HttpClientContext; @@ -53,98 +49,38 @@ import org.apache.http.impl.auth.BasicSchemeFactory; import org.apache.http.impl.auth.DigestScheme; import org.apache.http.impl.auth.DigestSchemeFactory; -import org.apache.http.message.BasicHeader; -import org.apache.http.message.BasicHttpResponse; +import org.apache.http.message.BasicNameValuePair; import org.junit.Assert; import org.junit.Test; -import org.mockito.Mockito; /** - * Simple tests for {@link AuthenticationStrategyImpl}. + * Simple tests for {@link DefaultAuthenticationStrategy}. */ @SuppressWarnings("boxing") // test code public class TestAuthenticationStrategy { - @Test(expected=IllegalArgumentException.class) - public void testIsAuthenticationRequestedInvalidInput() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost host = new HttpHost("localhost", 80); - final HttpClientContext context = HttpClientContext.create(); - authStrategy.isAuthenticationRequested(host, null, context); - } - - @Test - public void testTargetAuthRequested() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); - final HttpHost host = new HttpHost("localhost", 80); - final HttpClientContext context = HttpClientContext.create(); - Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context)); - } - - @Test - public void testProxyAuthRequested() throws Exception { - final ProxyAuthenticationStrategy authStrategy = new ProxyAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED, "UNAUTHORIZED"); - final HttpHost host = new HttpHost("localhost", 80); - final HttpClientContext context = HttpClientContext.create(); - Assert.assertTrue(authStrategy.isAuthenticationRequested(host, response, context)); - } - - @Test(expected=IllegalArgumentException.class) - public void testGetChallengesInvalidInput() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost host = new HttpHost("localhost", 80); - final HttpClientContext context = HttpClientContext.create(); - authStrategy.getChallenges(host, null, context); - } - - @Test - public void testGetChallenges() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpClientContext context = HttpClientContext.create(); - final HttpHost host = new HttpHost("localhost", 80); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); - final Header h1 = new BasicHeader(AUTH.WWW_AUTH, " Basic realm=\"test\""); - final Header h2 = new BasicHeader(AUTH.WWW_AUTH, "\t\tDigest realm=\"realm1\", nonce=\"1234\""); - final Header h3 = new BasicHeader(AUTH.WWW_AUTH, "WhatEver realm=\"realm1\", stuff=\"1234\""); - response.addHeader(h1); - response.addHeader(h2); - response.addHeader(h3); - - final Map challenges = authStrategy.getChallenges(host, response, context); - - Assert.assertNotNull(challenges); - Assert.assertEquals(3, challenges.size()); - Assert.assertSame(h1, challenges.get("basic")); - Assert.assertSame(h2, challenges.get("digest")); - Assert.assertSame(h3, challenges.get("whatever")); - } - @Test public void testSelectInvalidInput() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final Map challenges = new HashMap<>(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("locahost", 80); final HttpClientContext context = HttpClientContext.create(); try { - authStrategy.select(null, authhost, response, context); + authStrategy.select(null, authhost, Collections.emptyMap(), context); Assert.fail("IllegalArgumentException expected"); } catch (final IllegalArgumentException ex) { } try { - authStrategy.select(challenges, null, response, context); + authStrategy.select(ChallengeType.TARGET, null, Collections.emptyMap(), context); Assert.fail("IllegalArgumentException expected"); } catch (final IllegalArgumentException ex) { } try { - authStrategy.select(challenges, authhost, null, context); + authStrategy.select(ChallengeType.TARGET, authhost, null, context); Assert.fail("IllegalArgumentException expected"); } catch (final IllegalArgumentException ex) { } try { - authStrategy.select(challenges, authhost, response, null); + authStrategy.select(ChallengeType.TARGET, authhost, Collections.emptyMap(), null); Assert.fail("IllegalArgumentException expected"); } catch (final IllegalArgumentException ex) { } @@ -152,51 +88,54 @@ public void testSelectInvalidInput() throws Exception { @Test public void testSelectNoSchemeRegistry() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("locahost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "test"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "test"), new BasicNameValuePair("nonce", "1234"))); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(0, options.size()); } @Test public void testSelectNoCredentialsProvider() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("locahost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"test\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm1\", nonce=\"1234\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "test"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "test"), new BasicNameValuePair("nonce", "1234"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register("basic", new BasicSchemeFactory()) .register("digest", new DigestSchemeFactory()).build(); context.setAuthSchemeRegistry(authSchemeRegistry); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(0, options.size()); } @Test public void testNoCredentials() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("locahost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "realm1"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register("basic", new BasicSchemeFactory()) @@ -206,21 +145,22 @@ public void testNoCredentials() throws Exception { final CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); context.setCredentialsProvider(credentialsProvider); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(0, options.size()); } @Test public void testCredentialsFound() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("somehost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "realm1"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register("basic", new BasicSchemeFactory()) @@ -232,7 +172,7 @@ public void testCredentialsFound() throws Exception { new UsernamePasswordCredentials("user", "pwd")); context.setCredentialsProvider(credentialsProvider); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(1, options.size()); final AuthOption option = options.remove(); @@ -241,15 +181,17 @@ public void testCredentialsFound() throws Exception { @Test public void testUnsupportedScheme() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final HttpHost authhost = new HttpHost("somehost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\"")); - challenges.put("whatever", new BasicHeader(AUTH.WWW_AUTH, "Whatever realm=\"realm3\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "realm1"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); + challenges.put("whatever", new AuthChallenge("Whatever", + new BasicNameValuePair("realm", "realm3"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register("basic", new BasicSchemeFactory()) @@ -261,7 +203,7 @@ public void testUnsupportedScheme() throws Exception { new UsernamePasswordCredentials("user", "pwd")); context.setCredentialsProvider(credentialsProvider); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(2, options.size()); final AuthOption option1 = options.remove(); @@ -272,8 +214,7 @@ public void testUnsupportedScheme() throws Exception { @Test public void testCustomAuthPreference() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED"); + final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy(); final RequestConfig config = RequestConfig.custom() .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)) .build(); @@ -281,9 +222,11 @@ public void testCustomAuthPreference() throws Exception { final HttpHost authhost = new HttpHost("somehost", 80); final HttpClientContext context = HttpClientContext.create(); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=\"realm1\"")); - challenges.put("digest", new BasicHeader(AUTH.WWW_AUTH, "Digest realm=\"realm2\", nonce=\"1234\"")); + final Map challenges = new HashMap<>(); + challenges.put("basic", new AuthChallenge("Basic", + new BasicNameValuePair("realm", "realm1"))); + challenges.put("digest", new AuthChallenge("Digest", + new BasicNameValuePair("realm", "realm2"), new BasicNameValuePair("nonce", "1234"))); final Registry authSchemeRegistry = RegistryBuilder.create() .register("basic", new BasicSchemeFactory()) @@ -296,143 +239,11 @@ public void testCustomAuthPreference() throws Exception { new UsernamePasswordCredentials("user", "pwd")); context.setCredentialsProvider(credentialsProvider); - final Queue options = authStrategy.select(challenges, authhost, response, context); + final Queue options = authStrategy.select(ChallengeType.TARGET, authhost, challenges, context); Assert.assertNotNull(options); Assert.assertEquals(1, options.size()); final AuthOption option1 = options.remove(); Assert.assertTrue(option1.getAuthScheme() instanceof BasicScheme); } - - @Test - public void testAuthSucceededInvalidInput() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("locahost", 80); - final BasicScheme authScheme = new BasicScheme(); - final HttpClientContext context = HttpClientContext.create(); - try { - authStrategy.authSucceeded(null, authScheme, context); - Assert.fail("IllegalArgumentException expected"); - } catch (final IllegalArgumentException ex) { - } - try { - authStrategy.authSucceeded(authhost, null, context); - Assert.fail("IllegalArgumentException expected"); - } catch (final IllegalArgumentException ex) { - } - try { - authStrategy.authSucceeded(authhost, authScheme, null); - Assert.fail("IllegalArgumentException expected"); - } catch (final IllegalArgumentException ex) { - } - } - - @Test - public void testAuthSucceeded() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final BasicScheme authScheme = new BasicScheme(); - authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); - - final AuthCache authCache = Mockito.mock(AuthCache.class); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(authCache); - - authStrategy.authSucceeded(authhost, authScheme, context); - Mockito.verify(authCache).put(authhost, authScheme); - } - - @Test - public void testAuthSucceededNoCache() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final BasicScheme authScheme = new BasicScheme(); - authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(null); - - authStrategy.authSucceeded(authhost, authScheme, context); - final AuthCache authCache = context.getAuthCache(); - Assert.assertNotNull(authCache); - } - - @Test - public void testAuthScemeNotCompleted() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final BasicScheme authScheme = new BasicScheme(); - - final AuthCache authCache = Mockito.mock(AuthCache.class); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(authCache); - - authStrategy.authSucceeded(authhost, authScheme, context); - Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme); - } - - @Test - public void testAuthScemeNonCacheable() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final AuthScheme authScheme = Mockito.mock(AuthScheme.class); - Mockito.when(authScheme.isComplete()).thenReturn(true); - Mockito.when(authScheme.getSchemeName()).thenReturn("whatever"); - - final AuthCache authCache = Mockito.mock(AuthCache.class); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(authCache); - - authStrategy.authSucceeded(authhost, authScheme, context); - Mockito.verify(authCache, Mockito.never()).put(authhost, authScheme); - } - - @Test - public void testAuthFailedInvalidInput() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("locahost", 80); - final BasicScheme authScheme = new BasicScheme(); - final HttpClientContext context = HttpClientContext.create(); - try { - authStrategy.authFailed(null, authScheme, context); - Assert.fail("IllegalArgumentException expected"); - } catch (final IllegalArgumentException ex) { - } - try { - authStrategy.authFailed(authhost, authScheme, null); - Assert.fail("IllegalArgumentException expected"); - } catch (final IllegalArgumentException ex) { - } - } - - @Test - public void testAuthFailed() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final BasicScheme authScheme = new BasicScheme(); - authScheme.processChallenge(new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); - - final AuthCache authCache = Mockito.mock(AuthCache.class); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(authCache); - - authStrategy.authFailed(authhost, authScheme, context); - Mockito.verify(authCache).remove(authhost); - } - - @Test - public void testAuthFailedNoCache() throws Exception { - final TargetAuthenticationStrategy authStrategy = new TargetAuthenticationStrategy(); - final HttpHost authhost = new HttpHost("somehost", 80); - final BasicScheme authScheme = new BasicScheme(); - - final HttpClientContext context = HttpClientContext.create(); - context.setAuthCache(null); - - authStrategy.authFailed(authhost, authScheme, context); - } - + } diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthentication.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthentication.java index 92300cb29..ff9c7597b 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthentication.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthentication.java @@ -28,6 +28,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.Map; +import java.util.Queue; import java.util.concurrent.atomic.AtomicLong; import org.apache.http.Consts; @@ -39,9 +41,13 @@ import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; +import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthScope; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.Credentials; import org.apache.http.auth.CredentialsProvider; +import org.apache.http.auth.MalformedChallengeException; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthCache; import org.apache.http.client.ClientProtocolException; @@ -56,7 +62,7 @@ import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.client.BasicAuthCache; import org.apache.http.impl.client.BasicCredentialsProvider; -import org.apache.http.impl.client.TargetAuthenticationStrategy; +import org.apache.http.impl.client.DefaultAuthenticationStrategy; import org.apache.http.localserver.BasicAuthTokenExtractor; import org.apache.http.localserver.LocalServerTestBase; import org.apache.http.localserver.RequestBasicAuth; @@ -342,7 +348,7 @@ public void testBasicAuthenticationFailureOnNonRepeatablePost() throws Exception } } - static class TestTargetAuthenticationStrategy extends TargetAuthenticationStrategy { + static class TestTargetAuthenticationStrategy extends DefaultAuthenticationStrategy { private final AtomicLong count; @@ -352,15 +358,14 @@ public TestTargetAuthenticationStrategy() { } @Override - public boolean isAuthenticationRequested( + public Queue select( + final ChallengeType challengeType, final HttpHost host, - final HttpResponse response, - final HttpContext context) { - final boolean res = super.isAuthenticationRequested(host, response, context); - if (res == true) { - this.count.incrementAndGet(); - } - return res; + final Map challenges, + final HttpContext context) throws MalformedChallengeException { + final Queue authOptions = super.select(challengeType, host, challenges, context); + this.count.incrementAndGet(); + return authOptions; } public long getCount() { diff --git a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientReauthentication.java b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientReauthentication.java index c4e327570..8481ba0c5 100644 --- a/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientReauthentication.java +++ b/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientReauthentication.java @@ -46,6 +46,7 @@ import org.apache.http.auth.CredentialsProvider; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.Registry; @@ -53,7 +54,6 @@ import org.apache.http.entity.StringEntity; import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.auth.BasicSchemeFactory; -import org.apache.http.impl.client.TargetAuthenticationStrategy; import org.apache.http.localserver.LocalServerTestBase; import org.apache.http.localserver.RequestBasicAuth; import org.apache.http.protocol.HttpContext; @@ -162,15 +162,6 @@ public String getSchemeName() { }; - final TargetAuthenticationStrategy myAuthStrategy = new TargetAuthenticationStrategy() { - - @Override - protected boolean isCachable(final AuthScheme authScheme) { - return "MyBasic".equalsIgnoreCase(authScheme.getSchemeName()); - } - - }; - final TestCredentialsProvider credsProvider = new TestCredentialsProvider( new UsernamePasswordCredentials("test", "test")); @@ -182,7 +173,6 @@ protected boolean isCachable(final AuthScheme authScheme) { .build(); this.httpclient = this.clientBuilder .setDefaultAuthSchemeRegistry(authSchemeRegistry) - .setTargetAuthenticationStrategy(myAuthStrategy) .setDefaultCredentialsProvider(credsProvider) .build(); @@ -192,11 +182,12 @@ protected boolean isCachable(final AuthScheme authScheme) { for (int i = 0; i < 10; i++) { final HttpGet httpget = new HttpGet("/"); httpget.setConfig(config); - final HttpResponse response = this.httpclient.execute(target, httpget, context); - final HttpEntity entity = response.getEntity(); - Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); - Assert.assertNotNull(entity); - EntityUtils.consume(entity); + try (final CloseableHttpResponse response = this.httpclient.execute(target, httpget, context)) { + final HttpEntity entity = response.getEntity(); + Assert.assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode()); + Assert.assertNotNull(entity); + EntityUtils.consume(entity); + } } } diff --git a/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java index c03819e5f..3c839d178 100644 --- a/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java +++ b/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java @@ -32,25 +32,26 @@ import java.io.InputStream; import java.io.InterruptedIOException; import java.util.Arrays; -import java.util.HashMap; +import java.util.Collections; import java.util.LinkedList; import java.util.Map; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import org.apache.http.ConnectionReuseStrategy; -import org.apache.http.Header; import org.apache.http.HttpClientConnection; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; +import org.apache.http.HttpHeaders; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpVersion; -import org.apache.http.auth.AUTH; +import org.apache.http.auth.AuthChallenge; import org.apache.http.auth.AuthOption; import org.apache.http.auth.AuthProtocolState; import org.apache.http.auth.AuthState; +import org.apache.http.auth.ChallengeType; import org.apache.http.auth.NTCredentials; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.AuthenticationStrategy; @@ -74,7 +75,6 @@ import org.apache.http.impl.auth.BasicScheme; import org.apache.http.impl.auth.NTLMScheme; import org.apache.http.impl.conn.ConnectionShutdownException; -import org.apache.http.message.BasicHeader; import org.apache.http.message.BasicHttpResponse; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpProcessor; @@ -132,39 +132,6 @@ public void setup() throws Exception { Mockito.any(), Mockito.any())).thenReturn(connRequest); Mockito.when(connRequest.get( Mockito.anyLong(), Mockito.any())).thenReturn(managedConn); - final Map challenges = new HashMap<>(); - challenges.put("basic", new BasicHeader(AUTH.WWW_AUTH, "Basic realm=test")); - final AuthOption authOption = new AuthOption( - new BasicScheme(), new UsernamePasswordCredentials("user:pass")); - Mockito.when(targetAuthStrategy.getChallenges( - Mockito.eq(target), - Mockito.any(), - Mockito.any())).thenReturn(challenges); - Mockito.when(targetAuthStrategy.getChallenges( - Mockito.eq(target), - Mockito.any(), - Mockito.any())).thenReturn(challenges); - Mockito.when(targetAuthStrategy.select( - Mockito.same(challenges), - Mockito.eq(target), - Mockito.any(), - Mockito.any())).thenReturn( - new LinkedList<>(Arrays.asList(authOption))); - Mockito.when(proxyAuthStrategy.getChallenges( - Mockito.eq(proxy), - Mockito.any(), - Mockito.any())).thenReturn(challenges); - Mockito.when(proxyAuthStrategy.getChallenges( - Mockito.eq(proxy), - Mockito.any(), - Mockito.any())).thenReturn(challenges); - Mockito.when(proxyAuthStrategy.select( - Mockito.same(challenges), - Mockito.eq(proxy), - Mockito.any(), - Mockito.any())).thenReturn( - new LinkedList<>(Arrays.asList(authOption))); - } @Test @@ -418,6 +385,7 @@ public void testExecRequestRetryOnAuthChallenge() throws Exception { final HttpClientContext context = new HttpClientContext(); final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(instream1) @@ -437,10 +405,14 @@ public void testExecRequestRetryOnAuthChallenge() throws Exception { Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any())).thenReturn(Boolean.TRUE); - Mockito.when(targetAuthStrategy.isAuthenticationRequested( + Mockito.when(targetAuthStrategy.select( + Mockito.eq(ChallengeType.TARGET), Mockito.eq(target), - Mockito.same(response1), - Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.>any(), + Mockito.any())).thenReturn(new LinkedList<>( + Collections.singleton(new AuthOption( + new BasicScheme(), + new UsernamePasswordCredentials("user", "pass"))))); final CloseableHttpResponse finalResponse = mainClientExec.execute( route, request, context, execAware); @@ -457,6 +429,7 @@ public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exceptio final HttpRoute route = new HttpRoute(target); final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(instream1) @@ -483,10 +456,15 @@ public void testExecEntityEnclosingRequestRetryOnAuthChallenge() throws Exceptio Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any())).thenReturn(Boolean.FALSE); - Mockito.when(targetAuthStrategy.isAuthenticationRequested( + + final AuthOption authOption = new AuthOption( + new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + Mockito.when(targetAuthStrategy.select( + Mockito.eq(ChallengeType.TARGET), Mockito.eq(target), - Mockito.same(response1), - Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.>any(), + Mockito.any())).thenReturn( + new LinkedList<>(Arrays.asList(authOption))); final CloseableHttpResponse finalResponse = mainClientExec.execute( route, request, context, execAware); @@ -512,6 +490,7 @@ public void testExecEntityEnclosingRequest() throws Exception { final HttpRequestWrapper request = HttpRequestWrapper.wrap(post); final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + response1.setHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=test"); final InputStream instream1 = new ByteArrayInputStream(new byte[] {1, 2, 3}); response1.setEntity(EntityBuilder.create() .setStream(instream1) @@ -536,10 +515,15 @@ public HttpResponse answer(final InvocationOnMock invocationOnMock) throws Throw Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any())).thenReturn(Boolean.TRUE); - Mockito.when(targetAuthStrategy.isAuthenticationRequested( + + final AuthOption authOption = new AuthOption( + new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + Mockito.when(targetAuthStrategy.select( + Mockito.eq(ChallengeType.TARGET), Mockito.eq(target), - Mockito.same(response1), - Mockito.any())).thenReturn(Boolean.TRUE); + Mockito.>any(), + Mockito.any())).thenReturn( + new LinkedList<>(Arrays.asList(authOption))); mainClientExec.execute(route, request, context, execAware); } @@ -732,7 +716,8 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnec final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); - final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 407, "Huh?"); + response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic realm=test"); final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(instream1) @@ -740,10 +725,6 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnec final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(proxyAuthStrategy.isAuthenticationRequested( - Mockito.eq(proxy), - Mockito.same(response1), - Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any())).thenReturn(Boolean.TRUE); @@ -752,6 +733,15 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengePersistentConnec Mockito.any(), Mockito.any())).thenReturn(response1, response2); + final AuthOption authOption = new AuthOption( + new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + Mockito.when(proxyAuthStrategy.select( + Mockito.eq(ChallengeType.PROXY), + Mockito.eq(proxy), + Mockito.>any(), + Mockito.any())).thenReturn( + new LinkedList<>(Arrays.asList(authOption))); + mainClientExec.establishRoute(authState, managedConn, route, request, context); Mockito.verify(connManager).connect(managedConn, route, 0, context); @@ -765,7 +755,8 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentCon final HttpRoute route = new HttpRoute(target, null, proxy, true); final HttpClientContext context = new HttpClientContext(); final HttpRequestWrapper request = HttpRequestWrapper.wrap(new HttpGet("http://bar/test")); - final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 401, "Huh?"); + final HttpResponse response1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 407, "Huh?"); + response1.setHeader(HttpHeaders.PROXY_AUTHENTICATE, "Basic realm=test"); final InputStream instream1 = Mockito.spy(new ByteArrayInputStream(new byte[] {1, 2, 3})); response1.setEntity(EntityBuilder.create() .setStream(instream1) @@ -773,10 +764,6 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentCon final HttpResponse response2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); Mockito.when(managedConn.isOpen()).thenReturn(Boolean.TRUE); - Mockito.when(proxyAuthStrategy.isAuthenticationRequested( - Mockito.eq(proxy), - Mockito.same(response1), - Mockito.any())).thenReturn(Boolean.TRUE); Mockito.when(reuseStrategy.keepAlive( Mockito.any(), Mockito.any())).thenReturn(Boolean.FALSE); @@ -785,6 +772,15 @@ public void testEstablishRouteViaProxyTunnelRetryOnAuthChallengeNonPersistentCon Mockito.any(), Mockito.any())).thenReturn(response1, response2); + final AuthOption authOption = new AuthOption( + new BasicScheme(), new UsernamePasswordCredentials("user:pass")); + Mockito.when(proxyAuthStrategy.select( + Mockito.eq(ChallengeType.PROXY), + Mockito.eq(proxy), + Mockito.>any(), + Mockito.any())).thenReturn( + new LinkedList<>(Arrays.asList(authOption))); + mainClientExec.establishRoute(authState, managedConn, route, request, context); Mockito.verify(connManager).connect(managedConn, route, 0, context);