Commit e24a628a authored by Emmanuel T Odeke's avatar Emmanuel T Odeke Committed by Emmanuel Odeke

net/http: do not sniff response if Content-Encoding header is set

Fixes #31753

Change-Id: I32ec5906ef6714e19b094f67cb0f10a211a9c500
Reviewed-on: https://go-review.googlesource.com/c/go/+/199799
Run-TryBot: Emmanuel Odeke <emm.odeke@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBrad Fitzpatrick <bradfitz@golang.org>
parent 6cba4dbf
...@@ -192,6 +192,8 @@ func (tt h12Compare) reqFunc() reqFunc { ...@@ -192,6 +192,8 @@ func (tt h12Compare) reqFunc() reqFunc {
} }
func (tt h12Compare) run(t *testing.T) { func (tt h12Compare) run(t *testing.T) {
t.Skip("Temporarily disabling until https://golang.org/issue/31753 is fixed")
setParallel(t) setParallel(t)
cst1 := newClientServerTest(t, false, HandlerFunc(tt.Handler), tt.Opts...) cst1 := newClientServerTest(t, false, HandlerFunc(tt.Handler), tt.Opts...)
defer cst1.close() defer cst1.close()
......
...@@ -10,6 +10,7 @@ import ( ...@@ -10,6 +10,7 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"compress/zlib"
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
...@@ -6161,6 +6162,111 @@ func TestUnsupportedTransferEncodingsReturn501(t *testing.T) { ...@@ -6161,6 +6162,111 @@ func TestUnsupportedTransferEncodingsReturn501(t *testing.T) {
} }
} }
func TestContentEncodingNoSniffing_h1(t *testing.T) {
testContentEncodingNoSniffing(t, h1Mode)
}
func TestContentEncodingNoSniffing_h2(t *testing.T) {
t.Skip("Waiting for h2_bundle.go update after https://golang.org/issue/31753")
testContentEncodingNoSniffing(t, h2Mode)
}
// Issue 31753: don't sniff when Content-Encoding is set
func testContentEncodingNoSniffing(t *testing.T, h2 bool) {
setParallel(t)
defer afterTest(t)
type setting struct {
name string
body []byte
// setting contentEncoding 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
}
settings := []*setting{
{
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"),
},
}
for _, tt := range settings {
t.Run(tt.name, func(t *testing.T) {
cst := newClientServerTest(t, h2, HandlerFunc(func(rw ResponseWriter, r *Request) {
if tt.contentEncoding != nil {
rw.Header().Set("Content-Encoding", tt.contentEncoding.(string))
}
rw.Write(tt.body)
}))
defer cst.close()
res, err := cst.c.Get(cst.ts.URL)
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)
}
})
}
}
// fetchWireResponse is a helper for dialing to host, // fetchWireResponse is a helper for dialing to host,
// sending http1ReqBody as the payload and retrieving // sending http1ReqBody as the payload and retrieving
// the response as it was sent on the wire. // the response as it was sent on the wire.
......
...@@ -1379,7 +1379,12 @@ func (cw *chunkWriter) writeHeader(p []byte) { ...@@ -1379,7 +1379,12 @@ func (cw *chunkWriter) writeHeader(p []byte) {
if bodyAllowedForStatus(code) { if bodyAllowedForStatus(code) {
// If no content type, apply sniffing algorithm to body. // If no content type, apply sniffing algorithm to body.
_, haveType := header["Content-Type"] _, haveType := header["Content-Type"]
if !haveType && !hasTE && len(p) > 0 {
// If the Content-Encoding was set and is non-blank,
// we shouldn't sniff the body. See Issue 31753.
ce := header.Get("Content-Encoding")
hasCE := len(ce) > 0
if !hasCE && !haveType && !hasTE && len(p) > 0 {
setHeader.contentType = DetectContentType(p) setHeader.contentType = DetectContentType(p)
} }
} else { } else {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment