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 2021/09/19 10:09:45 UTC

[httpcomponents-client] branch develop created (now 7ba1c6b)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch develop
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git.


      at 7ba1c6b  AuthCache conformance to RFC 7617

This branch includes the following new commits:

     new 7ba1c6b  AuthCache conformance to RFC 7617

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


[httpcomponents-client] 01/01: AuthCache conformance to RFC 7617

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit 7ba1c6b875b8b4bb22148cc15967c07d0b681003
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Sep 15 15:55:47 2021 +0200

    AuthCache conformance to RFC 7617
---
 .../apache/hc/client5/http/SchemePortResolver.java |  10 ++
 .../org/apache/hc/client5/http/auth/AuthCache.java |  65 +++++++++++++
 .../http/impl/DefaultSchemePortResolver.java       |  14 ++-
 .../client5/http/impl/async/AsyncConnectExec.java  |   7 +-
 .../client5/http/impl/async/AsyncProtocolExec.java |  12 +--
 .../hc/client5/http/impl/auth/BasicAuthCache.java  | 103 ++++++++++++++++++---
 .../client5/http/impl/auth/HttpAuthenticator.java  |  75 ++++++++++++---
 .../hc/client5/http/impl/classic/ConnectExec.java  |   4 +-
 .../hc/client5/http/impl/classic/ProtocolExec.java |  12 +--
 .../hc/client5/http/impl/classic/ProxyClient.java  |   7 +-
 .../client5/http/impl/auth/TestBasicAuthCache.java |   2 +-
 .../http/impl/auth/TestHttpAuthenticator.java      |  40 ++++----
 12 files changed, 283 insertions(+), 68 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/SchemePortResolver.java b/httpclient5/src/main/java/org/apache/hc/client5/http/SchemePortResolver.java
index 26ddb42..f57e019 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/SchemePortResolver.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/SchemePortResolver.java
@@ -29,6 +29,7 @@ package org.apache.hc.client5.http;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.net.NamedEndpoint;
 
 /**
  * Strategy for default port resolution for protocol schemes.
@@ -43,4 +44,13 @@ public interface SchemePortResolver {
      */
     int resolve(HttpHost host);
 
+    /**
+     * Returns the actual port for the host based on the protocol scheme.
+     *
+     * @since 5.2
+     */
+    default int resolve(String scheme, NamedEndpoint endpoint) {
+        return resolve(new HttpHost(scheme, endpoint));
+    }
+
 }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthCache.java b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthCache.java
index f566b39..ec784a5 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthCache.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/auth/AuthCache.java
@@ -27,6 +27,7 @@
 package org.apache.hc.client5.http.auth;
 
 import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.net.NamedEndpoint;
 
 /**
  * This interface represents an cache of {@link AuthScheme} state information
@@ -36,12 +37,76 @@ import org.apache.hc.core5.http.HttpHost;
  */
 public interface AuthCache {
 
+    /**
+     * Stores the authentication state with the given authentication scope in the cache.
+     *
+     * @param host the authentication authority.
+     * @param authScheme the cacheable authentication state.
+     */
     void put(HttpHost host, AuthScheme authScheme);
 
+    /**
+     * Returns the authentication state with the given authentication scope from the cache
+     * if available.
+     *
+     * @param host the authentication authority.
+     * @return the authentication state ir {@code null} if not available in the cache.
+     */
     AuthScheme get(HttpHost host);
 
+    /**
+     * Removes the authentication state with the given authentication scope from the cache
+     * if found.
+     *
+     * @param host the authentication authority.
+     */
     void remove(HttpHost host);
 
     void clear();
 
+    /**
+     * Stores the authentication state with the given authentication scope in the cache.
+     *
+     * @param scheme the protocol scheme.
+     * @param authority the authentication authority.
+     * @param pathPrefix the path prefix (the path component up to the last segment separator).
+     *                   Can be {@code null}.
+     * @param authScheme the cacheable authentication state.
+     *
+     * @since 5.2
+     */
+    default void put(String scheme, NamedEndpoint authority, String pathPrefix, AuthScheme authScheme) {
+        put(new HttpHost(scheme, authority), authScheme);
+    }
+
+    /**
+     * Returns the authentication state with the given authentication scope from the cache
+     * if available.
+     * @param scheme the protocol scheme.
+     * @param authority the authentication authority.
+     * @param pathPrefix the path prefix (the path component up to the last segment separator).
+     *                   Can be {@code null}.
+     * @return the authentication state ir {@code null} if not available in the cache.
+     *
+     * @since 5.2
+     */
+    default AuthScheme get(String scheme, NamedEndpoint authority, String pathPrefix) {
+        return get(new HttpHost(scheme, authority));
+    }
+
+    /**
+     * Removes the authentication state with the given authentication scope from the cache
+     * if found.
+     *
+     * @param scheme the protocol scheme.
+     * @param authority the authentication authority.
+     * @param pathPrefix the path prefix (the path component up to the last segment separator).
+     *                   Can be {@code null}.
+     *
+     * @since 5.2
+     */
+    default void remove(String scheme, NamedEndpoint authority, String pathPrefix) {
+        remove(new HttpHost(scheme, authority));
+    }
+
 }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultSchemePortResolver.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultSchemePortResolver.java
