You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2015/07/04 15:42:40 UTC

svn commit: r1689155 [1/3] - in /httpcomponents/httpclient/trunk: fluent-hc/src/main/java/org/apache/http/client/fluent/ httpclient-win/src/main/java/org/apache/http/impl/auth/win/ httpclient/src/main/java/org/apache/http/auth/ httpclient/src/main/java...

Author: olegk
Date: Sat Jul  4 13:42:40 2015
New Revision: 1689155

URL: http://svn.apache.org/r1689155
Log:
RFC 7231: redesign of HTTP authenticator and related classes

Added:
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java   (contents, props changed)
      - copied, changed from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java   (contents, props changed)
      - copied, changed from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java   (with props)
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java   (contents, props changed)
      - copied, changed from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestNonStandardHttpScheme.java   (with props)
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestStandardHttpScheme.java   (with props)
Removed:
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthSchemeBase.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/RFC2617Scheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/TargetAuthenticationStrategy.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestRFC2617Scheme.java
Modified:
    httpcomponents/httpclient/trunk/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java
    httpcomponents/httpclient/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java
    httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/execchain/MainClientExec.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestAuthChallengeParser.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestBasicScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestDigestScheme.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/auth/TestHttpAuthenticator.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/TestAuthenticationStrategy.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientAuthentication.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/client/integration/TestClientReauthentication.java
    httpcomponents/httpclient/trunk/httpclient/src/test/java/org/apache/http/impl/execchain/TestMainClientExec.java

Modified: httpcomponents/httpclient/trunk/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java (original)
+++ httpcomponents/httpclient/trunk/fluent-hc/src/main/java/org/apache/http/client/fluent/Executor.java Sat Jul  4 13:42:40 2015
@@ -29,13 +29,16 @@ package org.apache.http.client.fluent;
 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.Basic
 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 class Executor {
     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.<NameValuePair>emptyList()));
         } catch (final MalformedChallengeException ignore) {
         }
         this.authCache.put(host, basicScheme);
@@ -168,7 +170,7 @@ public class Executor {
     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.<NameValuePair>emptyList()));
         } catch (final MalformedChallengeException ignore) {
         }
         this.authCache.put(proxy, basicScheme);

Modified: httpcomponents/httpclient/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient-win/src/main/java/org/apache/http/impl/auth/win/WindowsNegotiateScheme.java Sat Jul  4 13:42:40 2015
@@ -34,14 +34,16 @@ import org.apache.http.HttpHost;
 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 @@ import com.sun.jna.ptr.IntByReference;
  * @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 exte
     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 class WindowsNegotiateScheme exte
         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 class WindowsNegotiateScheme exte
             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 class WindowsNegotiateScheme exte
                     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);

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthChallenge.java Sat Jul  4 13:42:40 2015
@@ -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 final class AuthChallenge {
         this.params = params != null ? Collections.unmodifiableList(new ArrayList<>(params)) : null;
     }
 
+    public AuthChallenge(final String scheme, final NameValuePair... params) {
+        this(scheme, null, Arrays.asList(params));
+    }
+
     public String getScheme() {
         return scheme;
     }

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/AuthScheme.java Sat Jul  4 13:42:40 2015
@@ -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(
+            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
      */
-    void processChallenge(final Header header) throws MalformedChallengeException;
+    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;
-
 }

Copied: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java (from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java?p2=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java&p1=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java&r1=1687909&r2=1689155&rev=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeState.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java Sat Jul  4 13:42:40 2015
@@ -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
 

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/auth/ChallengeType.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/client/AuthenticationStrategy.java Sat Jul  4 13:42:40 2015
@@ -30,18 +30,16 @@ package org.apache.http.client;
 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).
  * <p>
  * 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.
@@ -51,80 +49,24 @@ import org.apache.http.protocol.HttpCont
 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<String, Header> 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<AuthOption> select(
-            Map<String, Header> challenges,
-            HttpHost authhost,
-            HttpResponse response,
+            ChallengeType challengeType,
+            HttpHost host,
+            Map<String, AuthChallenge> 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);
-
 }

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/AuthChallengeParser.java Sat Jul  4 13:42:40 2015
@@ -41,6 +41,8 @@ import org.apache.http.util.CharArrayBuf
 
 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 @@ public class AuthChallengeParser {
             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);

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/BasicScheme.java Sat Jul  4 13:42:40 2015
@@ -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.Header;
 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 @@ import org.apache.http.util.EncodingUtil
  * @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 class BasicScheme extends RFC2617
         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 class BasicScheme extends RFC2617
 
         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 class BasicScheme extends RFC2617
             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 {
     }
