refactor: replace NYTimes/gziphandler with stdlib compress/gzip

Drop the external gziphandler dependency in hiveui. The replacement
uses a simple http.ResponseWriter wrapper that checks Content-Type
against an allowlist before enabling gzip compression. Same behavior,
zero external deps.
This commit is contained in:
Julian Sommer
2026-03-05 11:16:44 +10:00
parent 8af3d2e5fa
commit 77c6d05042
3 changed files with 62 additions and 19 deletions
-1
View File
@@ -3,7 +3,6 @@ module github.com/mulgadc/hive
go 1.26.0
require (
github.com/NYTimes/gziphandler v1.1.1
github.com/aws/aws-sdk-go v1.55.8
github.com/go-chi/chi/v5 v5.2.4
github.com/google/uuid v1.6.0
-2
View File
@@ -27,8 +27,6 @@ github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+62 -16
View File
@@ -16,7 +16,7 @@ import (
"syscall"
"time"
"github.com/NYTimes/gziphandler"
"compress/gzip"
"github.com/mulgadc/hive/hive/utils"
)
@@ -270,19 +270,65 @@ func buildCSP() string {
)
}
func gzipMiddleware(next http.Handler) http.Handler {
g, err := gziphandler.GzipHandlerWithOpts(gziphandler.ContentTypes([]string{
"text/html",
"text/css",
"application/javascript",
"text/javascript",
"application/json",
"image/svg+xml",
"text/plain",
}))
if err != nil {
slog.Warn("Failed to create gzip middleware, serving uncompressed", "error", err)
return next
}
return g(next)
// gzipContentTypes lists the MIME types eligible for gzip compression.
var gzipContentTypes = map[string]bool{
"text/html": true,
"text/css": true,
"application/javascript": true,
"text/javascript": true,
"application/json": true,
"image/svg+xml": true,
"text/plain": true,
}
// gzipResponseWriter wraps http.ResponseWriter to compress eligible responses.
type gzipResponseWriter struct {
http.ResponseWriter
gw *gzip.Writer
wroteHeader bool
compress bool
}
func (w *gzipResponseWriter) Write(b []byte) (int, error) {
if !w.wroteHeader {
w.WriteHeader(http.StatusOK)
}
if w.compress {
return w.gw.Write(b)
}
return w.ResponseWriter.Write(b)
}
func (w *gzipResponseWriter) WriteHeader(code int) {
if w.wroteHeader {
return
}
w.wroteHeader = true
ct := w.ResponseWriter.Header().Get("Content-Type")
// Strip parameters (e.g. "text/html; charset=utf-8" → "text/html")
if idx := strings.IndexByte(ct, ';'); idx != -1 {
ct = strings.TrimSpace(ct[:idx])
}
if gzipContentTypes[ct] {
w.compress = true
w.ResponseWriter.Header().Set("Content-Encoding", "gzip")
w.ResponseWriter.Header().Del("Content-Length")
}
w.ResponseWriter.WriteHeader(code)
}
func gzipMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.Header.Get("Accept-Encoding"), "gzip") {
next.ServeHTTP(w, r)
return
}
gz, _ := gzip.NewWriterLevel(w, gzip.DefaultCompression)
defer gz.Close()
grw := &gzipResponseWriter{ResponseWriter: w, gw: gz}
next.ServeHTTP(grw, r)
})
}