[squid-dev] [PATCH] Fix for bug 4190

Amos Jeffries squid3 at treenet.co.nz
Sun Sep 27 23:56:33 UTC 2015


New patch attached. I believe is resolves all the issues brought up so
far and a few clang build issues I found during the re-work.


The changes since previous iteration are:

* rename to CredentialsCache

* split Cache and Runner into two objects. Creating a Cache spawns a
Runner dedicated to managing it through a Pointer held by the Runner.

* made the cache key a value passed in by the caller code. For storing
credentials by arbitrary token/format/scope value rather than just username.

* removed the explicit eventDelete(), and using eventAdd() only when
data exists in the cache to be cleaned up.


Despite the long description of changes the actual logic difference is
quite small.

So, if there are no objections I would like to merge this in the next
day or two.

Amos

-------------- next part --------------
=== modified file 'src/auth/Config.cc'
--- src/auth/Config.cc	2015-01-13 07:25:36 +0000
+++ src/auth/Config.cc	2015-09-25 10:36:19 +0000
@@ -140,39 +140,20 @@
 
     storeAppendPrintf(entry, "%s %s children %d startup=%d idle=%d concurrency=%d\n",
                       name, scheme->type(),
                       authenticateChildren.n_max, authenticateChildren.n_startup,
                       authenticateChildren.n_idle, authenticateChildren.concurrency);
 
     if (keyExtrasLine.size() > 0)
         storeAppendPrintf(entry, "%s %s key_extras \"%s\"\n", name, scheme->type(), keyExtrasLine.termedBuf());
 
     return true;
 }
 
 void
 Auth::Config::done()
 {
     delete keyExtras;
     keyExtras = NULL;
     keyExtrasLine.clean();
 }
 
-Auth::User::Pointer
-Auth::Config::findUserInCache(const char *nameKey, Auth::Type authType)
-{
-    AuthUserHashPointer *usernamehash;
-    debugs(29, 9, "Looking for user '" << nameKey << "'");
-
-    if (nameKey && (usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, nameKey)))) {
-        while (usernamehash) {
-            if ((usernamehash->user()->auth_type == authType) &&
-                    !strcmp(nameKey, (char const *)usernamehash->key))
-                return usernamehash->user();
-
-            usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
-        }
-    }
-
-    return NULL;
-}
-

=== modified file 'src/auth/Config.h'
--- src/auth/Config.h	2015-08-04 19:57:07 +0000
+++ src/auth/Config.h	2015-09-25 10:36:20 +0000
@@ -91,43 +91,40 @@
      *          No other module functions except Shutdown/Dump/Parse/FreeConfig will be called by Squid.
      */
     virtual bool configured() const = 0;
 
     /**
      * Shutdown just the auth helpers.
      * For use by log rotate etc. where auth needs to stay running, with the helpers restarted.
      */
     virtual void rotateHelpers(void) = 0;
 
     /**
      * Responsible for writing to the StoreEntry the configuration parameters that a user
      * would put in a config file to recreate the running configuration.
      * Returns whether the scheme is configured.
      */
     virtual bool dump(StoreEntry *, const char *, Config *) const;
 
     /** add headers as needed when challenging for auth */
     virtual void fixHeader(UserRequest::Pointer, HttpReply *, Http::HdrType, HttpRequest *) = 0;
 
-    /// Find any existing user credentials in the authentication cache by name and type.
-    virtual Auth::User::Pointer findUserInCache(const char *nameKey, Auth::Type type);
-
     /** prepare to handle requests */
     virtual void init(Config *) = 0;
 
     /** expose any/all statistics to a CacheManager */
     virtual void registerWithCacheManager(void);
 
     /** parse config options */
     virtual void parse(Config *, int, char *);
 
     /** the http string id */
     virtual const char * type() const = 0;
 
 public:
     Helper::ChildConfig authenticateChildren;
     wordlist *authenticateProgram; ///< Helper program to run, includes all parameters
     String keyExtrasLine;  ///< The format of the request to the auth helper
     Format::Format *keyExtras; ///< The compiled request format
 
 protected:
     /// RFC 7235 section 2.2 - Protection Space (Realm)

=== added file 'src/auth/CredentialsCache.cc'
--- src/auth/CredentialsCache.cc	1970-01-01 00:00:00 +0000
+++ src/auth/CredentialsCache.cc	2015-09-25 10:36:22 +0000
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+/* DEBUG: section 29    Authenticator */
+
+#include "squid.h"
+#include "acl/Gadgets.h"
+#include "auth/CredentialsCache.h"
+#include "base/RunnersRegistry.h"
+#include "Debug.h"
+#include "event.h"
+#include "SquidConfig.h"
+#include "SquidTime.h"
+
+namespace Auth {
+
+class CredentialCacheRr : public RegisteredRunner
+{
+public:
+    explicit CredentialCacheRr(const char *n, CredentialsCache * const c) :
+        name(n),
+        whichCache(c)
+    {}
+
+    virtual ~CredentialCacheRr() {
+        debugs(29, 5, "Terminating Auth credentials cache: " << name);
+        // invalidate the CBDATA reference.
+        // causes Auth::*::User::Cache() to produce nil / invalid pointer
+        delete whichCache.get();
+    }
+
+    virtual void endingShutdown() override {
+        debugs(29, 5, "Clearing Auth credentials cache: " << name);
+        whichCache->reset();
+    }
+
+    virtual void syncConfig() override {
+        debugs(29, 5, "Reconfiguring Auth credentials cache: " << name);
+        whichCache->doConfigChangeCleanup();
+    }
+
+private:
+    /// name of the cache being managed, for logs
+    const char *name;
+
+    /// reference to the scheme cache which is being managed
+    CbcPointer<CredentialsCache> whichCache;
+};
+
+CBDATA_CLASS_INIT(CredentialsCache);
+
+CredentialsCache::CredentialsCache(const char *name, const char * const prettyEvName) :
+    gcScheduled_(false),
+    cacheCleanupEventName(prettyEvName)
+{
+    debugs(29, 5, "initializing " << name << " credentials cache");
+    RegisterRunner(new Auth::CredentialCacheRr(name, this));
+}
+
+Auth::User::Pointer
+CredentialsCache::lookup(const SBuf &userKey) const
+{
+    debugs(29, 6, "lookup for " << userKey);
+    auto p = store_.find(userKey);
+    if (p == store_.end())
+        return User::Pointer(nullptr);
+    return p->second;
+}
+
+void
+CredentialsCache::Cleanup(void *data)
+{
+    debugs(29, 5, "checkpoint");
+    // data is this in disguise
+    CredentialsCache *self = static_cast<CredentialsCache *>(data);
+    self->cleanup();
+}
+
+void
+CredentialsCache::cleanup()
+{
+    // cache entries with expiretime <= expirationTime are to be evicted
+    const time_t expirationTime =  current_time.tv_sec - ::Config.authenticateTTL;
+
+    const auto end = store_.end();
+    for (auto i = store_.begin(); i != end;) {
+        debugs(29, 6, "considering " << i->first << "(expires in " <<
+               (expirationTime - i->second->expiretime) << " sec)");
+        if (i->second->expiretime <= expirationTime) {
+            debugs(29, 6, "evicting " << i->first);
+            i = store_.erase(i); //erase advances i
+        } else {
+            ++i;
+        }
+    }
+    scheduleCleanup();
+}
+
+void
+CredentialsCache::insert(const SBuf &userKey, Auth::User::Pointer anAuth_user)
+{
+    debugs(29, 6, "adding " << userKey << " (" << anAuth_user->username() << ")");
+    store_[userKey] = anAuth_user;
+    scheduleCleanup();
+}
+
+// generates the list of cached usernames in a format that is convenient
+// to merge with equivalent lists obtained from other CredentialsCaches.
+std::vector<Auth::User::Pointer>
+CredentialsCache::sortedUsersList() const
+{
+    std::vector<Auth::User::Pointer> rv(size(), nullptr);
+    std::transform(store_.begin(), store_.end(), rv.begin(),
+    [](StoreType::value_type v) { return v.second; }
+                  );
+    std::sort(rv.begin(), rv.end(),
+    [](const Auth::User::Pointer &lhs, const Auth::User::Pointer &rhs) {
+        return strcmp(lhs->username(), rhs->username()) < 0;
+    }
+             );
+    return rv;
+}
+
+void
+CredentialsCache::scheduleCleanup()
+{
+    if (!gcScheduled_ && store_.size()) {
+        gcScheduled_ = true;
+        eventAdd(cacheCleanupEventName, &CredentialsCache::Cleanup,
+                 this, ::Config.authenticateGCInterval, 1);
+    }
+}
+
+void
+CredentialsCache::doConfigChangeCleanup()
+{
+    // purge expired entries entirely
+    cleanup();
+    // purge the ACL match data stored in the credentials
+    for (auto i : store_) {
+        aclCacheMatchFlush(&i.second->proxy_match_cache);
+    }
+}
+
+} /* namespace Auth */
+

