From 9956e64f2c9f8c055c60bc82fcbb1e979bfb0b47 Mon Sep 17 00:00:00 2001 From: Mike Crute Date: Thu, 17 Aug 2023 07:49:29 -0700 Subject: echo: support etags for static files --- echo/static_file.go | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/echo/static_file.go b/echo/static_file.go index 9723db7..258c832 100644 --- a/echo/static_file.go +++ b/echo/static_file.go @@ -1,6 +1,8 @@ package echo import ( + "crypto/sha256" + "encoding/base64" "io" "io/fs" "net/http" @@ -12,7 +14,16 @@ import ( type routeFunc func(string, echo.HandlerFunc, ...echo.MiddlewareFunc) *echo.Route +func StaticFSSha256Etags(get routeFunc, f fs.FS, prefix, root string, m ...echo.MiddlewareFunc) *echo.Route { + return staticFS(get, f, prefix, root, true, m...) +} + func StaticFS(get routeFunc, f fs.FS, prefix, root string, m ...echo.MiddlewareFunc) *echo.Route { + return staticFS(get, f, prefix, root, false, m...) +} + +// TODO: This should support HEAD requests +func staticFS(get routeFunc, f fs.FS, prefix, root string, addEtags bool, m ...echo.MiddlewareFunc) *echo.Route { if root == "" { root = "." // For security we want to restrict to CWD. } @@ -52,6 +63,25 @@ func StaticFS(get routeFunc, f fs.FS, prefix, root string, m ...echo.MiddlewareF return echo.ErrInternalServerError } + // Only do this if the consumer requests it since it could be expensive + // for high traffic sites as it requires a full read and SHA256 + // computation + // TODO: Cache this? + if addEtags { + h := sha256.New() + if _, err := io.Copy(h, fs); err != nil { + c.Logger().Errorf("Error checksumming file %s: %s", p, err) + return echo.ErrInternalServerError + } + etag := base64.RawStdEncoding.EncodeToString(h.Sum(nil)) + c.Response().Header().Add("ETag", etag) + + if _, err := fs.Seek(0, io.SeekStart); err != nil { + c.Logger().Errorf("Error seeking 0 in file %s: %s", p, err) + return echo.ErrInternalServerError + } + } + http.ServeContent(c.Response(), c.Request(), fi.Name(), fi.ModTime(), fs) return nil } -- cgit v1.2.3