Skip to content

Commit 79b4968

Browse files
authored
fix(middleware): match gzip Content-Encoding case-insensitively (#3056)
* fix(middleware): match gzip Content-Encoding case-insensitively Proxies and clients may send Content-Encoding: Gzip or GZIP. Exact string compare skipped decompression. Trim and EqualFold against "gzip". * fix(middleware): keep gzip match to EqualFold only Drop TrimSpace so the change is strictly RFC 9110 case-insensitivity for content codings, per review discussion.
1 parent ed8bbe4 commit 79b4968

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

middleware/decompress.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"compress/gzip"
88
"io"
99
"net/http"
10+
"strings"
1011
"sync"
1112

1213
"github.com/labstack/echo/v5"
@@ -82,7 +83,7 @@ func (config DecompressConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
8283
return next(c)
8384
}
8485

85-
if c.Request().Header.Get(echo.HeaderContentEncoding) != GZIPEncoding {
86+
if !isGzipContentEncoding(c.Request().Header.Get(echo.HeaderContentEncoding)) {
8687
return next(c)
8788
}
8889

@@ -127,6 +128,13 @@ func (config DecompressConfig) ToMiddleware() (echo.MiddlewareFunc, error) {
127128
}, nil
128129
}
129130

131+
132+
// isGzipContentEncoding reports whether Content-Encoding is gzip.
133+
// Content codings are case-insensitive per RFC 9110 §8.4.1.
134+
func isGzipContentEncoding(v string) bool {
135+
return strings.EqualFold(v, GZIPEncoding)
136+
}
137+
130138
// limitedGzipReader wraps a gzip reader with size limiting to prevent zip bombs
131139
type limitedGzipReader struct {
132140
*gzip.Reader

middleware/decompress_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,3 +506,29 @@ func BenchmarkDecompress_WithLimit(b *testing.B) {
506506
})(c)
507507
}
508508
}
509+
510+
func TestDecompressContentEncodingCaseInsensitive(t *testing.T) {
511+
e := echo.New()
512+
body := `{"name":"echo"}`
513+
gz, err := gzipString(body)
514+
assert.NoError(t, err)
515+
516+
for _, encoding := range []string{"GZIP", "Gzip"} {
517+
t.Run(encoding, func(t *testing.T) {
518+
req := httptest.NewRequest(http.MethodPost, "/", bytes.NewReader(gz))
519+
req.Header.Set(echo.HeaderContentEncoding, encoding)
520+
rec := httptest.NewRecorder()
521+
c := e.NewContext(req, rec)
522+
523+
h := Decompress()(func(c *echo.Context) error {
524+
b, err := io.ReadAll(c.Request().Body)
525+
if err != nil {
526+
return err
527+
}
528+
return c.String(http.StatusOK, string(b))
529+
})
530+
assert.NoError(t, h(c))
531+
assert.Equal(t, body, rec.Body.String())
532+
})
533+
}
534+
}

0 commit comments

Comments
 (0)