From a33560a4d15ecb99ff3fd29d8c7339a3c9f3c40a Mon Sep 17 00:00:00 2001 From: James Elliott Date: Sun, 28 May 2023 23:02:51 +1000 Subject: [PATCH] feat(middlewares): etag path-based middleware Signed-off-by: James Elliott --- internal/middlewares/etag.go | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 internal/middlewares/etag.go diff --git a/internal/middlewares/etag.go b/internal/middlewares/etag.go new file mode 100644 index 000000000..39101e5d5 --- /dev/null +++ b/internal/middlewares/etag.go @@ -0,0 +1,66 @@ +package middlewares + +import ( + "bytes" + "crypto/sha256" + "net/url" + "sync" + + "github.com/valyala/fasthttp" +) + +func NewPathETag() *PathETag { + return &PathETag{ + lock: &sync.Mutex{}, + tags: map[string][]byte{}, + } +} + +type PathETag struct { + lock *sync.Mutex + tags map[string][]byte +} + +func (m *PathETag) Middleware(next RequestHandler) RequestHandler { + hash := sha256.New() + + return func(ctx *AutheliaCtx) { + var ( + found bool + furl *url.URL + err error + ) + + if furl, err = ctx.GetXForwardedURL(); err == nil { + m.lock.Lock() + + defer m.lock.Unlock() + + if etag, ok := m.tags[furl.String()]; ok { + found = true + + ctx.Response.Header.SetBytesV(fasthttp.HeaderETag, etag) + ctx.Response.Header.SetBytesK(headerCacheControl, "public, max-age=0, must-revalidate") + + if bytes.Equal(etag, ctx.Request.Header.Peek(fasthttp.HeaderIfNoneMatch)) { + ctx.SetStatusCode(fasthttp.StatusNotModified) + + return + } + } + } + + next(ctx) + + if !found && furl != nil { + if _, err = hash.Write(ctx.Response.Body()); err != nil { + return + } + + m.tags[furl.String()] = hash.Sum(nil) + + hash.Reset() + } + } + +}