+
 }

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestScheme.java Sat Jul  4 13:42:40 2015
@@ -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.Locale;
 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 @@ import org.apache.http.util.EncodingUtil
  * @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 RFC261
     private String a1;
     private String a2;
 
-    /**
-     * @since 4.3
-     */
-    public DigestScheme(final Charset credentialsCharset) {
-        super(credentialsCharset);
-        this.complete = false;
-    }
-
     public DigestScheme() {
-        this(Consts.ASCII);
+        this.complete = false;
     }
 
-    /**
-     * 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 class DigestScheme extends RFC261
         }
     }
 
-    /**
-     * 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 class DigestScheme extends RFC261
         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 class DigestScheme extends RFC261
         // 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 @@ public class DigestScheme extends RFC261
         }
     }
 
-    /**
-     * 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 @@ public class DigestScheme extends RFC261
         return new String(buffer);
     }
 
-
     /**
      * Creates a random cnonce value based on the current time.
      *
@@ -460,14 +405,4 @@ public class DigestScheme extends RFC261
         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();
-    }
-
 }

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/DigestSchemeFactory.java Sat Jul  4 13:42:40 2015
@@ -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 @@ import org.apache.http.protocol.HttpCont
 @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();
     }
 
 }

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/GGSSchemeBase.java Sat Jul  4 13:42:40 2015
@@ -37,7 +37,9 @@ import org.apache.http.HttpHost;
 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 @@ import org.ietf.jgss.Oid;
  * @since 4.2
  */
 @NotThreadSafe
