From 06495fa358902cfcfd5beb0ae584a3554a39935e Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Fri, 27 Jun 2025 20:39:17 +0100 Subject: [PATCH 1/3] Add caching to static files based on etag --- cmd/server/main.go | 14 +++++++++++++- internal/middleware/etag_cache.go | 21 +++------------------ 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/cmd/server/main.go b/cmd/server/main.go index 8435b13..31342ee 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,8 +6,10 @@ import ( "net" "net/http" "os" + "time" "forgejo.gwairfelin.com/max/gonotes/internal/conf" + "forgejo.gwairfelin.com/max/gonotes/internal/middleware" "forgejo.gwairfelin.com/max/gonotes/internal/notes/views" ) @@ -24,12 +26,22 @@ func main() { router := http.NewServeMux() notesRouter := views.GetRoutes("/notes") + cacheExpiration, err := time.ParseDuration("24h") + if err != nil { + log.Fatal(err) + } + + etag := middleware.NewETag("static", cacheExpiration) + router.Handle("/", logger(http.RedirectHandler("/notes/", http.StatusFound))) router.Handle("/notes/", logger(http.StripPrefix("/notes", notesRouter))) router.Handle( "/static/", logger( - http.FileServer(http.FS(conf.Static)), + middleware.StaticEtagMiddleware( + *etag, + http.FileServer(http.FS(conf.Static)), + ), ), ) diff --git a/internal/middleware/etag_cache.go b/internal/middleware/etag_cache.go index d19cfbe..d1878c2 100644 --- a/internal/middleware/etag_cache.go +++ b/internal/middleware/etag_cache.go @@ -2,7 +2,6 @@ package middleware import ( "fmt" - "hash/crc64" "log" "net/http" "strings" @@ -13,11 +12,8 @@ type ETag struct { Name string Value string Expiration time.Duration - HashTime time.Duration } -var etags = make([]*ETag, 0) - func (etag *ETag) Header() string { return fmt.Sprintf("\"pb-%s-%s\"", etag.Name, etag.Value) } @@ -46,21 +42,10 @@ func StaticEtagMiddleware(etag ETag, next http.Handler) http.Handler { } // GenerateETagFromBuffer calculates etag value from one or more buffers (e.g. embedded files) -func GenerateETagFromBuffer(name string, expiration time.Duration, buffers ...[]byte) (*ETag, error) { - start := time.Now() - hash := crc64.New(crc64.MakeTable(crc64.ECMA)) - for _, buffer := range buffers { - _, err := hash.Write(buffer) - if err != nil { - return nil, fmt.Errorf("unable to generate etag from buffer: %w", err) - } - } - etag := &ETag{ +func NewETag(name string, expiration time.Duration) *ETag { + return &ETag{ Name: name, Expiration: expiration, - Value: fmt.Sprintf("%x", hash.Sum64()), - HashTime: time.Since(start), + Value: fmt.Sprintf("%d", time.Now().Unix()), } - etags = append(etags, etag) - return etag, nil } From 6e56cefe6fdd90217b9310d7db33a5e7c55aec77 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Fri, 27 Jun 2025 20:42:07 +0100 Subject: [PATCH 2/3] Move logging middleware into internal middleware package --- cmd/server/main.go | 31 +++---------------------------- internal/middleware/logger.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 28 deletions(-) create mode 100644 internal/middleware/logger.go diff --git a/cmd/server/main.go b/cmd/server/main.go index 31342ee..baea7dd 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -33,11 +33,11 @@ func main() { etag := middleware.NewETag("static", cacheExpiration) - router.Handle("/", logger(http.RedirectHandler("/notes/", http.StatusFound))) - router.Handle("/notes/", logger(http.StripPrefix("/notes", notesRouter))) + router.Handle("/", middleware.LoggingMiddleware(http.RedirectHandler("/notes/", http.StatusFound))) + router.Handle("/notes/", middleware.LoggingMiddleware(http.StripPrefix("/notes", notesRouter))) router.Handle( "/static/", - logger( + middleware.LoggingMiddleware( middleware.StaticEtagMiddleware( *etag, http.FileServer(http.FS(conf.Static)), @@ -51,28 +51,3 @@ func main() { } log.Fatal(http.Serve(listener, router)) } - -type loggingResponseWriter struct { - http.ResponseWriter - statusCode int -} - -func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { - return &loggingResponseWriter{w, http.StatusOK} -} - -func (w *loggingResponseWriter) WriteHeader(code int) { - w.statusCode = code - w.ResponseWriter.WriteHeader(code) -} - -func logger(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - lwr := NewLoggingResponseWriter(w) - next.ServeHTTP(lwr, r) - - if conf.Conf.LogAccess { - log.Printf("%s %s %s%s %d", r.RemoteAddr, r.Method, r.Host, r.URL.Path, lwr.statusCode) - } - }) -} diff --git a/internal/middleware/logger.go b/internal/middleware/logger.go new file mode 100644 index 0000000..fe7f253 --- /dev/null +++ b/internal/middleware/logger.go @@ -0,0 +1,33 @@ +package middleware + +import ( + "log" + "net/http" + + "forgejo.gwairfelin.com/max/gonotes/internal/conf" +) + +type loggingResponseWriter struct { + http.ResponseWriter + statusCode int +} + +func NewLoggingResponseWriter(w http.ResponseWriter) *loggingResponseWriter { + return &loggingResponseWriter{w, http.StatusOK} +} + +func (w *loggingResponseWriter) WriteHeader(code int) { + w.statusCode = code + w.ResponseWriter.WriteHeader(code) +} + +func LoggingMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + lwr := NewLoggingResponseWriter(w) + next.ServeHTTP(lwr, r) + + if conf.Conf.LogAccess { + log.Printf("%s %s %s%s %d", r.RemoteAddr, r.Method, r.Host, r.URL.Path, lwr.statusCode) + } + }) +} From eb0c264ad7a2ae7f61e75a6ac5382ef05e5df582 Mon Sep 17 00:00:00 2001 From: Maximilian Friedersdorff Date: Fri, 27 Jun 2025 20:46:52 +0100 Subject: [PATCH 3/3] Add doc comments --- internal/middleware/etag_cache.go | 3 ++- internal/middleware/logger.go | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/middleware/etag_cache.go b/internal/middleware/etag_cache.go index d1878c2..47a27a2 100644 --- a/internal/middleware/etag_cache.go +++ b/internal/middleware/etag_cache.go @@ -1,3 +1,4 @@ +// Middleware to add naive caching headers package middleware import ( @@ -15,7 +16,7 @@ type ETag struct { } func (etag *ETag) Header() string { - return fmt.Sprintf("\"pb-%s-%s\"", etag.Name, etag.Value) + return fmt.Sprintf("\"gn-%s-%s\"", etag.Name, etag.Value) } func (etag *ETag) CacheControlHeader() string { diff --git a/internal/middleware/logger.go b/internal/middleware/logger.go index fe7f253..b8c279a 100644 --- a/internal/middleware/logger.go +++ b/internal/middleware/logger.go @@ -1,3 +1,4 @@ +// Middleware to Log out requests and response status code package middleware import ( @@ -7,6 +8,7 @@ import ( "forgejo.gwairfelin.com/max/gonotes/internal/conf" ) +// Response writer that store the status code for logging type loggingResponseWriter struct { http.ResponseWriter statusCode int @@ -21,6 +23,7 @@ func (w *loggingResponseWriter) WriteHeader(code int) { w.ResponseWriter.WriteHeader(code) } +// Middleware to log out requests and response status code func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lwr := NewLoggingResponseWriter(w)