Files
authentik/internal/utils/web/http_compress.go
Dominic R 1c36b361b2 router: fix missing response headers on compressed 404 for static files (#16216)
* router: only serve dist assets if present; fallback to backend 404

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-08-18 20:18:28 +01:00

92 lines
2.6 KiB
Go

// https://github.com/gorilla/handlers/issues/259#issuecomment-2671695039
package web
import (
"bufio"
"net"
"net/http"
"github.com/gorilla/handlers"
)
// compressHandler is an HTTP handler that adds the Content-Encoding header
// back to responses when removed by the http.FileServer.
//
// handlers.CompressHandler(newCompressHandler(http.FileServer(...)))
type compressHandler struct {
// handler is an HTTP handler, usually an http.FileServer.
handler http.Handler
}
var _ http.Handler = &compressHandler{}
func NewCompressHandler(handler http.Handler) http.Handler {
h := &compressHandler{
handler: handler,
}
return handlers.CompressHandler(h)
}
func (h *compressHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// The wrapped response writer saves the incoming content encoding so
// it can be restored when writing the response headers.
cw := &compressedResponseWriter{
encoding: w.Header().Get("Content-Encoding"),
fixed: false,
responseWriter: w,
}
h.handler.ServeHTTP(cw, r)
}
// compressedResponseWriter is an http.ResponseWriter that ensures that a
// previously-set Content-Encoding header is in place before writing the
// response.
type compressedResponseWriter struct {
encoding string
fixed bool
responseWriter http.ResponseWriter
}
var _ http.ResponseWriter = &compressedResponseWriter{}
func (w *compressedResponseWriter) Header() http.Header {
return w.responseWriter.Header()
}
func (w *compressedResponseWriter) fixContentEncoding() {
if w.fixed {
return
}
w.fixed = true
// The Go 1.23 http.FileServer() removes headers like Content-Encoding
// from error responses. This breaks gzip and deflate encoding.
// https://github.com/gorilla/handlers/issues/259
// https://github.com/golang/go/issues/66343
if w.encoding == "gzip" || w.encoding == "deflate" {
if w.Header().Get("Content-Encoding") == "" {
w.Header().Set("Content-Encoding", w.encoding)
}
}
}
func (w *compressedResponseWriter) Write(data []byte) (int, error) {
w.fixContentEncoding()
return w.responseWriter.Write(data)
}
func (w *compressedResponseWriter) WriteHeader(statusCode int) {
w.fixContentEncoding()
w.responseWriter.WriteHeader(statusCode)
}
func (w *compressedResponseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
if hj, ok := w.responseWriter.(http.Hijacker); ok {
return hj.Hijack()
}
return nil, nil, http.ErrNotSupported
}
// Ensure our compressedResponseWriter implements the necessary interfaces.
var _ http.ResponseWriter = &compressedResponseWriter{}
var _ http.Hijacker = &compressedResponseWriter{}