-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Fix compress middleware Accept-Encoding parsing (substring + q=0) #1070
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -109,6 +109,125 @@ func TestCompressor(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: "match with whitespace", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{" gzip"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: true, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "match with quality value", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=1.0"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: true, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "no match", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"deflate"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "encoding excluded with q=0", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=0"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "should not substring match", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"br"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "b", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "should not substring match prefix", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"bgzip"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "q=0 in decimal form", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=0.0"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "q=0 max precision", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=0.000"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "smallest non-zero quality", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=0.001"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: true, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "whitespace around semicolon", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip ; q=0"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "q=0 with trailing params", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=0;ext=foo"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: false, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||
| name: "explicit full quality", | ||||||||||||||||||||||||||||||||||||
| accepted: []string{"gzip;q=1"}, | ||||||||||||||||||||||||||||||||||||
| encoding: "gzip", | ||||||||||||||||||||||||||||||||||||
| want: true, | ||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| 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 TestCompressorRespectsQualityZero(t *testing.T) { | ||||||||||||||||||||||||||||||||||||
| // When a client sends Accept-Encoding: gzip;q=0, the server must not | ||||||||||||||||||||||||||||||||||||
| // compress the response with gzip (q=0 means "not acceptable"). | ||||||||||||||||||||||||||||||||||||
| r := chi.NewRouter() | ||||||||||||||||||||||||||||||||||||
| compressor := NewCompressor(5, "text/html") | ||||||||||||||||||||||||||||||||||||
| r.Use(compressor.Handler) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| r.Get("/", func(w http.ResponseWriter, r *http.Request) { | ||||||||||||||||||||||||||||||||||||
| w.Header().Set("Content-Type", "text/html") | ||||||||||||||||||||||||||||||||||||
| w.Write([]byte("hello")) | ||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| ts := httptest.NewServer(r) | ||||||||||||||||||||||||||||||||||||
| defer ts.Close() | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| // gzip;q=0 means "I do NOT accept gzip" | ||||||||||||||||||||||||||||||||||||
| resp, _ := testRequestWithAcceptedEncodings(t, ts, "GET", "/", "gzip;q=0, deflate") | ||||||||||||||||||||||||||||||||||||
| if got := resp.Header.Get("Content-Encoding"); got == "gzip" { | ||||||||||||||||||||||||||||||||||||
| t.Errorf("server used gzip despite q=0; Content-Encoding = %q", got) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
Comment on lines
+224
to
+228
|
||||||||||||||||||||||||||||||||||||
| // gzip;q=0 means "I do NOT accept gzip" | |
| resp, _ := testRequestWithAcceptedEncodings(t, ts, "GET", "/", "gzip;q=0, deflate") | |
| if got := resp.Header.Get("Content-Encoding"); got == "gzip" { | |
| t.Errorf("server used gzip despite q=0; Content-Encoding = %q", got) | |
| } | |
| // gzip;q=0 means "I do NOT accept gzip", but deflate is acceptable. | |
| resp, _ := testRequestWithAcceptedEncodings(t, ts, "GET", "/", "gzip;q=0, deflate") | |
| got := resp.Header.Get("Content-Encoding") | |
| if got == "" { | |
| t.Fatalf("expected a compressed response using a fallback encoding, got no Content-Encoding header") | |
| } | |
| if got == "gzip" { | |
| t.Errorf("server used gzip despite q=0; Content-Encoding = %q", got) | |
| } | |
| if got != "deflate" { | |
| t.Errorf("expected deflate fallback when gzip is disallowed; got Content-Encoding = %q", got) | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
matchAcceptEncodingonly checksq=...if the params string starts withq=. If the header includes additional params after q (e.g.gzip;q=0;ext=1) or an invalid qvalue that still parses (NaN/Inf) / is out of [0,1], this function will treat the encoding as acceptable and return true. Consider splittingparamson;and scanning for aq=parameter, and treating parse errors / NaN / out-of-range values asq=0(i.e., not acceptable) to stay RFC-compliant and avoid accidental gzip selection.