diff --git a/middleware/decompress.go b/middleware/decompress.go index 501ee6c5b..52febc680 100644 --- a/middleware/decompress.go +++ b/middleware/decompress.go @@ -7,6 +7,7 @@ import ( "compress/gzip" "io" "net/http" + "strings" "sync" "github.com/labstack/echo/v5" @@ -82,7 +83,7 @@ func (config DecompressConfig) ToMiddleware() (echo.MiddlewareFunc, error) { return next(c) } - if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding { + if !isGzipContentEncoding(c.Request().Header.Get(echo.HeaderContentEncoding)) { return next(c) } @@ -127,6 +128,13 @@ func (config DecompressConfig) ToMiddleware() (echo.MiddlewareFunc, error) { }, nil } + +// isGzipContentEncoding reports whether Content-Encoding is gzip. +// Content codings are case-insensitive per RFC 9110 ยง8.4.1. +func isGzipContentEncoding(v string) bool { + return strings.EqualFold(v, GZIPEncoding) +} + // limitedGzipReader wraps a gzip reader with size limiting to prevent zip bombs type limitedGzipReader struct { *gzip.Reader diff --git a/middleware/decompress_test.go b/middleware/decompress_test.go index 8dc3057ba..fadfa7fac 100644 --- a/middleware/decompress_test.go +++ b/middleware/decompress_test.go @@ -506,3 +506,29 @@ func BenchmarkDecompress_WithLimit(b *testing.B) { })(c) } } + +func TestDecompressContentEncodingCaseInsensitive(t *testing.T) { + e := echo.New() + body := `{"name":"echo"}` + gz, err := gzipString(body) + assert.NoError(t, err) + + for _, encoding := range []string{"GZIP", "Gzip"} { + t.Run(encoding, func(t *testing.T) { + req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(gz)) + req.Header.Set(echo.HeaderContentEncoding, encoding) + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + h := Decompress()(func(c *echo.Context) error { + b, err := io.ReadAll(c.Request().Body) + if err != nil { + return err + } + return c.String(http.StatusOK, string(b)) + }) + assert.NoError(t, h(c)) + assert.Equal(t, body, rec.Body.String()) + }) + } +}