index 144ae5f..c65e65d 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultSchemePortResolver.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/DefaultSchemePortResolver.java
@@ -31,6 +31,7 @@ import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -46,14 +47,19 @@ public class DefaultSchemePortResolver implements SchemePortResolver {
     @Override
     public int resolve(final HttpHost host) {
         Args.notNull(host, "HTTP host");
-        final int port = host.getPort();
+        return resolve(host.getSchemeName(), host);
+    }
+
+    @Override
+    public int resolve(final String scheme, final NamedEndpoint endpoint) {
+        Args.notNull(endpoint, "Endpoint");
+        final int port = endpoint.getPort();
         if (port > 0) {
             return port;
         }
-        final String name = host.getSchemeName();
-        if (URIScheme.HTTP.same(name)) {
+        if (URIScheme.HTTP.same(scheme)) {
             return 80;
-        } else if (URIScheme.HTTPS.same(name)) {
+        } else if (URIScheme.HTTPS.same(scheme)) {
             return 443;
         } else {
             return -1;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
index b0288c7..7510536 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncConnectExec.java
@@ -430,10 +430,11 @@ public final class AsyncConnectExec implements AsyncExecChainHandler {
             final HttpClientContext context) {
         final RequestConfig config = context.getRequestConfig();
         if (config.isAuthenticationEnabled()) {
-            final boolean proxyAuthRequested = authenticator.isChallenged(proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+            final boolean proxyAuthRequested = authenticator.isChallenged(
+                    proxy, null, ChallengeType.PROXY, response, proxyAuthExchange, context);
             if (proxyAuthRequested) {
-                return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
-                        proxyAuthStrategy, proxyAuthExchange, context);
+                return authenticator.updateAuthState(
+                        proxy, null, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context);
             }
         }
         return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
index fe0d73b..f418feb 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/async/AsyncProtocolExec.java
@@ -275,7 +275,7 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
         if (config.isAuthenticationEnabled()) {
             final HttpHost target = AuthSupport.resolveAuthTarget(request, route);
             final boolean targetAuthRequested = authenticator.isChallenged(
-                    target, ChallengeType.TARGET, response, targetAuthExchange, context);
+                    target, null, ChallengeType.TARGET, response, targetAuthExchange, context);
 
             HttpHost proxy = route.getProxyHost();
             // if proxy is not set use target host instead
@@ -283,15 +283,15 @@ public final class AsyncProtocolExec implements AsyncExecChainHandler {
                 proxy = route.getTargetHost();
             }
             final boolean proxyAuthRequested = authenticator.isChallenged(
-                    proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+                    proxy, null, ChallengeType.PROXY, response, proxyAuthExchange, context);
 
             if (targetAuthRequested) {
-                return authenticator.updateAuthState(target, ChallengeType.TARGET, response,
-                        targetAuthStrategy, targetAuthExchange, context);
+                return authenticator.updateAuthState(
+                        target, null, ChallengeType.TARGET, response, targetAuthStrategy, targetAuthExchange, context);
             }
             if (proxyAuthRequested) {
-                return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
-                        proxyAuthStrategy, proxyAuthExchange, context);
+                return authenticator.updateAuthState(
+                        proxy, null, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context);
             }
         }
         return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicAuthCache.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicAuthCache.java
index c22c57b..cd6deee 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicAuthCache.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/BasicAuthCache.java
@@ -32,6 +32,7 @@ import java.io.IOException;
 import java.io.ObjectInputStream;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
+import java.util.Locale;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -39,11 +40,12 @@ import org.apache.hc.client5.http.SchemePortResolver;
 import org.apache.hc.client5.http.auth.AuthCache;
 import org.apache.hc.client5.http.auth.AuthScheme;
 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
-import org.apache.hc.client5.http.routing.RoutingSupport;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.net.NamedEndpoint;
 import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.LangUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -62,7 +64,65 @@ public class BasicAuthCache implements AuthCache {
 
     private static final Logger LOG = LoggerFactory.getLogger(BasicAuthCache.class);
 
-    private final Map<HttpHost, byte[]> map;
+    static class Key {
+
+        final String scheme;
+        final String host;
+        final int port;
+        final String pathPrefix;
+
+        Key(final String scheme, final String host, final int port, final String pathPrefix) {
+            Args.notBlank(scheme, "Scheme");
+            Args.notBlank(host, "Scheme");
+            this.scheme = scheme.toLowerCase(Locale.ROOT);
+            this.host = host.toLowerCase(Locale.ROOT);
+            this.port = port;
+            this.pathPrefix = pathPrefix;
+        }
+
+        @Override
+        public boolean equals(final Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj instanceof Key) {
+                final Key that = (Key) obj;
+                return this.scheme.equals(that.scheme) &&
+                        this.host.equals(that.host) &&
+                        this.port == that.port &&
+                        LangUtils.equals(this.pathPrefix, that.pathPrefix);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            int hash = LangUtils.HASH_SEED;
+            hash = LangUtils.hashCode(hash, this.scheme);
+            hash = LangUtils.hashCode(hash, this.host);
+            hash = LangUtils.hashCode(hash, this.port);
+            hash = LangUtils.hashCode(hash, this.pathPrefix);
+            return hash;
+        }
+
+        @Override
+        public String toString() {
+            final StringBuilder buf = new StringBuilder();
+            buf.append(scheme).append("://").append(host);
+            if (port >= 0) {
+                buf.append(":").append(port);
+            }
+            if (pathPrefix != null) {
+                if (!pathPrefix.startsWith("/")) {
+                    buf.append("/");
+                }
+                buf.append(pathPrefix);
+            }
+            return buf.toString();
+        }
+    }
+
+    private final Map<Key, byte[]> map;
     private final SchemePortResolver schemePortResolver;
 
     /**
@@ -80,9 +140,31 @@ public class BasicAuthCache implements AuthCache {
         this(null);
     }
 
+    private Key key(final String scheme, final NamedEndpoint authority, final String pathPrefix) {
+        return new Key(scheme, authority.getHostName(), schemePortResolver.resolve(scheme, authority), null);
+    }
+
     @Override
     public void put(final HttpHost host, final AuthScheme authScheme) {
         Args.notNull(host, "HTTP host");
+        put(host.getSchemeName(), host, null, authScheme);
+    }
+
+    @Override
+    public AuthScheme get(final HttpHost host) {
+        Args.notNull(host, "HTTP host");
+        return get(host.getSchemeName(), host, null);
+    }
+
+    @Override
+    public void remove(final HttpHost host) {
+        Args.notNull(host, "HTTP host");
+        remove(host.getSchemeName(), host, null);
+    }
+
+    @Override
+    public void put(final String scheme, final NamedEndpoint authority, final String pathPrefix, final AuthScheme authScheme) {
+        Args.notNull(authority, "Authority");
         if (authScheme == null) {
             return;
         }
@@ -92,8 +174,7 @@ public class BasicAuthCache implements AuthCache {
                 try (final ObjectOutputStream out = new ObjectOutputStream(buf)) {
                     out.writeObject(authScheme);
                 }
-                final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
-                this.map.put(key, buf.toByteArray());
+                this.map.put(key(scheme, authority, pathPrefix), buf.toByteArray());
             } catch (final IOException ex) {
                 if (LOG.isWarnEnabled()) {
                     LOG.warn("Unexpected I/O error while serializing auth scheme", ex);
@@ -107,10 +188,9 @@ public class BasicAuthCache implements AuthCache {
     }
 
     @Override
-    public AuthScheme get(final HttpHost host) {
-        Args.notNull(host, "HTTP host");
-        final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
-        final byte[] bytes = this.map.get(key);
+    public AuthScheme get(final String scheme, final NamedEndpoint authority, final String pathPrefix) {
+        Args.notNull(authority, "Authority");
+        final byte[] bytes = this.map.get(key(scheme, authority, pathPrefix));
         if (bytes != null) {
             try {
                 final ByteArrayInputStream buf = new ByteArrayInputStream(bytes);
@@ -131,10 +211,9 @@ public class BasicAuthCache implements AuthCache {
     }
 
     @Override
-    public void remove(final HttpHost host) {
-        Args.notNull(host, "HTTP host");
-        final HttpHost key = RoutingSupport.normalize(host, schemePortResolver);
-        this.map.remove(key);
+    public void remove(final String scheme, final NamedEndpoint authority, final String pathPrefix) {
+        Args.notNull(authority, "Authority");
+        this.map.remove(key(scheme, authority, pathPrefix));
     }
 
     @Override
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java
index 7987402..02d0404 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/auth/HttpAuthenticator.java
@@ -105,6 +105,31 @@ public final class HttpAuthenticator {
             final HttpResponse response,
             final AuthExchange authExchange,
             final HttpContext context) {
+        return isChallenged(host, null, challengeType, response, authExchange, context);
+    }
+
+    /**
+     * Determines whether the given response represents an authentication challenge.
+     *
+     * @param host the hostname of the opposite endpoint.
+     * @param pathPrefix the path prefix (the path component up to the last segment separator).
+     *                   Can be {@code null}.
+     * @param challengeType the challenge type (target or proxy).
+     * @param response the response message head.
+     * @param authExchange the current authentication exchange state.
+     * @param context the current execution context.
+     * @return {@code true} if the response message represents an authentication challenge,
+     *   {@code false} otherwise.
+     *
+     * @since 5.2
+     */
+    public boolean isChallenged(
+            final HttpHost host,
+            final String pathPrefix,
+            final ChallengeType challengeType,
+            final HttpResponse response,
+            final AuthExchange authExchange,
+            final HttpContext context) {
         final int challengeCode;
         switch (challengeType) {
             case TARGET:
@@ -125,7 +150,7 @@ public final class HttpAuthenticator {
                 log.debug("{} Authentication required", exchangeId);
             }
             if (authExchange.getState() == AuthExchange.State.SUCCESS) {
-                clearCache(host, clientContext);
+                clearCache(host, pathPrefix, clientContext);
             }
             return true;
         }
@@ -136,7 +161,7 @@ public final class HttpAuthenticator {
                 log.debug("{} Authentication succeeded", exchangeId);
             }
             authExchange.setState(AuthExchange.State.SUCCESS);
-            updateCache(host, authExchange.getAuthScheme(), clientContext);
+            updateCache(host, pathPrefix, authExchange.getAuthScheme(), clientContext);
             break;
         case SUCCESS:
             break;
@@ -166,6 +191,34 @@ public final class HttpAuthenticator {
             final AuthenticationStrategy authStrategy,
             final AuthExchange authExchange,
             final HttpContext context) {
+        return updateAuthState(host, null, challengeType, response, authStrategy, authExchange, context);
+    }
+
+    /**
+     * Updates the {@link AuthExchange} state based on the challenge presented in the response message
+     * using the given {@link AuthenticationStrategy}.
+     *
+     * @param host the hostname of the opposite endpoint.
+     * @param pathPrefix the path prefix (the path component up to the last segment separator).
+     *                   Can be {@code null}.
+     * @param challengeType the challenge type (target or proxy).
+     * @param response the response message head.
+     * @param authStrategy the authentication strategy.
+     * @param authExchange the current authentication exchange state.
+     * @param context the current execution context.
+     * @return {@code true} if the authentication state has been updated,
+     *   {@code false} if unchanged.
+     *
+     * @since 5.2
+     */
+    public boolean updateAuthState(
+            final HttpHost host,
+            final String pathPrefix,
+            final ChallengeType challengeType,
+            final HttpResponse response,
+            final AuthenticationStrategy authStrategy,
+            final AuthExchange authExchange,
+            final HttpContext context) {
 
         final HttpClientContext clientContext = HttpClientContext.adapt(context);
         final String exchangeId = clientContext.getExchangeId();
@@ -213,7 +266,7 @@ public final class HttpAuthenticator {
             if (log.isDebugEnabled()) {
                 log.debug("{} Response contains no valid authentication challenges", exchangeId);
             }
-            clearCache(host, clientContext);
+            clearCache(host, pathPrefix, clientContext);
             authExchange.reset();
             return false;
         }
@@ -242,7 +295,7 @@ public final class HttpAuthenticator {
                             if (log.isWarnEnabled()) {
                                 log.warn("{} {}", exchangeId, ex.getMessage());
                             }
-                            clearCache(host, clientContext);
+                            clearCache(host, pathPrefix, clientContext);
                             authExchange.reset();
                             return false;
                         }
@@ -250,7 +303,7 @@ public final class HttpAuthenticator {
                             if (log.isDebugEnabled()) {
                                 log.debug("{} Authentication failed", exchangeId);
                             }
-                            clearCache(host, clientContext);
+                            clearCache(host, pathPrefix, clientContext);
                             authExchange.reset();
                             authExchange.setState(AuthExchange.State.FAILURE);
                             return false;
@@ -376,7 +429,7 @@ public final class HttpAuthenticator {
         }
     }
 
-    private void updateCache(final HttpHost host, final AuthScheme authScheme, final HttpClientContext clientContext) {
+    private void updateCache(final HttpHost host, final String pathPrefix, final AuthScheme authScheme, final HttpClientContext clientContext) {
         final boolean cacheable = authScheme.getClass().getAnnotation(AuthStateCacheable.class) != null;
         if (cacheable) {
             AuthCache authCache = clientContext.getAuthCache();
@@ -386,21 +439,21 @@ public final class HttpAuthenticator {
             }
             if (log.isDebugEnabled()) {
                 final String exchangeId = clientContext.getExchangeId();
-                log.debug("{} Caching '{}' auth scheme for {}", exchangeId, authScheme.getName(), host);
+                log.debug("{} Caching '{}' auth scheme for {}{}", exchangeId, authScheme.getName(), host, pathPrefix);
             }
-            authCache.put(host, authScheme);
+            authCache.put(host.getSchemeName(), host, pathPrefix, authScheme);
         }
     }
 
-    private void clearCache(final HttpHost host, final HttpClientContext clientContext) {
+    private void clearCache(final HttpHost host, final String pathPrefix, final HttpClientContext clientContext) {
 
         final AuthCache authCache = clientContext.getAuthCache();
         if (authCache != null) {
             if (log.isDebugEnabled()) {
                 final String exchangeId = clientContext.getExchangeId();
-                log.debug("{} Clearing cached auth scheme for {}", exchangeId, host);
+                log.debug("{} Clearing cached auth scheme for {}{}", exchangeId, host, pathPrefix);
             }
-            authCache.remove(host);
+            authCache.remove(host.getSchemeName(), host, pathPrefix);
         }
     }
 
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
index 357915f..e4090cb 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ConnectExec.java
@@ -239,9 +239,9 @@ public final class ConnectExec implements ExecChainHandler {
             }
 
             if (config.isAuthenticationEnabled()) {
-                if (this.authenticator.isChallenged(proxy, ChallengeType.PROXY, response,
+                if (this.authenticator.isChallenged(proxy, null, ChallengeType.PROXY, response,
                         proxyAuthExchange, context)) {
-                    if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
+                    if (this.authenticator.updateAuthState(proxy, null, ChallengeType.PROXY, response,
                             this.proxyAuthStrategy, proxyAuthExchange, context)) {
                         // Retry request
                         response = null;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
index 2edfe4c..9e2b285 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProtocolExec.java
@@ -238,7 +238,7 @@ public final class ProtocolExec implements ExecChainHandler {
         if (config.isAuthenticationEnabled()) {
             final HttpHost target = AuthSupport.resolveAuthTarget(request, route);
             final boolean targetAuthRequested = authenticator.isChallenged(
-                    target, ChallengeType.TARGET, response, targetAuthExchange, context);
+                    target, null, ChallengeType.TARGET, response, targetAuthExchange, context);
 
             HttpHost proxy = route.getProxyHost();
             // if proxy is not set use target host instead
@@ -246,15 +246,15 @@ public final class ProtocolExec implements ExecChainHandler {
                 proxy = route.getTargetHost();
             }
             final boolean proxyAuthRequested = authenticator.isChallenged(
-                    proxy, ChallengeType.PROXY, response, proxyAuthExchange, context);
+                    proxy, null, ChallengeType.PROXY, response, proxyAuthExchange, context);
 
             if (targetAuthRequested) {
-                return authenticator.updateAuthState(target, ChallengeType.TARGET, response,
-                        targetAuthStrategy, targetAuthExchange, context);
+                return authenticator.updateAuthState(
+                        target, null, ChallengeType.TARGET, response, targetAuthStrategy, targetAuthExchange, context);
             }
             if (proxyAuthRequested) {
-                return authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
-                        proxyAuthStrategy, proxyAuthExchange, context);
+                return authenticator.updateAuthState(
+                        proxy, null, ChallengeType.PROXY, response, proxyAuthStrategy, proxyAuthExchange, context);
             }
         }
         return false;
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProxyClient.java b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProxyClient.java
index 1c8a4e5..d8f8cfb 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProxyClient.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ProxyClient.java
@@ -187,9 +187,10 @@ public class ProxyClient {
             if (status < 200) {
                 throw new HttpException("Unexpected response to CONNECT request: " + response);
             }
-            if (this.authenticator.isChallenged(proxy, ChallengeType.PROXY, response, this.proxyAuthExchange, context)) {
-                if (this.authenticator.updateAuthState(proxy, ChallengeType.PROXY, response,
-                        this.proxyAuthStrategy, this.proxyAuthExchange, context)) {
+            if (this.authenticator.isChallenged(
+                    proxy, null, ChallengeType.PROXY, response, this.proxyAuthExchange, context)) {
+                if (this.authenticator.updateAuthState(
+                        proxy, null, ChallengeType.PROXY, response, this.proxyAuthStrategy, this.proxyAuthExchange, context)) {
                     // Retry request
                     if (this.reuseStrategy.keepAlive(connect, response, context)) {
                         // Consume response content
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestBasicAuthCache.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestBasicAuthCache.java
index b57fa6d..3486065 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestBasicAuthCache.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestBasicAuthCache.java
@@ -67,7 +67,7 @@ public class TestBasicAuthCache {
     }
 
     @Test
-    public void testStoreNonserializable() throws Exception {
+    public void testStoreNonSerializable() throws Exception {
         final BasicAuthCache cache = new BasicAuthCache();
         final AuthScheme authScheme = new NTLMScheme();
         cache.put(new HttpHost("localhost", 80), authScheme);
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
index fd24894..2988ab3 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/impl/auth/TestHttpAuthenticator.java
@@ -108,7 +108,7 @@ public class TestHttpAuthenticator {
         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_UNAUTHORIZED, "UNAUTHORIZED");
         response.setHeader(HttpHeaders.WWW_AUTHENTICATE, StandardAuthScheme.BASIC + " realm=test");
         Assert.assertTrue(this.httpAuthenticator.isChallenged(
-                this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context));
+                this.defaultHost, null, ChallengeType.TARGET, response, this.authExchange, this.context));
         Mockito.verifyNoInteractions(this.authCache);
     }
 
@@ -121,9 +121,9 @@ public class TestHttpAuthenticator {
         this.authExchange.setState(AuthExchange.State.SUCCESS);
 
         Assert.assertTrue(this.httpAuthenticator.isChallenged(
-                this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context));
+                this.defaultHost, null, ChallengeType.TARGET, response, this.authExchange, this.context));
 
-        Mockito.verify(this.authCache).remove(this.defaultHost);
+        Mockito.verify(this.authCache).remove(this.defaultHost.getSchemeName(), this.defaultHost, null);
     }
 
     @Test
@@ -131,7 +131,7 @@ public class TestHttpAuthenticator {
         final HttpResponse response = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
 
         Assert.assertFalse(this.httpAuthenticator.isChallenged(
-                this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context));
+                this.defaultHost, null, ChallengeType.TARGET, response, this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.UNCHALLENGED, this.authExchange.getState());
     }
 
@@ -142,10 +142,10 @@ public class TestHttpAuthenticator {
         this.authExchange.setState(AuthExchange.State.CHALLENGED);
 
         Assert.assertFalse(this.httpAuthenticator.isChallenged(
-                this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context));
+                this.defaultHost, null, ChallengeType.TARGET, response, this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.SUCCESS, this.authExchange.getState());
 
-        Mockito.verify(this.authCache).put(this.defaultHost, this.authScheme);
+        Mockito.verify(this.authCache).put(this.defaultHost.getSchemeName(), this.defaultHost, null, this.authScheme);
     }
 
     @Test
@@ -155,10 +155,10 @@ public class TestHttpAuthenticator {
         this.authExchange.setState(AuthExchange.State.HANDSHAKE);
 
         Assert.assertFalse(this.httpAuthenticator.isChallenged(
-                this.defaultHost, ChallengeType.TARGET, response, this.authExchange, this.context));
+                this.defaultHost, null, ChallengeType.TARGET, response, this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.SUCCESS, this.authExchange.getState());
 
-        Mockito.verify(this.authCache).put(this.defaultHost, this.authScheme);
+        Mockito.verify(this.authCache).put(this.defaultHost.getSchemeName(), this.defaultHost, null, this.authScheme);
     }
 
     @Test
@@ -175,7 +175,7 @@ public class TestHttpAuthenticator {
 
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
-        Assert.assertTrue(this.httpAuthenticator.updateAuthState(host, ChallengeType.TARGET, response, authStrategy,
+        Assert.assertTrue(this.httpAuthenticator.updateAuthState(host, null, ChallengeType.TARGET, response, authStrategy,
                                                                      this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState());
 
@@ -204,7 +204,7 @@ public class TestHttpAuthenticator {
 
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
-        Assert.assertTrue(this.httpAuthenticator.updateAuthState(host, ChallengeType.TARGET, response, authStrategy,
+        Assert.assertTrue(this.httpAuthenticator.updateAuthState(host, null, ChallengeType.TARGET, response, authStrategy,
                                                                      this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState());
 
@@ -224,7 +224,7 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
     }
 
     @Test
@@ -237,7 +237,7 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
     }
 
     @Test
@@ -250,7 +250,7 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
     }
 
     @Test
@@ -266,11 +266,11 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
 
         Assert.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState());
 
-        Mockito.verify(this.authCache).remove(host);
+        Mockito.verify(this.authCache).remove(host.getSchemeName(), host, null);
     }
 
     @Test
@@ -285,7 +285,7 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
 
         Assert.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState());
     }
@@ -304,7 +304,7 @@ public class TestHttpAuthenticator {
         this.authExchange.select(new BasicScheme());
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.FAILURE, this.authExchange.getState());
     }
 
@@ -322,7 +322,7 @@ public class TestHttpAuthenticator {
         this.authExchange.select(new DigestScheme());
 
         Assert.assertTrue(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
 
         Assert.assertEquals(AuthExchange.State.HANDSHAKE, this.authExchange.getState());
     }
@@ -344,7 +344,7 @@ public class TestHttpAuthenticator {
         this.authExchange.select(new BasicScheme());
 
         Assert.assertTrue(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
         Assert.assertEquals(AuthExchange.State.CHALLENGED, this.authExchange.getState());
 
         final Queue<AuthScheme> options = this.authExchange.getAuthOptions();
@@ -366,7 +366,7 @@ public class TestHttpAuthenticator {
         final DefaultAuthenticationStrategy authStrategy = new DefaultAuthenticationStrategy();
 
         Assert.assertFalse(this.httpAuthenticator.updateAuthState(
-                host, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
+                host, null, ChallengeType.TARGET, response, authStrategy, this.authExchange, this.context));
 
         Assert.assertEquals(AuthExchange.State.UNCHALLENGED, this.authExchange.getState());
         Assert.assertNull(this.authExchange.getAuthScheme());