=== added file 'src/auth/CredentialsCache.h'
--- src/auth/CredentialsCache.h	1970-01-01 00:00:00 +0000
+++ src/auth/CredentialsCache.h	2015-09-25 10:36:22 +0000
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_SRC_AUTH_CREDENTIALSCACHE_H
+#define SQUID_SRC_AUTH_CREDENTIALSCACHE_H
+
+#include "auth/User.h"
+#include "cbdata.h"
+#include "SBufAlgos.h"
+
+#include <unordered_map>
+
+namespace Auth {
+
+/// Cache of Auth::User credentials, keyed by Auth::User::userKey
+class CredentialsCache
+{
+private:
+    CBDATA_CLASS(CredentialsCache);
+
+    /// key is User::userKey(), mapped value is User::Pointer
+    typedef std::unordered_map<SBuf, Auth::User::Pointer> StoreType;
+
+public:
+    explicit CredentialsCache(const char *name, const char * const eventName);
+
+    ~CredentialsCache() = default;
+    CredentialsCache(const CredentialsCache&) = delete;
+    CredentialsCache& operator=(const CredentialsCache&) = delete;
+
+    /// \returns a pointer to cached credentials, or nil if none found
+    Auth::User::Pointer lookup(const SBuf &userKey) const;
+
+    /// add an user to the cache with the provided key
+    void insert(const SBuf &userKey, Auth::User::Pointer anAuth_user);
+
+    /// clear cache
+    void reset() { store_.clear(); }
+
+    /// \returns number of cached usernames
+    size_t size() const { return store_.size(); }
+
+    /** periodic cleanup function, removes timed-out entries
+     *
+     * Must be static to support EVH interface. Argument will be this
+     */
+    static void Cleanup(void *);
+
+    /// cache garbage collection, removes timed-out entries
+    void cleanup();
+
+    /**
+     * Cleanup cache data after a reconfiguration has occured.
+     * Similar to cleanup() but also flushes stale config dependent
+     * state from retained entries.
+     */
+    void doConfigChangeCleanup();
+
+    /// \returns alphanumerically sorted list of usernames
+    std::vector<Auth::User::Pointer> sortedUsersList() const;
+
+private:
+    void scheduleCleanup();
+
+    /// whether a cleanup (garbage collection) event has been scheduled
+    bool gcScheduled_;
+
+    StoreType store_;
+
+    // c-string raw pointer used as event name
+    const char * const cacheCleanupEventName;
+};
+
+} /* namespace Auth */
+
+#endif /* SQUID_SRC_AUTH_CREDENTIALSCACHE_H */
+

=== modified file 'src/auth/Gadgets.cc'
--- src/auth/Gadgets.cc	2015-01-13 07:25:36 +0000
+++ src/auth/Gadgets.cc	2015-09-25 10:36:23 +0000
@@ -1,40 +1,45 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 29    Authenticator */
 
 /* The functions in this file handle authentication.
  * They DO NOT perform access control or auditing.
  * See acl.c for access control and client_side.c for auditing */
 
 #include "squid.h"
 #include "acl/Acl.h"
 #include "acl/FilledChecklist.h"
 #include "auth/AclProxyAuth.h"
+#include "auth/basic/User.h"
 #include "auth/Config.h"
+#include "auth/CredentialsCache.h"
+#include "auth/digest/User.h"
 #include "auth/Gadgets.h"
