@marcel91200 / caddy Mirror · read-only / commits / 8d3a1b8b
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 13, 06:45 UTC

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>

Y.Horie committed Feb 7, 2023 at 03:29 UTC 8d3a1b8bcbc36930ca3ad0a6b38604ceaa697ecb
2 files changed +12 -2
go.mod
+1
@@ -32,6 +32,7 @@ require (
32 go.uber.org/zap v1.23.0
33 golang.org/x/crypto v0.4.0
34 golang.org/x/net v0.5.0
35 + golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
36 golang.org/x/term v0.4.0
37 google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c
38 gopkg.in/natefinch/lumberjack.v2 v2.0.0
modules/caddyhttp/caddyauth/basicauth.go
+11 -2
@@ -26,6 +26,7 @@ import (
26 "time"
27
28 "github.com/caddyserver/caddy/v2"
29 + "golang.org/x/sync/singleflight"
30 )
31
32 func init() {
@@ -142,6 +143,7 @@ func (hba *HTTPBasicAuth) Provision(ctx caddy.Context) error {
143 if hba.HashCache != nil {
144 hba.HashCache.cache = make(map[string]bool)
145 hba.HashCache.mu = new(sync.RWMutex)
146 + hba.HashCache.g = new(singleflight.Group)
147 }
148
149 return nil
@@ -190,12 +192,18 @@ func (hba HTTPBasicAuth) correctPassword(account Account, plaintextPassword []by
192 if ok {
193 return same, nil
194 }
193 -
195 // slow track: do the expensive op, then add it to the cache
195 - same, err := compare()
196 + // but perform it in a singleflight group so that multiple
197 + // parallel requests using the same password don't cause a
198 + // thundering herd problem by all performing the same hashing
199 + // operation before the first one finishes and caches it.
200 + v, err, _ := hba.HashCache.g.Do(cacheKey, func() (any, error) {
201 + return compare()
202 + })
203 if err != nil {
204 return false, err
205 }
206 + same = v.(bool)
207 hba.HashCache.mu.Lock()
208 if len(hba.HashCache.cache) >= 1000 {
209 hba.HashCache.makeRoom() // keep cache size under control
@@ -223,6 +231,7 @@ func (hba HTTPBasicAuth) promptForCredentials(w http.ResponseWriter, err error)
231 // compute on every HTTP request.
232 type Cache struct {
233 mu *sync.RWMutex
234 + g *singleflight.Group
235
236 // map of concatenated hashed password + plaintext password + salt, to result
237 cache map[string]bool