|
|
@@ -6,6 +6,8 @@ package http2
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
+ "compress/gzip"
|
|
|
+ "compress/zlib"
|
|
|
"context"
|
|
|
"crypto/tls"
|
|
|
"errors"
|
|
|
@@ -3966,3 +3968,97 @@ func TestServerGracefulShutdown(t *testing.T) {
|
|
|
t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+// Issue 31753: don't sniff when Content-Encoding is set
|
|
|
+func TestContentEncodingNoSniffing(t *testing.T) {
|
|
|
+ type resp struct {
|
|
|
+ name string
|
|
|
+ body []byte
|
|
|
+ // setting Content-Encoding as an interface instead of a string
|
|
|
+ // directly, so as to differentiate between 3 states:
|
|
|
+ // unset, empty string "" and set string "foo/bar".
|
|
|
+ contentEncoding interface{}
|
|
|
+ wantContentType string
|
|
|
+ }
|
|
|
+
|
|
|
+ resps := []*resp{
|
|
|
+ {
|
|
|
+ name: "gzip content-encoding, gzipped", // don't sniff.
|
|
|
+ contentEncoding: "application/gzip",
|
|
|
+ wantContentType: "",
|
|
|
+ body: func() []byte {
|
|
|
+ buf := new(bytes.Buffer)
|
|
|
+ gzw := gzip.NewWriter(buf)
|
|
|
+ gzw.Write([]byte("doctype html><p>Hello</p>"))
|
|
|
+ gzw.Close()
|
|
|
+ return buf.Bytes()
|
|
|
+ }(),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "zlib content-encoding, zlibbed", // don't sniff.
|
|
|
+ contentEncoding: "application/zlib",
|
|
|
+ wantContentType: "",
|
|
|
+ body: func() []byte {
|
|
|
+ buf := new(bytes.Buffer)
|
|
|
+ zw := zlib.NewWriter(buf)
|
|
|
+ zw.Write([]byte("doctype html><p>Hello</p>"))
|
|
|
+ zw.Close()
|
|
|
+ return buf.Bytes()
|
|
|
+ }(),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "no content-encoding", // must sniff.
|
|
|
+ wantContentType: "application/x-gzip",
|
|
|
+ body: func() []byte {
|
|
|
+ buf := new(bytes.Buffer)
|
|
|
+ gzw := gzip.NewWriter(buf)
|
|
|
+ gzw.Write([]byte("doctype html><p>Hello</p>"))
|
|
|
+ gzw.Close()
|
|
|
+ return buf.Bytes()
|
|
|
+ }(),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "phony content-encoding", // don't sniff.
|
|
|
+ contentEncoding: "foo/bar",
|
|
|
+ body: []byte("doctype html><p>Hello</p>"),
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: "empty but set content-encoding",
|
|
|
+ contentEncoding: "",
|
|
|
+ wantContentType: "audio/mpeg",
|
|
|
+ body: []byte("ID3"),
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
+ tr := &Transport{TLSClientConfig: tlsConfigInsecure}
|
|
|
+ defer tr.CloseIdleConnections()
|
|
|
+
|
|
|
+ for _, tt := range resps {
|
|
|
+ t.Run(tt.name, func(t *testing.T) {
|
|
|
+ st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if tt.contentEncoding != nil {
|
|
|
+ w.Header().Set("Content-Encoding", tt.contentEncoding.(string))
|
|
|
+ }
|
|
|
+ w.Write(tt.body)
|
|
|
+ }, optOnlyServer)
|
|
|
+ defer st.Close()
|
|
|
+
|
|
|
+ req, _ := http.NewRequest("GET", st.ts.URL, nil)
|
|
|
+ res, err := tr.RoundTrip(req)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("Failed to fetch URL: %v", err)
|
|
|
+ }
|
|
|
+ defer res.Body.Close()
|
|
|
+ if g, w := res.Header.Get("Content-Encoding"), tt.contentEncoding; g != w {
|
|
|
+ if w != nil { // The case where contentEncoding was set explicitly.
|
|
|
+ t.Errorf("Content-Encoding mismatch\n\tgot: %q\n\twant: %q", g, w)
|
|
|
+ } else if g != "" { // "" should be the equivalent when the contentEncoding is unset.
|
|
|
+ t.Errorf("Unexpected Content-Encoding %q", g)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if g, w := res.Header.Get("Content-Type"), tt.wantContentType; g != w {
|
|
|
+ t.Errorf("Content-Type mismatch\n\tgot: %q\n\twant: %q", g, w)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|