From 0cb1898c364273872c1273609b97231d14a02fd6 Mon Sep 17 00:00:00 2001 From: Noah Date: Wed, 24 Jun 2026 14:27:21 -0700 Subject: [PATCH 1/2] fix(middleware): fix Accept-Encoding matching in compress middleware Closes #1069 --- middleware/compress.go | 17 +++++- middleware/compress_test.go | 116 ++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 2 deletions(-) diff --git a/middleware/compress.go b/middleware/compress.go index 4e46f70a..d1b1a4fe 100644 --- a/middleware/compress.go +++ b/middleware/compress.go @@ -9,6 +9,7 @@ import ( "io" "net" "net/http" + "strconv" "strings" "sync" ) @@ -239,9 +240,21 @@ func (c *Compressor) selectEncoder(h http.Header, w io.Writer) (io.Writer, strin func matchAcceptEncoding(accepted []string, encoding string) bool { for _, v := range accepted { - if strings.Contains(v, encoding) { - return true + name, params, _ := strings.Cut(strings.TrimSpace(v), ";") + if strings.TrimSpace(name) != encoding { + continue } + for _, param := range strings.Split(params, ";") { + key, value, ok := strings.Cut(strings.TrimSpace(param), "=") + if !ok || strings.TrimSpace(key) != "q" { + continue + } + quality, err := strconv.ParseFloat(strings.TrimSpace(value), 64) + if err == nil && quality <= 0 { + return false + } + } + return true } return false } diff --git a/middleware/compress_test.go b/middleware/compress_test.go index 028343c8..30fcef6f 100644 --- a/middleware/compress_test.go +++ b/middleware/compress_test.go @@ -92,6 +92,18 @@ func TestCompressor(t *testing.T) { acceptedEncodings: []string{"nop, gzip, deflate"}, expectedEncoding: "nop", }, + { + name: "gzip rejected with q=0 returns no encoding", + path: "/gethtml", + acceptedEncodings: []string{"gzip;q=0"}, + expectedEncoding: "", + }, + { + name: "gzip rejected with q=0 falls back to deflate", + path: "/gethtml", + acceptedEncodings: []string{"gzip;q=0", "deflate"}, + expectedEncoding: "deflate", + }, } for _, tc := range tests { @@ -169,6 +181,110 @@ func TestCompressorWildcards(t *testing.T) { } } +func TestMatchAcceptEncoding(t *testing.T) { + tests := []struct { + name string + accepted []string + encoding string + want bool + }{ + { + name: "exact match", + accepted: []string{"gzip"}, + encoding: "gzip", + want: true, + }, + { + name: "no match", + accepted: []string{"deflate"}, + encoding: "gzip", + want: false, + }, + { + name: "empty list", + accepted: []string{""}, + encoding: "gzip", + want: false, + }, + { + name: "leading space", + accepted: []string{" gzip"}, + encoding: "gzip", + want: true, + }, + { + name: "trailing space", + accepted: []string{"gzip "}, + encoding: "gzip", + want: true, + }, + { + name: "br should not match b", + accepted: []string{"br"}, + encoding: "b", + want: false, + }, + { + name: "gzip should not match zip", + accepted: []string{"gzip"}, + encoding: "zip", + want: false, + }, + { + name: "q=0 rejected", + accepted: []string{"gzip;q=0"}, + encoding: "gzip", + want: false, + }, + { + name: "q=0.0 rejected", + accepted: []string{"gzip;q=0.0"}, + encoding: "gzip", + want: false, + }, + { + name: "q=0 with space", + accepted: []string{"gzip; q=0"}, + encoding: "gzip", + want: false, + }, + { + name: "q=0.5 accepted", + accepted: []string{"gzip;q=0.5"}, + encoding: "gzip", + want: true, + }, + { + name: "q=1.0 accepted", + accepted: []string{"gzip;q=1.0"}, + encoding: "gzip", + want: true, + }, + { + name: "match on second element", + accepted: []string{"deflate", "gzip"}, + encoding: "gzip", + want: true, + }, + { + name: "q=0 on second element", + accepted: []string{"deflate", "gzip;q=0"}, + encoding: "gzip", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := matchAcceptEncoding(tt.accepted, tt.encoding) + if got != tt.want { + t.Errorf("matchAcceptEncoding(%v, %q) = %v, want %v", + tt.accepted, tt.encoding, got, tt.want) + } + }) + } +} + func testRequestWithAcceptedEncodings(t *testing.T, ts *httptest.Server, method, path string, encodings ...string) (*http.Response, string) { req, err := http.NewRequest(method, ts.URL+path, nil) if err != nil { From 867de67d67cf5f8ab9a674e2405b819b74d0e16c Mon Sep 17 00:00:00 2001 From: Noah Date: Wed, 24 Jun 2026 14:52:55 -0700 Subject: [PATCH 2/2] fix(middleware): compare accept encoding tokens case-insensitively --- middleware/compress.go | 4 ++-- middleware/compress_test.go | 12 ++++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/middleware/compress.go b/middleware/compress.go index d1b1a4fe..005d6389 100644 --- a/middleware/compress.go +++ b/middleware/compress.go @@ -241,12 +241,12 @@ func (c *Compressor) selectEncoder(h http.Header, w io.Writer) (io.Writer, strin func matchAcceptEncoding(accepted []string, encoding string) bool { for _, v := range accepted { name, params, _ := strings.Cut(strings.TrimSpace(v), ";") - if strings.TrimSpace(name) != encoding { + if !strings.EqualFold(strings.TrimSpace(name), encoding) { continue } for _, param := range strings.Split(params, ";") { key, value, ok := strings.Cut(strings.TrimSpace(param), "=") - if !ok || strings.TrimSpace(key) != "q" { + if !ok || !strings.EqualFold(strings.TrimSpace(key), "q") { continue } quality, err := strconv.ParseFloat(strings.TrimSpace(value), 64) diff --git a/middleware/compress_test.go b/middleware/compress_test.go index 30fcef6f..3fcdb1a5 100644 --- a/middleware/compress_test.go +++ b/middleware/compress_test.go @@ -218,6 +218,12 @@ func TestMatchAcceptEncoding(t *testing.T) { encoding: "gzip", want: true, }, + { + name: "mixed case encoding", + accepted: []string{"GZip"}, + encoding: "gzip", + want: true, + }, { name: "br should not match b", accepted: []string{"br"}, @@ -248,6 +254,12 @@ func TestMatchAcceptEncoding(t *testing.T) { encoding: "gzip", want: false, }, + { + name: "mixed case q=0 rejected", + accepted: []string{"gzip; Q=0"}, + encoding: "gzip", + want: false, + }, { name: "q=0.5 accepted", accepted: []string{"gzip;q=0.5"},