diff options
author | Y.Horie <u5.horie@gmail.com> | 2023-02-07 03:29:03 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-02-06 18:29:03 +0000 |
commit | 8d3a1b8bcbc36930ca3ad0a6b38604ceaa697ecb (patch) | |
tree | 667145696d028e6893dc3d88a56a778a27e3366a | |
parent | ac83b7e218e3c3eb4ef26922196759434b130786 (diff) |
caddyauth: Use singleflight for basic auth (#5344)
* caddyauth: Add singleflight for basic auth
* Fixes #5338
* it occurred the thunder herd problem like this https://medium.com/@mhrlife/avoid-duplicate-requests-while-filling-cache-98c687879f59
* Update modules/caddyhttp/caddyauth/basicauth.go
Fix comment
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
---------
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
Co-authored-by: Matt Holt <mholt@users.noreply.github.com>
-rw-r--r-- | go.mod | 1 | ||||
-rw-r--r-- | modules/caddyhttp/caddyauth/basicauth.go | 13 |
2 files changed, 12 insertions, 2 deletions
@@ -32,6 +32,7 @@ require ( go.uber.org/zap v1.23.0 golang.org/x/crypto v0.4.0 golang.org/x/net v0.5.0 + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 golang.org/x/term v0.4.0 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c gopkg.in/natefinch/lumberjack.v2 v2.0.0 diff --git a/modules/caddyhttp/caddyauth/basicauth.go b/modules/caddyhttp/caddyauth/basicauth.go index eb6fd59..f515a72 100644 --- a/modules/caddyhttp/caddyauth/basicauth.go +++ b/modules/caddyhttp/caddyauth/basicauth.go @@ -26,6 +26,7 @@ import ( "time" "github.com/caddyserver/caddy/v2" + "golang.org/x/sync/singleflight" ) func init() { @@ -142,6 +143,7 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error { if hba.HashCache != nil { hba.HashCache.cache = make(map[string]bool) hba.HashCache.mu = new(sync.RWMutex) + hba.HashCache.g = new(singleflight.Group) } return nil @@ -190,12 +192,18 @@ func (hba HTTPBasicAuth) correctPassword(account Account, plaintextPassword []by if ok { return same, nil } - // slow track: do the expensive op, then add it to the cache - same, err := compare() + // but perform it in a singleflight group so that multiple + // parallel requests using the same password don't cause a + // thundering herd problem by all performing the same hashing + // operation before the first one finishes and caches it. + v, err, _ := hba.HashCache.g.Do(cacheKey, func() (any, error) { + return compare() + }) if err != nil { return false, err } + same = v.(bool) hba.HashCache.mu.Lock() if len(hba.HashCache.cache) >= 1000 { hba.HashCache.makeRoom() // keep cache size under control @@ -223,6 +231,7 @@ func (hba HTTPBasicAuth) promptForCredentials(w http.ResponseWriter, err error) // compute on every HTTP request. type Cache struct { mu *sync.RWMutex + g *singleflight.Group // map of concatenated hashed password + plaintext password + salt, to result cache map[string]bool |