+#include "auth/negotiate/User.h"
+#include "auth/ntlm/User.h"
 #include "auth/Scheme.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
 #include "client_side.h"
 #include "globals.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 
 /**** PUBLIC FUNCTIONS (ALL GENERIC!)  ****/
 
 int
 authenticateActiveSchemeCount(void)
 {
     int rv = 0;
 
     for (Auth::ConfigVector::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
         if ((*i)->configured())
             ++rv;
 
     debugs(29, 9, HERE << rv << " active.");
@@ -47,84 +52,85 @@
 {
     int rv = Auth::Scheme::GetSchemes().size();
 
     debugs(29, 9, HERE << rv << " active.");
 
     return rv;
 }
 
 static void
 authenticateRegisterWithCacheManager(Auth::ConfigVector * config)
 {
     for (Auth::ConfigVector::iterator i = config->begin(); i != config->end(); ++i) {
         Auth::Config *scheme = *i;
         scheme->registerWithCacheManager();
     }
 }
 
 void
 authenticateInit(Auth::ConfigVector * config)
 {
-    /* Do this first to clear memory and remove dead state on a reconfigure */
-    if (proxy_auth_username_cache)
-        Auth::User::CachedACLsReset();
-
     /* If we do not have any auth config state to create stop now. */
     if (!config)
         return;
 
     for (Auth::ConfigVector::iterator i = config->begin(); i != config->end(); ++i) {
         Auth::Config *schemeCfg = *i;
 
         if (schemeCfg->configured())
             schemeCfg->init(schemeCfg);
     }
 
-    if (!proxy_auth_username_cache)
-        Auth::User::cacheInit();
-
     authenticateRegisterWithCacheManager(config);
 }
 
 void
 authenticateRotate(void)
 {
     for (Auth::ConfigVector::iterator i = Auth::TheConfig.begin(); i != Auth::TheConfig.end(); ++i)
         if ((*i)->configured())
             (*i)->rotateHelpers();
 }
 
 void
 authenticateReset(void)
 {
-    debugs(29, 2, HERE << "Reset authentication State.");
+    debugs(29, 2, "Reset authentication State.");
 
-    /* free all username cache entries */
-    hash_first(proxy_auth_username_cache);
-    AuthUserHashPointer *usernamehash;
-    while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
-        debugs(29, 5, HERE << "Clearing entry for user: " << usernamehash->user()->username());
-        hash_remove_link(proxy_auth_username_cache, (hash_link *)usernamehash);
-        delete usernamehash;
-    }
+    // username cache is cleared via Runner registry
 
     /* schedule shutdown of the helpers */
     authenticateRotate();
 
     /* free current global config details too. */
     Auth::TheConfig.clear();
 }
 
-AuthUserHashPointer::AuthUserHashPointer(Auth::User::Pointer anAuth_user):
-    auth_user(anAuth_user)
+std::vector<Auth::User::Pointer>
+authenticateCachedUsersList()
 {
-    key = (void *)anAuth_user->userKey();
-    next = NULL;
-    hash_join(proxy_auth_username_cache, (hash_link *) this);
-}
-
-Auth::User::Pointer
-AuthUserHashPointer::user() const
-{
-    return auth_user;
+    auto aucp_compare = [=](const Auth::User::Pointer lhs, const Auth::User::Pointer rhs) {
+        return lhs->userKey() < rhs->userKey();
+    };
+    std::vector<Auth::User::Pointer> v1, v2, rv, u1, u2;
+    if (Auth::Config::Find("basic") != nullptr)
+        u1 = Auth::Basic::User::Cache()->sortedUsersList();
+    if (Auth::Config::Find("digest") != nullptr)
+        u2 = Auth::Digest::User::Cache()->sortedUsersList();
+    v1.reserve(u1.size()+u2.size());
+    std::merge(u1.begin(), u1.end(),u2.begin(), u2.end(),
+               std::back_inserter(v1), aucp_compare);
+    u1.clear();
+    u2.clear();
+    if (Auth::Config::Find("negotiate") != nullptr)
+        u1 = Auth::Negotiate::User::Cache()->sortedUsersList();
+    if (Auth::Config::Find("ntlm") != nullptr)
+        u2 = Auth::Ntlm::User::Cache()->sortedUsersList();
+    v2.reserve(u1.size()+u2.size());
+    std::merge(u1.begin(), u1.end(),u2.begin(), u2.end(),
+               std::back_inserter(v2), aucp_compare);
+    rv.reserve(v1.size()+v2.size());
+    std::merge(v1.begin(), v1.end(),v2.begin(), v2.end(),
+               std::back_inserter(rv), aucp_compare);
+    return rv;
 }
 

=== modified file 'src/auth/Gadgets.h'
--- src/auth/Gadgets.h	2015-01-13 07:25:36 +0000
+++ src/auth/Gadgets.h	2015-09-25 10:36:24 +0000
@@ -1,86 +1,62 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_AUTH_GADGETS_H
 #define SQUID_AUTH_GADGETS_H
 
 #if USE_AUTH
 
 #include "auth/Config.h"
 #include "auth/User.h"
 #include "hash.h"
 
-/**
- \ingroup AuthAPI
- *
- * This is used to link AuthUsers objects into the username cache.
- * Because some schemes may link in aliases to a user,
- * the link is not part of the AuthUser structure itself.
- *
- * Code must not hold onto copies of these objects.
- * They may exist only so long as the AuthUser being referenced
- * is recorded in the cache. Any caller using hash_remove_link
- * must then delete the AuthUserHashPointer.
- */
-class AuthUserHashPointer : public hash_link
-{
-    MEMPROXY_CLASS(AuthUserHashPointer);
-
-public:
-    AuthUserHashPointer(Auth::User::Pointer);
-    ~AuthUserHashPointer() { auth_user = NULL; };
-
-    Auth::User::Pointer user() const;
-
-private:
-    Auth::User::Pointer auth_user;
-};
-
 namespace Auth
 {
 class Scheme;
 }
 class ConnStateData;
 class StoreEntry;
 
 /**
  \ingroup AuthAPI
  \todo this should be a generic cachemgr API type ?
  */
 typedef void AUTHSSTATS(StoreEntry *);
 
 /// \ingroup AuthAPI
 void authenticateInit(Auth::ConfigVector *);
 
 /** \ingroup AuthAPI
  * Remove all idle authentication state. Intended for use by reconfigure.
  *
  * Removes the username cache contents and global configuration state.
  * Stops just short of detaching the auth components completely.
  *
  * Currently active requests should finish. Howevee new requests will not use
  * authentication unless something causes the global config to be rebuilt.
  * Such as a configure load action adding config and re-running authenticateInit().
  */
 void authenticateReset(void);
 
 void authenticateRotate(void);
 
 /// \ingroup AuthAPI
 void authenticateFreeProxyAuthUserACLResults(void *data);
 /// \ingroup AuthAPI
 int authenticateActiveSchemeCount(void);
 /// \ingroup AuthAPI
 int authenticateSchemeCount(void);
 
 /// \ingroup AuthAPI
 void authenticateOnCloseConnection(ConnStateData * conn);
 
+std::vector<Auth::User::Pointer> authenticateCachedUsersList();
+
 #endif /* USE_AUTH */
 #endif /* SQUID_AUTH_GADGETS_H */
 

=== modified file 'src/auth/Makefile.am'
--- src/auth/Makefile.am	2015-01-13 07:25:36 +0000
+++ src/auth/Makefile.am	2015-09-25 10:36:24 +0000
@@ -4,40 +4,42 @@
 ## contributions from numerous individuals and organizations.
 ## Please see the COPYING and CONTRIBUTORS files for details.
 ##
 
 include $(top_srcdir)/src/Common.am
 include $(top_srcdir)/src/TestHeaders.am
 
 SUBDIRS = $(AUTH_MODULES)
 DIST_SUBDIRS = basic digest negotiate ntlm
 
 noinst_LTLIBRARIES = libauth.la libacls.la
 ## not needed? $(AUTH_LIBS_TO_BUILD)
 ## EXTRA_LTLIBRARIES = libdigest.la libntlm.la libnegotiate.la
 
 ## authentication framework; this library is always built
 libauth_la_SOURCES = \
 	Type.h \
 	Type.cc \
 	Config.cc \
 	Config.h \
+	CredentialsCache.h \
+	CredentialsCache.cc \
 	CredentialState.cc \
 	CredentialState.h \
 	Gadgets.cc \
 	Gadgets.h \
 	QueueNode.h \
 	Scheme.cc \
 	Scheme.h \
 	State.h \
 	State.cc \
 	User.h \
 	User.cc \
 	UserRequest.h \
 	UserRequest.cc
 
 libauth_la_LIBADD = $(AUTH_LIBS_TO_BUILD)
 libauth_la_DEPENDENCIES = $(AUTH_LIBS_TO_BUILD)
 
 ## authentication-dependent ACLs and authentication code they share
 libacls_la_SOURCES = \
 	Acl.cc \

=== modified file 'src/auth/User.cc'
--- src/auth/User.cc	2015-08-24 14:20:07 +0000
+++ src/auth/User.cc	2015-09-25 10:36:29 +0000
@@ -1,45 +1,44 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 29    Authenticator */
 
 #include "squid.h"
 #include "acl/Acl.h"
 #include "acl/Gadgets.h"
 #include "auth/Config.h"
+#include "auth/CredentialsCache.h"
 #include "auth/Gadgets.h"
 #include "auth/User.h"
 #include "auth/UserRequest.h"
 #include "event.h"
 #include "globals.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "Store.h"
 
-time_t Auth::User::last_discard = 0;
-
 Auth::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
     auth_type(Auth::AUTH_UNKNOWN),
     config(aConfig),
     ipcount(0),
     expiretime(0),
     credentials_state(Auth::Unchecked),
     username_(nullptr),
     requestRealm_(aRequestRealm)
 {
     proxy_match_cache.head = proxy_match_cache.tail = NULL;
     ip_list.head = ip_list.tail = NULL;
     debugs(29, 5, HERE << "Initialised auth_user '" << this << "'.");
 }
 
 Auth::CredentialState
 Auth::User::credentials() const
 {
     return credentials_state;
 }
 
@@ -122,116 +121,40 @@
 
 Auth::User::~User()
 {
     debugs(29, 5, HERE << "Freeing auth_user '" << this << "'.");
     assert(LockCount() == 0);
 
     /* free cached acl results */
     aclCacheMatchFlush(&proxy_match_cache);
 
     /* free seen ip address's */
     clearIp();
 
     if (username_)
         xfree((char*)username_);
 
     /* prevent accidental reuse */
     auth_type = Auth::AUTH_UNKNOWN;
 }
 
 void
-Auth::User::cacheInit(void)
-{
-    if (!proxy_auth_username_cache) {
-        /* First time around, 7921 should be big enough */
-        proxy_auth_username_cache = hash_create((HASHCMP *) strcmp, 7921, hash_string);
-        assert(proxy_auth_username_cache);
-        eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
-        last_discard = squid_curtime;
-    }
-}
-
-void
-Auth::User::CachedACLsReset()
-{
-    /*
-     * This must complete all at once, because we are ensuring correctness.
-     */
-    AuthUserHashPointer *usernamehash;
-    Auth::User::Pointer auth_user;
-    debugs(29, 3, HERE << "Flushing the ACL caches for all users.");
-    hash_first(proxy_auth_username_cache);
-
-    while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
-        auth_user = usernamehash->user();
-        /* free cached acl results */
-        aclCacheMatchFlush(&auth_user->proxy_match_cache);
-    }
-
-    debugs(29, 3, HERE << "Finished.");
-}
-
-void
-Auth::User::cacheCleanup(void *)
-{
-    /*
-     * We walk the hash by username as that is the unique key we use.
-     * For big hashs we could consider stepping through the cache, 100/200
-     * entries at a time. Lets see how it flys first.
-     */
-    AuthUserHashPointer *usernamehash;
-    Auth::User::Pointer auth_user;
-    char const *username = NULL;
-    debugs(29, 3, HERE << "Cleaning the user cache now");
-    debugs(29, 3, HERE << "Current time: " << current_time.tv_sec);
-    hash_first(proxy_auth_username_cache);
-
-    while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
-        auth_user = usernamehash->user();
-        username = auth_user->username();
-
-        /* if we need to have indedendent expiry clauses, insert a module call
-         * here */
-        debugs(29, 4, HERE << "Cache entry:\n\tType: " <<
-               auth_user->auth_type << "\n\tUsername: " << username <<
-               "\n\texpires: " <<
-               (long int) (auth_user->expiretime + ::Config.authenticateTTL) <<
-               "\n\treferences: " << auth_user->LockCount());
-
-        if (auth_user->expiretime + ::Config.authenticateTTL <= current_time.tv_sec) {
-            debugs(29, 5, HERE << "Removing user " << username << " from cache due to timeout.");
-
-            /* Old credentials are always removed. Existing users must hold their own
-             * Auth::User::Pointer to the credentials. Cache exists only for finding
-             * and re-using current valid credentials.
-             */
-            hash_remove_link(proxy_auth_username_cache, usernamehash);
-            delete usernamehash;
-        }
-    }
-
-    debugs(29, 3, HERE << "Finished cleaning the user cache.");
-    eventAdd("User Cache Maintenance", cacheCleanup, NULL, ::Config.authenticateGCInterval, 1);
-    last_discard = squid_curtime;
-}
-
-void
 Auth::User::clearIp()
 {
     AuthUserIP *ipdata, *tempnode;
 
     ipdata = (AuthUserIP *) ip_list.head;
 
     while (ipdata) {
         tempnode = (AuthUserIP *) ipdata->node.next;
         /* walk the ip list */
         dlinkDelete(&ipdata->node, &ip_list);
         delete ipdata;
         /* catch incipient underflow */
         assert(ipcount);
         -- ipcount;
         ipdata = tempnode;
     }
 
     /* integrity check */
     assert(ipcount == 0);
 }