-public abstract class GGSSchemeBase extends AuthSchemeBase {
+public abstract class GGSSchemeBase extends NonStandardAuthScheme {
 
     enum State {
         UNINITIATED,
@@ -70,7 +72,6 @@ public abstract class GGSSchemeBase exte
 
     private final Log log = LogFactory.getLog(getClass());
 
-    private final Base64 base64codec;
     private final boolean stripPort;
     private final boolean useCanonicalHostname;
 
@@ -82,7 +83,6 @@ public abstract class GGSSchemeBase exte
 
     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 @@ public abstract class GGSSchemeBase exte
         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 abstract class GGSSchemeBase exte
                 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 abstract class GGSSchemeBase exte
         }
     }
 
-    @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();

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/HttpAuthenticator.java Sat Jul  4 13:42:40 2015
@@ -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 @@ import org.apache.http.util.Asserts;
 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 class HttpAuthenticator {
             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 class HttpAuthenticator {
 
     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<String, AuthChallenge> 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<AuthChallenge> 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<String, Header> 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 class HttpAuthenticator {
             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 class HttpAuthenticator {
             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 class HttpAuthenticator {
                     }
                 }
             }
-            final Queue<AuthOption> authOptions = authStrategy.select(challenges, host, response, context);
+            final Queue<AuthOption> 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 class HttpAuthenticator {
         case FAILURE:
             return;
         case SUCCESS:
-            ensureAuthScheme(authScheme);
+            Asserts.notNull(authScheme, "AuthScheme");
             if (authScheme.isConnectionBased()) {
                 return;
             }
@@ -209,7 +273,7 @@ public class HttpAuthenticator {
                 }
                 return;
             } else {
-                ensureAuthScheme(authScheme);
+                Asserts.notNull(authScheme, "AuthScheme");
             }
         }
         if (authScheme != null) {
@@ -224,8 +288,35 @@ public class HttpAuthenticator {
         }
     }
 
-    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(

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/KerberosScheme.java Sat Jul  4 13:42:40 2015
@@ -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 class KerberosScheme extends GGSS
         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;

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NTLMScheme.java Sat Jul  4 13:42:40 2015
@@ -30,7 +30,9 @@ import org.apache.http.Header;
 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 @@ import org.apache.http.util.CharArrayBuf
  * @since 4.0
  */
 @NotThreadSafe
-public class NTLMScheme extends AuthSchemeBase {
+public class NTLMScheme extends NonStandardAuthScheme {
 
     enum State {
         UNINITIATED,
@@ -61,14 +63,12 @@ public class NTLMScheme extends AuthSche
     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;
     }
 
     /**
@@ -84,28 +84,17 @@ public class NTLMScheme extends AuthSche
     }
 
     @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 class NTLMScheme extends AuthSche
             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 class NTLMScheme extends AuthSche
              "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 class NTLMScheme extends AuthSche
                     ntcredentials.getPassword(),
                     ntcredentials.getNetbiosDomain(),
                     ntcredentials.getWorkstation(),
-                    this.challenge);
+                    getChallenge());
             this.state = State.MSG_TYPE3_GENERATED;
         } else {
             throw new AuthenticationException("Unexpected state: " + this.state);

Copied: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java (from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java?p2=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java&p1=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java&r1=1687909&r2=1689155&rev=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyAuthenticationStrategy.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java Sat Jul  4 13:42:40 2015
@@ -24,34 +24,47 @@
  * <http://www.apache.org/>.
  *
  */
+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;
-
-/**
- * Default {@link org.apache.http.client.AuthenticationStrategy} implementation
- * for proxy host authentication.
- *
- * @since 4.2
- */
-@Immutable
-public class ProxyAuthenticationStrategy extends AuthenticationStrategyImpl {
+    private ChallengeType challengeType;
+    private String challenge;
+
+    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
+    public String getParameter(final String name) {
+        return null;
+    }
+
+    @Override
+    public String getRealm() {
+        return null;
     }
 
     @Override
-    Collection<String> getPreferredAuthSchemes(final RequestConfig config) {
-        return config.getProxyPreferredAuthSchemes();
+    public String toString() {
+        return getSchemeName() + "(" + this.challengeType + ") " + this.challenge;
     }
 
 }

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/NonStandardAuthScheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/SPNegoScheme.java Sat Jul  4 13:42:40 2015
@@ -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 class SPNegoScheme extends GGSSch
         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;

Added: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java?rev=1689155&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java (added)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java Sat Jul  4 13:42:40 2015
@@ -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
+ * <http://www.apache.org/>.
+ *
+ */
+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<String, String> 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<NameValuePair> 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<String, String> 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;
+    }
+
+}

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/auth/StandardAuthScheme.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Copied: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java (from r1687909, httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java)
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java?p2=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java&p1=httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java&r1=1687909&r2=1689155&rev=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/AuthenticationStrategyImpl.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java Sat Jul  4 13:42:40 2015
@@ -30,7 +30,6 @@ package org.apache.http.impl.client;
 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 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.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<String> DEFAULT_SCHEME_PRIORITY =
         Collections.unmodifiableList(Arrays.asList(
                 AuthSchemes.SPNEGO,
@@ -75,82 +77,15 @@ abstract class AuthenticationStrategyImp
                 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 =&gt; Header entries.
-     *
-     * @return map: key=lower-cased auth-scheme name, value=Header that contains the challenge
-     */
-    @Override
-    public Map<String, Header> 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<String, Header> 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<String> getPreferredAuthSchemes(RequestConfig config);
-
     @Override
     public Queue<AuthOption> select(
-            final Map<String, Header> challenges,
+            final ChallengeType challengeType,
             final HttpHost authhost,
-            final HttpResponse response,
+            final Map<String, AuthChallenge> 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 @@ abstract class AuthenticationStrategyImp
             return options;
         }
         final RequestConfig config = clientContext.getRequestConfig();
-        Collection<String> authPrefs = getPreferredAuthSchemes(config);
+        Collection<String> authPrefs = challengeType == ChallengeType.TARGET ?
+                config.getTargetPreferredAuthSchemes() : config.getProxyPreferredAuthSchemes();
         if (authPrefs == null) {
             authPrefs = DEFAULT_SCHEME_PRIORITY;
         }
@@ -175,7 +111,7 @@ abstract class AuthenticationStrategyImp
         }
 
         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 @@ abstract class AuthenticationStrategyImp
                     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 @@ abstract class AuthenticationStrategyImp
         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);
-        }
-    }
-
 }

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/DefaultAuthenticationStrategy.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/HttpClientBuilder.java Sat Jul  4 13:42:40 2015
@@ -949,11 +949,11 @@ public class HttpClientBuilder {
         }
         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) {

Modified: httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java?rev=1689155&r1=1689154&r2=1689155&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java (original)
+++ httpcomponents/httpclient/trunk/httpclient/src/main/java/org/apache/http/impl/client/ProxyClient.java Sat Jul  4 13:42:40 2015
@@ -41,7 +41,9 @@ import org.apache.http.auth.AUTH;
 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<AuthSchemeProvider> authSchemeRegistry;
@@ -106,7 +108,7 @@ public class 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.<AuthSchemeProvider>create()
@@ -184,9 +186,8 @@ public class ProxyClient {
                 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)) {