@@ -299,79 +222,60 @@
 
     dlinkAddTail(ipdata, &ipdata->node, &ip_list);
 
     ++ipcount;
 
     debugs(29, 2, HERE << "user '" << username() << "' has been seen at a new IP address (" << ipaddr << ")");
 }
 
 SBuf
 Auth::User::BuildUserKey(const char *username, const char *realm)
 {
     SBuf key;
     if (realm)
         key.Printf("%s:%s", username, realm);
     else
         key.append(username, strlen(username));
     return key;
 }
 
 /**
- * Add the Auth::User structure to the username cache.
- */
-void
-Auth::User::addToNameCache()
-{
-    /* AuthUserHashPointer will self-register with the username cache */
-    new AuthUserHashPointer(this);
-}
-
-/**
  * Dump the username cache statictics for viewing...
  */
 void
-Auth::User::UsernameCacheStats(StoreEntry *output)
+Auth::User::CredentialsCacheStats(StoreEntry *output)
 {
-    AuthUserHashPointer *usernamehash;
-
-    /* overview of username cache */
-    storeAppendPrintf(output, "Cached Usernames: %d of %d\n", proxy_auth_username_cache->count, proxy_auth_username_cache->size);
-    storeAppendPrintf(output, "Next Garbage Collection in %d seconds.\n",
-                      static_cast<int32_t>(last_discard + ::Config.authenticateGCInterval - squid_curtime));
-
-    /* cache dump column titles */
-    storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\n",
+    auto userlist = authenticateCachedUsersList();
+    storeAppendPrintf(output, "Cached Usernames: %d", static_cast<int32_t>(userlist.size()));
+    storeAppendPrintf(output, "\n%-15s %-9s %-9s %-9s %s\t%s\n",
                       "Type",
                       "State",
                       "Check TTL",
                       "Cache TTL",
-                      "Username");
+                      "Username", "Key");
     storeAppendPrintf(output, "--------------- --------- --------- --------- ------------------------------\n");
-
-    hash_first(proxy_auth_username_cache);
-    while ((usernamehash = ((AuthUserHashPointer *) hash_next(proxy_auth_username_cache)))) {
-        Auth::User::Pointer auth_user = usernamehash->user();
-
-        storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\n",
+    for ( auto auth_user : userlist ) {
+        storeAppendPrintf(output, "%-15s %-9s %-9d %-9d %s\t" SQUIDSBUFPH "\n",
                           Auth::Type_str[auth_user->auth_type],
                           CredentialState_str[auth_user->credentials()],
                           auth_user->ttl(),
                           static_cast<int32_t>(auth_user->expiretime - squid_curtime + ::Config.authenticateTTL),
-                          auth_user->username()
+                          auth_user->username(),
+                          SQUIDSBUFPRINT(auth_user->userKey())
                          );
     }
 }
 
 void
 Auth::User::username(char const *aString)
 {
     if (aString) {
         assert(!username_);
         username_ = xstrdup(aString);
         // NP: param #2 is working around a c_str() data-copy performance regression
         userKey_ = BuildUserKey(username_, (!requestRealm_.isEmpty() ? requestRealm_.c_str() : NULL));
     } else {
         safe_free(username_);
         userKey_.clear();
     }
 }
 

=== modified file 'src/auth/User.h'
--- src/auth/User.h	2015-02-08 11:40:30 +0000
+++ src/auth/User.h	2015-09-25 10:56:43 +0000
@@ -1,129 +1,124 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_AUTH_USER_H
 #define SQUID_AUTH_USER_H
 
 #if USE_AUTH
 
 #include "auth/CredentialState.h"
 #include "auth/Type.h"
+#include "base/CbcPointer.h"
 #include "base/RefCount.h"
 #include "dlink.h"
 #include "ip/Address.h"
 #include "Notes.h"
 #include "SBuf.h"
 
-class AuthUserHashPointer;
 class StoreEntry;
 
 namespace Auth
 {
 
 class Config;
+class CredentialsCache;
 
 /**
- *  \ingroup AuthAPI
  * This is the main user related structure. It stores user-related data,
  * and is persistent across requests. It can even persist across
  * multiple external authentications. One major benefit of preserving this
  * structure is the cached ACL match results. This structure, is private to
  * the authentication framework.
  */
 class User : public RefCountable
 {
 public:
     typedef RefCount<User> Pointer;
 
+protected:
+    User(Auth::Config *, const char *requestRealm);
+public:
+    virtual ~User();
+
     /* extra fields for proxy_auth */
-    /* auth_type and auth_module are deprecated. Do Not add new users of these fields.
-     * Aim to remove shortly
-     */
     /** \deprecated this determines what scheme owns the user data. */
     Auth::Type auth_type;
     /** the config for this user */
     Auth::Config *config;
     dlink_list proxy_match_cache;
     size_t ipcount;
     long expiretime;
 
     /// list of key=value pairs the helper produced
     NotePairs notes;
 
 public:
-    static void cacheInit();
-    static void CachedACLsReset();
     static SBuf BuildUserKey(const char *username, const char *realm);
 
     void absorb(Auth::User::Pointer from);
-    virtual ~User();
     char const *username() const { return username_; }
     void username(char const *); ///< set stored username and userKey
 
     // NP: key is set at the same time as username_. Until then both are empty/NULL.
-    const char *userKey() {return !userKey_.isEmpty() ? userKey_.c_str() : NULL;}
+    const SBuf userKey() const {return userKey_;}
 
     /**
      * How long these credentials are still valid for.
      * Negative numbers means already expired.
      */
     virtual int32_t ttl() const = 0;
 
     /* Manage list of IPs using this username */
     void clearIp();
     void removeIp(Ip::Address);
     void addIp(Ip::Address);
 
-    void addToNameCache();
-    static void UsernameCacheStats(StoreEntry * output);
+    /// add the Auth::User to the protocol-specific username cache.
+    virtual void addToNameCache() = 0;
+    static void CredentialsCacheStats(StoreEntry * output);
+
+    // userKey ->Auth::User::Pointer cache
+    // must be reimplemented in subclasses
+    static CbcPointer<Auth::CredentialsCache> Cache();
 
     CredentialState credentials() const;
     void credentials(CredentialState);
 
 private:
     /**
      * The current state these credentials are in:
      *   Unchecked
      *   Authenticated
      *   Pending helper result
      *   Handshake happening in stateful auth.
      *   Failed auth
      */
     CredentialState credentials_state;
 
-protected:
-    User(Auth::Config *, const char *requestRealm);
-
 private:
     /**
-     * Garbage Collection for the username cache.
-     */
-    static void cacheCleanup(void *unused);
-    static time_t last_discard; /// Time of last username cache garbage collection.
-
-    /**
      * DPW 2007-05-08
      * The username_ memory will be allocated via
      * xstrdup().  It is our responsibility.
      */
     const char *username_;
 
     /**
      * A realm for the user depending on request, designed to identify users,
      * with the same username and different authentication domains.
      */
     SBuf requestRealm_;
 
     /**
      * A Unique key for the user, consist by username and requestRealm_
      */
     SBuf userKey_;
 
     /** what ip addresses has this user been seen at?, plus a list length cache */
     dlink_list ip_list;
 };

=== modified file 'src/auth/basic/Config.cc'
--- src/auth/basic/Config.cc	2015-08-04 19:57:07 +0000
+++ src/auth/basic/Config.cc	2015-09-25 10:36:34 +0000
@@ -1,39 +1,40 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 29    Authenticator */
 
 /* The functions in this file handle authentication.
  * They DO NOT perform access control or auditing.
  * See acl.c for access control and client_side.c for auditing */
 
 #include "squid.h"
 #include "auth/basic/Config.h"
 #include "auth/basic/Scheme.h"
 #include "auth/basic/User.h"
 #include "auth/basic/UserRequest.h"
+#include "auth/CredentialsCache.h"
 #include "auth/Gadgets.h"
 #include "auth/State.h"
 #include "cache_cf.h"
 #include "charset.h"
 #include "helper.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "mgr/Registration.h"
 #include "rfc1738.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "util.h"
 #include "uudecode.h"
 #include "wordlist.h"
 
 /* Basic Scheme */
 static AUTHSSTATS authenticateBasicStats;
 
 helper *basicauthenticators = NULL;
 
@@ -229,41 +230,41 @@
         auth_user_request->setDenyMessage("no password was present in the HTTP [proxy-]authorization header. This is most likely a browser bug");
     } else {
         if (local_basic->passwd[0] == '\0') {
             debugs(29, 4, HERE << "Disallowing empty password. User is '" << local_basic->username() << "'");
             safe_free(local_basic->passwd);
             auth_user_request->setDenyMessage("Request denied because you provided an empty password. Users MUST have a password.");
         }
     }
 
     xfree(cleartext);
 
     if (!local_basic->valid()) {
         lb->auth_type = Auth::AUTH_BROKEN;
         auth_user_request->user(lb);
         return auth_user_request;
     }
 
     /* now lookup and see if we have a matching auth_user structure in memory. */
     Auth::User::Pointer auth_user;
 
-    if ((auth_user = findUserInCache(lb->userKey(), Auth::AUTH_BASIC)) == NULL) {
+    if (!(auth_user = Auth::Basic::User::Cache()->lookup(lb->userKey()))) {
         /* the user doesn't exist in the username cache yet */
         /* save the credentials */
         debugs(29, 9, HERE << "Creating new user '" << lb->username() << "'");
         /* set the auth_user type */
         lb->auth_type = Auth::AUTH_BASIC;
         /* current time for timeouts */
         lb->expiretime = current_time.tv_sec;
 
         /* this basic_user struct is the 'lucky one' to get added to the username cache */
         /* the requests after this link to the basic_user */
         /* store user in hash */
         lb->addToNameCache();
 
         auth_user = lb;
         assert(auth_user != NULL);
     } else {
         /* replace the current cached password with the new one */
         Auth::Basic::User *basic_auth = dynamic_cast<Auth::Basic::User *>(auth_user.getRaw());
         assert(basic_auth);
         basic_auth->updateCached(local_basic);

=== modified file 'src/auth/basic/User.cc'
--- src/auth/basic/User.cc	2015-01-13 07:25:36 +0000
+++ src/auth/basic/User.cc	2015-09-25 10:36:37 +0000
@@ -1,31 +1,32 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "auth/basic/Config.h"
 #include "auth/basic/User.h"
+#include "auth/CredentialsCache.h"
 #include "Debug.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 
 Auth::Basic::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
     Auth::User(aConfig, aRequestRealm),
     passwd(NULL),
     queue(NULL),
     currentRequest(NULL)
 {}
 
 Auth::Basic::User::~User()
 {
     safe_free(passwd);
 }
 
 int32_t
 Auth::Basic::User::ttl() const
 {
     if (credentials() != Auth::Ok && credentials() != Auth::Pending)
@@ -62,20 +63,33 @@
 Auth::Basic::User::updateCached(Auth::Basic::User *from)
 {
     debugs(29, 9, HERE << "Found user '" << from->username() << "' already in the user cache as '" << this << "'");
 
     assert(strcmp(from->username(), username()) == 0);
 
     if (strcmp(from->passwd, passwd)) {
         debugs(29, 4, HERE << "new password found. Updating in user master record and resetting auth state to unchecked");
         credentials(Auth::Unchecked);
         xfree(passwd);
         passwd = from->passwd;
         from->passwd = NULL;
     }
 
     if (credentials() == Auth::Failed) {
         debugs(29, 4, HERE << "last attempt to authenticate this user failed, resetting auth state to unchecked");
         credentials(Auth::Unchecked);
     }
 }
 
+CbcPointer<Auth::CredentialsCache>
+Auth::Basic::User::Cache()
+{
+    static CbcPointer<Auth::CredentialsCache> p(new Auth::CredentialsCache("basic", "GC Basic user credentials"));
+    return p;
+}
+
+void
+Auth::Basic::User::addToNameCache()
+{
+    Cache()->insert(userKey(), this);
+}
+

=== modified file 'src/auth/basic/User.h'
--- src/auth/basic/User.h	2015-01-13 07:25:36 +0000
+++ src/auth/basic/User.h	2015-09-26 10:14:22 +0000
@@ -11,41 +11,45 @@
 
 #include "auth/User.h"
 #include "auth/UserRequest.h"
 
 namespace Auth
 {
 
 class Config;
 class QueueNode;
 
 namespace Basic
 {
 
 /** User credentials for the Basic authentication protocol */
 class User : public Auth::User
 {
     MEMPROXY_CLASS(Auth::Basic::User);
 
 public:
     User(Auth::Config *, const char *requestRealm);
-    ~User();
+    virtual ~User();
     bool authenticated() const;
     bool valid() const;
 
     /** Update the cached password for a username. */
     void updateCached(User *from);
-    virtual int32_t ttl() const;
+    virtual int32_t ttl() const override;
+
+    /* Auth::User API */
+    static CbcPointer<Auth::CredentialsCache> Cache();
+    virtual void addToNameCache() override;
 
     char *passwd;
 
     QueueNode *queue;
 
 private:
     Auth::UserRequest::Pointer currentRequest;
 };
 
 } // namespace Basic
 } // namespace Auth
 
 #endif /* _SQUID_AUTH_BASIC_USER_H */
 

=== modified file 'src/auth/digest/Config.cc'
--- src/auth/digest/Config.cc	2015-08-25 14:36:54 +0000
+++ src/auth/digest/Config.cc	2015-09-25 10:36:42 +0000
@@ -1,35 +1,36 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 29    Authenticator */
 
 /* The functions in this file handle authentication.
  * They DO NOT perform access control or auditing.
  * See acl.c for access control and client_side.c for auditing */
 
 #include "squid.h"
+#include "auth/CredentialsCache.h"
 #include "auth/digest/Config.h"
 #include "auth/digest/Scheme.h"
 #include "auth/digest/User.h"
 #include "auth/digest/UserRequest.h"
 #include "auth/Gadgets.h"
 #include "auth/State.h"
 #include "base/LookupTable.h"
 #include "base64.h"
 #include "cache_cf.h"
 #include "event.h"
 #include "helper.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "mgr/Registration.h"
 #include "rfc2617.h"
 #include "SBuf.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "StrList.h"
@@ -1025,41 +1026,41 @@
     authDigestNonceLink(nonce);
 
     /* check that we're not being hacked / the username hasn't changed */
     if (nonce->user && strcmp(username, nonce->user->username())) {
         debugs(29, 2, "Username for the nonce does not equal the username for the request");
         rv = authDigestLogUsername(username, digest_request, aRequestRealm);
         safe_free(username);
         return rv;
     }
 
     /* the method we'll check at the authenticate step as well */
 
     /* we don't send or parse opaques. Ok so we're flexable ... */
 
     /* find the user */
     Auth::Digest::User *digest_user;
 
     Auth::User::Pointer auth_user;
 
     SBuf key = Auth::User::BuildUserKey(username, aRequestRealm);
-    if (key.isEmpty() || (auth_user = findUserInCache(key.c_str(), Auth::AUTH_DIGEST)) == NULL) {
+    if (key.isEmpty() || !(auth_user = Auth::Digest::User::Cache()->lookup(key))) {
         /* the user doesn't exist in the username cache yet */
         debugs(29, 9, "Creating new digest user '" << username << "'");
         digest_user = new Auth::Digest::User(this, aRequestRealm);
         /* auth_user is a parent */
         auth_user = digest_user;
         /* save the username */
         digest_user->username(username);
         /* set the user type */
         digest_user->auth_type = Auth::AUTH_DIGEST;
         /* this auth_user struct is the one to get added to the
          * username cache */
         /* store user in hash's */
         digest_user->addToNameCache();
 
         /*
          * Add the digest to the user so we can tell if a hacking
          * or spoofing attack is taking place. We do this by assuming
          * the user agent won't change user name without warning.
          */
         authDigestUserLinkNonce(digest_user, nonce);

=== modified file 'src/auth/digest/Scheme.cc'
--- src/auth/digest/Scheme.cc	2015-01-13 07:25:36 +0000
+++ src/auth/digest/Scheme.cc	2015-09-25 10:36:44 +0000
@@ -20,52 +20,33 @@
 {
     if (_instance == NULL) {
         _instance = new Auth::Digest::Scheme();
         AddScheme(_instance);
     }
     return _instance;
 }
 
 char const *
 Auth::Digest::Scheme::type() const
 {
     return "digest";
 }
 
 void
 Auth::Digest::Scheme::shutdownCleanup()
 {
     if (_instance == NULL)
         return;
 
-    PurgeCredentialsCache();
     authenticateDigestNonceShutdown();
 
     _instance = NULL;
     debugs(29, DBG_CRITICAL, "Shutdown: Digest authentication.");
 }
 
 Auth::Config *
 Auth::Digest::Scheme::createConfig()
 {
     Auth::Digest::Config *digestCfg = new Auth::Digest::Config;
     return dynamic_cast<Auth::Config*>(digestCfg);
 }
 
-void
-Auth::Digest::Scheme::PurgeCredentialsCache(void)
-{
-    AuthUserHashPointer *usernamehash;
-
-    debugs(29, 2, HERE << "Erasing Digest authentication credentials from username cache.");
-    hash_first(proxy_auth_username_cache);
-
-    while ((usernamehash = static_cast<AuthUserHashPointer *>(hash_next(proxy_auth_username_cache)) )) {
-        Auth::User::Pointer auth_user = usernamehash->user();
-
-        if (strcmp(auth_user->config->type(), "digest") == 0) {
-            hash_remove_link(proxy_auth_username_cache, static_cast<hash_link*>(usernamehash));
-            delete usernamehash;
-        }
-    }
-}
-

=== modified file 'src/auth/digest/Scheme.h'
--- src/auth/digest/Scheme.h	2015-01-13 07:25:36 +0000
+++ src/auth/digest/Scheme.h	2015-09-25 10:36:44 +0000
@@ -21,33 +21,27 @@
 class Scheme : public Auth::Scheme
 {
 
 public:
     static Auth::Scheme::Pointer GetInstance();
     Scheme() {};
     virtual ~Scheme() {}
 
     /* per scheme */
     virtual char const *type () const;
     virtual void shutdownCleanup();
     virtual Auth::Config *createConfig();
 
     /* Not implemented */
     Scheme(Scheme const &);
     Scheme &operator=(Scheme const &);
 
 private:
     static Auth::Scheme::Pointer _instance;
 
-    /**
-     * Remove all cached user credentials from circulation.
-     * Intended for use during shutdown procedure.
-     * After calling this all newly received credentials must be re-authenticated.
-     */
-    static void PurgeCredentialsCache(void);
 };
 
 } // namespace Digest
 } // namespace Auth
 
 #endif /* SQUID_AUTH_DIGEST_SCHEME_H */
 

=== modified file 'src/auth/digest/User.cc'
--- src/auth/digest/User.cc	2015-01-13 07:25:36 +0000
+++ src/auth/digest/User.cc	2015-09-25 10:36:45 +0000
@@ -1,29 +1,30 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
+#include "auth/CredentialsCache.h"
 #include "auth/digest/Config.h"
 #include "auth/digest/User.h"
 #include "Debug.h"
 #include "dlink.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 
 Auth::Digest::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
     Auth::User(aConfig, aRequestRealm),
     HA1created(0)
 {
     memset(HA1, 0, sizeof(HA1));
 }
 
 Auth::Digest::User::~User()
 {
     dlink_node *link, *tmplink;
     link = nonces.head;
 
     while (link) {
@@ -55,20 +56,33 @@
         return min(-1, global_ttl);
 
     int32_t nonce_ttl = latest_nonce - current_time.tv_sec + static_cast<Config*>(Auth::Config::Find("digest"))->noncemaxduration;
 
     return min(nonce_ttl, global_ttl);
 }
 
 digest_nonce_h *
 Auth::Digest::User::currentNonce()
 {
     digest_nonce_h *nonce = NULL;
     dlink_node *link = nonces.tail;
     if (link) {
         nonce = static_cast<digest_nonce_h *>(link->data);
         if (authDigestNonceIsStale(nonce))
             nonce = NULL;
     }
     return nonce;
 }
 
+CbcPointer<Auth::CredentialsCache>
+Auth::Digest::User::Cache()
+{
+    static CbcPointer<Auth::CredentialsCache> p(new Auth::CredentialsCache("digest","GC Digest user credentials"));
+    return p;
+}
+
+void
+Auth::Digest::User::addToNameCache()
+{
+    Cache()->insert(userKey(), this);
+}
+

=== modified file 'src/auth/digest/User.h'
--- src/auth/digest/User.h	2015-01-13 07:25:36 +0000
+++ src/auth/digest/User.h	2015-09-26 10:14:28 +0000
@@ -1,44 +1,49 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef _SQUID_AUTH_DIGEST_USER_H
 #define _SQUID_AUTH_DIGEST_USER_H
 
+#include "auth/digest/Config.h"
 #include "auth/User.h"
+#include "rfc2617.h"
 
 namespace Auth
 {
 namespace Digest
 {
 
 /** User credentials for the Digest authentication protocol */
 class User : public Auth::User
 {
     MEMPROXY_CLASS(Auth::Digest::User);
 
 public:
     User(Auth::Config *, const char *requestRealm);
-    ~User();
+    virtual ~User();
     int authenticated() const;
+    virtual int32_t ttl() const override;
 
-    virtual int32_t ttl() const;
+    /* Auth::User API */
+    static CbcPointer<Auth::CredentialsCache> Cache();
+    virtual void addToNameCache() override;
 
     HASH HA1;
     int HA1created;
 
     /* what nonces have been allocated to this user */
     dlink_list nonces;
 
     digest_nonce_h * currentNonce();
 };
 
 } // namespace Digest
 } // namespace Auth
 
 #endif /* _SQUID_AUTH_DIGEST_USER_H */
 

=== modified file 'src/auth/negotiate/User.cc'
--- src/auth/negotiate/User.cc	2015-01-13 07:25:36 +0000
+++ src/auth/negotiate/User.cc	2015-09-25 10:36:53 +0000
@@ -1,29 +1,43 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "auth/Config.h"
+#include "auth/CredentialsCache.h"
 #include "auth/negotiate/User.h"
 #include "Debug.h"
 
 Auth::Negotiate::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
     Auth::User(aConfig, aRequestRealm)
 {
 }
 
 Auth::Negotiate::User::~User()
 {
     debugs(29, 5, HERE << "doing nothing to clear Negotiate scheme data for '" << this << "'");
 }
 
 int32_t
 Auth::Negotiate::User::ttl() const
 {
     return -1; // Negotiate cannot be cached.
 }
 
+CbcPointer<Auth::CredentialsCache>
+Auth::Negotiate::User::Cache()
+{
+    static CbcPointer<Auth::CredentialsCache> p(new Auth::CredentialsCache("negotiate", "GC Negotiate user credentials"));
+    return p;
+}
+
+void
+Auth::Negotiate::User::addToNameCache()
+{
+    Cache()->insert(userKey(), this);
+}
+

=== modified file 'src/auth/negotiate/User.h'
--- src/auth/negotiate/User.h	2015-01-13 07:25:36 +0000
+++ src/auth/negotiate/User.h	2015-09-26 10:14:36 +0000
@@ -9,31 +9,35 @@
 #ifndef _SQUID_AUTH_NEGOTIATE_USER_H
 #define _SQUID_AUTH_NEGOTIATE_USER_H
 
 #include "auth/User.h"
 
 namespace Auth
 {
 
 class Config;
 
 namespace Negotiate
 {
 
 /** User credentials for the Negotiate authentication protocol */
 class User : public Auth::User
 {
     MEMPROXY_CLASS(Auth::Negotiate::User);
 
 public:
     User(Auth::Config *, const char *requestRealm);
-    ~User();
-    virtual int32_t ttl() const;
+    virtual ~User();
+    virtual int32_t ttl() const override;
+
+    /* Auth::User API */
+    static CbcPointer<Auth::CredentialsCache> Cache();
+    virtual void addToNameCache() override;
 
     dlink_list proxy_auth_list;
 };
 
 } // namespace Negotiate
 } // namespace Auth
 
 #endif /* _SQUID_AUTH_NEGOTIATE_USER_H */
 

=== modified file 'src/auth/negotiate/UserRequest.cc'
--- src/auth/negotiate/UserRequest.cc	2015-08-24 14:20:07 +0000
+++ src/auth/negotiate/UserRequest.cc	2015-09-25 10:36:55 +0000
@@ -1,31 +1,33 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "AccessLogEntry.h"
+#include "auth/CredentialsCache.h"
 #include "auth/negotiate/Config.h"
+#include "auth/negotiate/User.h"
 #include "auth/negotiate/UserRequest.h"
 #include "auth/State.h"
 #include "auth/User.h"
 #include "client_side.h"
 #include "fatal.h"
 #include "format/Format.h"
 #include "globals.h"
 #include "helper.h"
 #include "helper/Reply.h"
 #include "HttpHeaderTools.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
 #include "SquidTime.h"
 
 Auth::Negotiate::UserRequest::UserRequest() :
     authserver(nullptr),
     server_blob(nullptr),
     client_blob(nullptr),
     waiting(0),
@@ -314,58 +316,53 @@
 
     case Helper::Okay: {
         const char *userNote = reply.notes.findFirst("user");
         const char *tokenNote = reply.notes.findFirst("token");
         if (userNote == NULL || tokenNote == NULL) {
             // XXX: handle a success with no username better
             /* protocol error */
             fatalf("authenticateNegotiateHandleReply: *** Unsupported helper response ***, '%s'\n", reply.other().content());
             break;
         }
 
         /* we're finished, release the helper */
         auth_user_request->user()->username(userNote);
         auth_user_request->denyMessage("Login successful");
         safe_free(lm_request->server_blob);
         lm_request->server_blob = xstrdup(tokenNote);
         lm_request->releaseAuthServer();
 
         /* connection is authenticated */
         debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
-        /* see if this is an existing user */
-        AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->userKey()));
-        Auth::User::Pointer local_auth_user = lm_request->user();
-        while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NEGOTIATE ||
-                                strcmp(usernamehash->user()->userKey(), auth_user_request->user()->userKey()) != 0))
-            usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
-        if (usernamehash) {
+        auto local_auth_user = lm_request->user();
+        auto cached_user = Auth::Negotiate::User::Cache()->lookup(auth_user_request->user()->userKey());
+        if (!cached_user) {
+            local_auth_user->addToNameCache();
+        } else {
             /* we can't seamlessly recheck the username due to the
              * challenge-response nature of the protocol.
              * Just free the temporary auth_user after merging as
              * much of it new state into the existing one as possible */
-            usernamehash->user()->absorb(local_auth_user);
+            cached_user->absorb(local_auth_user);
             /* from here on we are working with the original cached credentials. */
-            local_auth_user = usernamehash->user();
+            local_auth_user = cached_user;
             auth_user_request->user(local_auth_user);
-        } else {
-            /* store user in hash's */
-            local_auth_user->addToNameCache();
         }
         /* set these to now because this is either a new login from an
          * existing user or a new user */
         local_auth_user->expiretime = current_time.tv_sec;
         auth_user_request->user()->credentials(Auth::Ok);
         debugs(29, 4, HERE << "Successfully validated user via Negotiate. Username '" << auth_user_request->user()->username() << "'");
     }
     break;
 
     case Helper::Error: {
         const char *messageNote = reply.notes.find("message");
         const char *tokenNote = reply.notes.findFirst("token");
 
         /* authentication failure (wrong password, etc.) */
         if (messageNote != NULL)
             auth_user_request->denyMessage(messageNote);
         else
             auth_user_request->denyMessage("Negotiate Authentication denied with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);

=== modified file 'src/auth/ntlm/User.cc'
--- src/auth/ntlm/User.cc	2015-01-13 07:25:36 +0000
+++ src/auth/ntlm/User.cc	2015-09-25 10:37:01 +0000
@@ -1,29 +1,43 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "auth/Config.h"
+#include "auth/CredentialsCache.h"
 #include "auth/ntlm/User.h"
 #include "Debug.h"
 
 Auth::Ntlm::User::User(Auth::Config *aConfig, const char *aRequestRealm) :
     Auth::User(aConfig, aRequestRealm)
 {
 }
 
 Auth::Ntlm::User::~User()
 {
     debugs(29, 5, HERE << "doing nothing to clear NTLM scheme data for '" << this << "'");
 }
 
 int32_t
 Auth::Ntlm::User::ttl() const
 {
     return -1; // NTLM credentials cannot be cached.
 }
 
+CbcPointer<Auth::CredentialsCache>
+Auth::Ntlm::User::Cache()
+{
+    static CbcPointer<Auth::CredentialsCache> p(new Auth::CredentialsCache("ntlm", "GC NTLM user credentials"));
+    return p;
+}
+
+void
+Auth::Ntlm::User::addToNameCache()
+{
+    Cache()->insert(userKey(), this);
+}
+

=== modified file 'src/auth/ntlm/User.h'
--- src/auth/ntlm/User.h	2015-01-13 07:25:36 +0000
+++ src/auth/ntlm/User.h	2015-09-26 10:14:44 +0000
@@ -9,32 +9,35 @@
 #ifndef _SQUID_AUTH_NTLM_USER_H
 #define _SQUID_AUTH_NTLM_USER_H
 
 #include "auth/User.h"
 
 namespace Auth
 {
 
 class Config;
 
 namespace Ntlm
 {
 
 /** User credentials for the NTLM authentication protocol */
 class User : public Auth::User
 {
     MEMPROXY_CLASS(Auth::Ntlm::User);
 
 public:
     User(Auth::Config *, const char *requestRealm);
-    ~User();
+    virtual ~User();
+    virtual int32_t ttl() const override;
 
-    virtual int32_t ttl() const;
+    /* Auth::User API */
+    static CbcPointer<Auth::CredentialsCache> Cache();
+    virtual void addToNameCache() override;
 
     dlink_list proxy_auth_list;
 };
 
 } // namespace Ntlm
 } // namespace Auth
 
 #endif /* _SQUID_AUTH_NTLM_USER_H */
 

=== modified file 'src/auth/ntlm/UserRequest.cc'
--- src/auth/ntlm/UserRequest.cc	2015-08-24 14:20:07 +0000
+++ src/auth/ntlm/UserRequest.cc	2015-09-25 10:37:02 +0000
@@ -1,31 +1,33 @@
 /*
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "AccessLogEntry.h"
+#include "auth/CredentialsCache.h"
 #include "auth/ntlm/Config.h"
+#include "auth/ntlm/User.h"
 #include "auth/ntlm/UserRequest.h"
 #include "auth/State.h"
 #include "cbdata.h"
 #include "client_side.h"
 #include "fatal.h"
 #include "format/Format.h"
 #include "globals.h"
 #include "helper.h"
 #include "helper/Reply.h"
 #include "HttpMsg.h"
 #include "HttpRequest.h"
 #include "MemBuf.h"
 #include "SquidTime.h"
 
 Auth::Ntlm::UserRequest::UserRequest() :
     authserver(nullptr),
     server_blob(nullptr),
     client_blob(nullptr),
     waiting(0),
     request(nullptr)
@@ -309,57 +311,53 @@
 
     case Helper::Okay: {
         /* we're finished, release the helper */
         const char *userLabel = reply.notes.findFirst("user");
         if (!userLabel) {
             auth_user_request->user()->credentials(Auth::Failed);
             safe_free(lm_request->server_blob);
             lm_request->releaseAuthServer();
             debugs(29, DBG_CRITICAL, "ERROR: NTLM Authentication helper returned no username. Result: " << reply);
             break;
         }
         auth_user_request->user()->username(userLabel);
         auth_user_request->denyMessage("Login successful");
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
 
         debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << userLabel << "'");
         /* connection is authenticated */
         debugs(29, 4, HERE << "authenticated user " << auth_user_request->user()->username());
         /* see if this is an existing user */
-        AuthUserHashPointer *usernamehash = static_cast<AuthUserHashPointer *>(hash_lookup(proxy_auth_username_cache, auth_user_request->user()->userKey()));
-        Auth::User::Pointer local_auth_user = lm_request->user();
-        while (usernamehash && (usernamehash->user()->auth_type != Auth::AUTH_NTLM ||
-                                strcmp(usernamehash->user()->userKey(), auth_user_request->user()->userKey()) != 0))
-            usernamehash = static_cast<AuthUserHashPointer *>(usernamehash->next);
-        if (usernamehash) {
+        auto local_auth_user = lm_request->user();
+        auto cached_user = Auth::Ntlm::User::Cache()->lookup(auth_user_request->user()->userKey());
+        if (!cached_user) {
+            local_auth_user->addToNameCache();
+        } else {
             /* we can't seamlessly recheck the username due to the
              * challenge-response nature of the protocol.
              * Just free the temporary auth_user after merging as
              * much of it new state into the existing one as possible */
-            usernamehash->user()->absorb(local_auth_user);
+            cached_user->absorb(local_auth_user);
             /* from here on we are working with the original cached credentials. */
-            local_auth_user = usernamehash->user();
+            local_auth_user = cached_user;
             auth_user_request->user(local_auth_user);
-        } else {
-            /* store user in hash's */
-            local_auth_user->addToNameCache();
         }
         /* set these to now because this is either a new login from an
          * existing user or a new user */
         local_auth_user->expiretime = current_time.tv_sec;
         auth_user_request->user()->credentials(Auth::Ok);
         debugs(29, 4, HERE << "Successfully validated user via NTLM. Username '" << auth_user_request->user()->username() << "'");
     }
     break;
 
     case Helper::Error: {
         /* authentication failure (wrong password, etc.) */
         const char *errNote = reply.notes.find("message");
         if (errNote != NULL)
             auth_user_request->denyMessage(errNote);
         else
             auth_user_request->denyMessage("NTLM Authentication denied with no reason given");
         auth_user_request->user()->credentials(Auth::Failed);
         safe_free(lm_request->server_blob);
         lm_request->releaseAuthServer();
         debugs(29, 4, "Failed validating user via NTLM. Result: " << reply);

=== modified file 'src/globals.h'
--- src/globals.h	2015-01-13 07:25:36 +0000
+++ src/globals.h	2015-09-25 10:40:01 +0000
@@ -71,41 +71,40 @@
 extern double request_failure_ratio;    /* 0.0 */
 extern int store_hash_buckets;  /* 0 */
 extern hash_table *store_table; /* NULL */
 extern int hot_obj_count;   /* 0 */
 extern int CacheDigestHashFuncCount;    /* 4 */
 extern CacheDigest *store_digest;   /* NULL */
 extern const char *StoreDigestFileName;     /* "store_digest" */
 extern const char *StoreDigestMimeStr;  /* "application/cache-digest" */
 
 extern const char *MultipartMsgBoundaryStr; /* "Unique-Squid-Separator" */
 #if USE_HTTP_VIOLATIONS
 extern int refresh_nocache_hack;    /* 0 */
 #endif
 
 extern int store_open_disk_fd;  /* 0 */
 extern const char *SwapDirType[];
 extern int store_swap_low;  /* 0 */
 extern int store_swap_high; /* 0 */
 extern size_t store_pages_max;  /* 0 */
 extern int64_t store_maxobjsize;    /* 0 */
-extern hash_table *proxy_auth_username_cache;   /* NULL */
 extern int incoming_sockets_accepted;
 #if _SQUID_WINDOWS_
 extern unsigned int WIN32_Socks_initialized;    /* 0 */
 #endif
 #if _SQUID_WINDOWS_
 extern unsigned int WIN32_OS_version;   /* 0 */
 extern char *WIN32_OS_string;           /* NULL */
 extern char *WIN32_Command_Line;        /* NULL */
 extern char *WIN32_Service_Command_Line; /* NULL */
 extern unsigned int WIN32_run_mode;     /* _WIN_SQUID_RUN_MODE_INTERACTIVE */
 #endif
 
 extern int ssl_ex_index_server; /* -1 */
 extern int ssl_ctx_ex_index_dont_verify_domain; /* -1 */
 extern int ssl_ex_index_cert_error_check;   /* -1 */
 extern int ssl_ex_index_ssl_error_detail;      /* -1 */
 extern int ssl_ex_index_ssl_peeked_cert;      /* -1 */
 extern int ssl_ex_index_ssl_errors;   /* -1 */
 extern int ssl_ex_index_ssl_cert_chain;  /* -1 */
 extern int ssl_ex_index_ssl_validation_counter;  /* -1 */

=== modified file 'src/stat.cc'
--- src/stat.cc	2015-06-11 04:51:10 +0000
+++ src/stat.cc	2015-09-25 10:44:40 +0000
@@ -1199,41 +1199,41 @@
     Mgr::RegisterAction("counters", "Traffic and Resource Counters",
                         &Mgr::CountersAction::Create, 0, 1);
     Mgr::RegisterAction("peer_select", "Peer Selection Algorithms",
                         statPeerSelect, 0, 1);
     Mgr::RegisterAction("digest_stats", "Cache Digest and ICP blob",
                         statDigestBlob, 0, 1);
     Mgr::RegisterAction("5min", "5 Minute Average of Counters",
                         &Mgr::IntervalAction::Create5min, 0, 1);
     Mgr::RegisterAction("60min", "60 Minute Average of Counters",
                         &Mgr::IntervalAction::Create60min, 0, 1);
     Mgr::RegisterAction("utilization", "Cache Utilization",
                         statUtilization, 0, 1);
     Mgr::RegisterAction("histograms", "Full Histogram Counts",
                         statCountersHistograms, 0, 1);
     Mgr::RegisterAction("active_requests",
                         "Client-side Active Requests",
                         statClientRequests, 0, 1);
 #if USE_AUTH
     Mgr::RegisterAction("username_cache",
                         "Active Cached Usernames",
-                        Auth::User::UsernameCacheStats, 0, 1);
+                        Auth::User::CredentialsCacheStats, 0, 1);
 #endif
 #if DEBUG_OPENFD
     Mgr::RegisterAction("openfd_objects", "Objects with Swapout files open",
                         statOpenfdObj, 0, 0);
 #endif
 #if STAT_GRAPHS
     Mgr::RegisterAction("graph_variables", "Display cache metrics graphically",
                         statGraphDump, 0, 1);
 #endif
 }
 
 void
 statInit(void)
 {
     int i;
     debugs(18, 5, "statInit: Initializing...");
 
     for (i = 0; i < N_COUNT_HIST; ++i)
         statCountersInit(&CountHist[i]);
 

=== modified file 'src/tests/stub_libauth.cc'
--- src/tests/stub_libauth.cc	2015-08-04 19:57:07 +0000
+++ src/tests/stub_libauth.cc	2015-09-25 10:45:34 +0000
@@ -8,65 +8,58 @@
 
 #include "squid.h"
 
 #define STUB_API "auth/libauth.la"
 #include "STUB.h"
 
 #if USE_AUTH
 #include "auth/Config.h"
 Auth::UserRequest::Pointer Auth::Config::CreateAuthUser(const char *, AccessLogEntry::Pointer &al) STUB_RETVAL(NULL)
 Auth::Config * Auth::Config::Find(const char *) STUB_RETVAL(NULL)
 void Auth::Config::registerWithCacheManager(void) STUB_NOP
 Auth::ConfigVector Auth::TheConfig;
 
 #include "auth/Gadgets.h"
 int authenticateActiveSchemeCount(void) STUB_RETVAL(0)
 int authenticateSchemeCount(void) STUB_RETVAL(0)
 void authenticateInit(Auth::ConfigVector *) STUB
 void authenticateRotate(void) STUB
 void authenticateReset(void) STUB
 
-AuthUserHashPointer::AuthUserHashPointer(Auth::User::Pointer anAuth_user) STUB
-Auth::User::Pointer AuthUserHashPointer::user() const STUB_RETVAL(NULL)
-
 #include "auth/Scheme.h"
 #include <vector>
 std::vector<Auth::Scheme::Pointer> *Auth::Scheme::_Schemes = NULL;
 void Auth::Scheme::AddScheme(Auth::Scheme::Pointer) STUB
 Auth::Scheme::Pointer Auth::Scheme::Find(const char *) STUB_RETVAL(NULL)
 std::vector<Auth::Scheme::Pointer> & Auth::Scheme::GetSchemes() STUB_RETVAL(*_Schemes);
 void Auth::Scheme::FreeAll() STUB
 
 #include "auth/User.h"
 Auth::User::User(Auth::Config *, const char *) STUB
 Auth::CredentialState Auth::User::credentials() const STUB_RETVAL(credentials_state)
 void Auth::User::credentials(CredentialState) STUB
 void Auth::User::absorb(Auth::User::Pointer) STUB
 Auth::User::~User() STUB_NOP
-void Auth::User::cacheInit(void) STUB
-void Auth::User::CachedACLsReset() STUB
-void Auth::User::cacheCleanup(void *) STUB
 void Auth::User::clearIp() STUB
 void Auth::User::removeIp(Ip::Address) STUB
 void Auth::User::addIp(Ip::Address) STUB
-void Auth::User::addToNameCache() STUB
-void Auth::User::UsernameCacheStats(StoreEntry *) STUB
+void Auth::User::CredentialsCacheStats(StoreEntry *) STUB
 
 #include "auth/UserRequest.h"
 char const * Auth::UserRequest::username() const STUB_RETVAL("stub_username")
 void Auth::UserRequest::start(HttpRequest *, AccessLogEntry::Pointer &, AUTHCB *, void *) STUB
 bool Auth::UserRequest::valid() const STUB_RETVAL(false)
 void * Auth::UserRequest::operator new (size_t) STUB_RETVAL((void *)1)
 void Auth::UserRequest::operator delete (void *) STUB
 Auth::UserRequest::UserRequest() STUB
 Auth::UserRequest::~UserRequest() STUB
 void Auth::UserRequest::setDenyMessage(char const *) STUB
 char const * Auth::UserRequest::getDenyMessage() STUB_RETVAL("stub")
 char const * Auth::UserRequest::denyMessage(char const * const) STUB_RETVAL("stub")
 void authenticateAuthUserRequestRemoveIp(Auth::UserRequest::Pointer, Ip::Address const &) STUB
 void authenticateAuthUserRequestClearIp(Auth::UserRequest::Pointer) STUB
 int authenticateAuthUserRequestIPCount(Auth::UserRequest::Pointer) STUB_RETVAL(0)
 int authenticateUserAuthenticated(Auth::UserRequest::Pointer) STUB_RETVAL(0)
 Auth::Direction Auth::UserRequest::direction() STUB_RETVAL(Auth::CRED_ERROR)
 void Auth::UserRequest::addAuthenticationInfoHeader(HttpReply *, int) STUB
 void Auth::UserRequest::addAuthenticationInfoTrailer(HttpReply *, int) STUB
 void Auth::UserRequest::releaseAuthServer() STUB



More information about the squid-dev mailing list