Skip to content

Commit d66e710

Browse files
committed
http2: do not sniff body if Content-Encoding is set
Updates golang/go#31753 Change-Id: I2481ffcff6626c08ef32a02cffb3f108737fa87e Reviewed-on: https://go-review.googlesource.com/c/net/+/199841 Run-TryBot: Emmanuel Odeke <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent 72f9393 commit d66e710

File tree

2 files changed

+101
-1
lines changed

2 files changed

+101
-1
lines changed

http2/server.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2415,7 +2415,11 @@ func (rws *responseWriterState) writeChunk(p []byte) (n int, err error) {
24152415
clen = strconv.Itoa(len(p))
24162416
}
24172417
_, hasContentType := rws.snapHeader["Content-Type"]
2418-
if !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
2418+
// If the Content-Encoding is non-blank, we shouldn't
2419+
// sniff the body. See Issue golang.org/issue/31753.
2420+
ce := rws.snapHeader.Get("Content-Encoding")
2421+
hasCE := len(ce) > 0
2422+
if !hasCE && !hasContentType && bodyAllowedForStatus(rws.status) && len(p) > 0 {
24192423
ctype = http.DetectContentType(p)
24202424
}
24212425
var date string

http2/server_test.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ package http2
66

77
import (
88
"bytes"
9+
"compress/gzip"
10+
"compress/zlib"
911
"context"
1012
"crypto/tls"
1113
"errors"
@@ -3966,3 +3968,97 @@ func TestServerGracefulShutdown(t *testing.T) {
39663968
t.Errorf("Read = %v, %v; want 0, non-nil", n, err)
39673969
}
39683970
}
3971+
3972+
// Issue 31753: don't sniff when Content-Encoding is set
3973+
func TestContentEncodingNoSniffing(t *testing.T) {
3974+
type resp struct {
3975+
name string
3976+
body []byte
3977+
// setting Content-Encoding as an interface instead of a string
3978+
// directly, so as to differentiate between 3 states:
3979+
// unset, empty string "" and set string "foo/bar".
3980+
contentEncoding interface{}
3981+
wantContentType string
3982+
}
3983+
3984+
resps := []*resp{
3985+
{
3986+
name: "gzip content-encoding, gzipped", // don't sniff.
3987+
contentEncoding: "application/gzip",
3988+
wantContentType: "",
3989+
body: func() []byte {
3990+
buf := new(bytes.Buffer)
3991+
gzw := gzip.NewWriter(buf)
3992+
gzw.Write([]byte("doctype html><p>Hello</p>"))
3993+
gzw.Close()
3994+
return buf.Bytes()
3995+
}(),
3996+
},
3997+
{
3998+
name: "zlib content-encoding, zlibbed", // don't sniff.
3999+
contentEncoding: "application/zlib",
4000+
wantContentType: "",
4001+
body: func() []byte {
4002+
buf := new(bytes.Buffer)
4003+
zw := zlib.NewWriter(buf)
4004+
zw.Write([]byte("doctype html><p>Hello</p>"))
4005+
zw.Close()
4006+
return buf.Bytes()
4007+
}(),
4008+
},
4009+
{
4010+
name: "no content-encoding", // must sniff.
4011+
wantContentType: "application/x-gzip",
4012+
body: func() []byte {
4013+
buf := new(bytes.Buffer)
4014+
gzw := gzip.NewWriter(buf)
4015+
gzw.Write([]byte("doctype html><p>Hello</p>"))
4016+
gzw.Close()
4017+
return buf.Bytes()
4018+
}(),
4019+
},
4020+
{
4021+
name: "phony content-encoding", // don't sniff.
4022+
contentEncoding: "foo/bar",
4023+
body: []byte("doctype html><p>Hello</p>"),
4024+
},
4025+
{
4026+
name: "empty but set content-encoding",
4027+
contentEncoding: "",
4028+
wantContentType: "audio/mpeg",
4029+
body: []byte("ID3"),
4030+
},
4031+
}
4032+
4033+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
4034+
defer tr.CloseIdleConnections()
4035+
4036+
for _, tt := range resps {
4037+
t.Run(tt.name, func(t *testing.T) {
4038+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
4039+
if tt.contentEncoding != nil {
4040+
w.Header().Set("Content-Encoding", tt.contentEncoding.(string))
4041+
}
4042+
w.Write(tt.body)
4043+
}, optOnlyServer)
4044+
defer st.Close()
4045+
4046+
req, _ := http.NewRequest("GET", st.ts.URL, nil)
4047+
res, err := tr.RoundTrip(req)
4048+
if err != nil {
4049+
t.Fatalf("Failed to fetch URL: %v", err)
4050+
}
4051+
defer res.Body.Close()
4052+
if g, w := res.Header.Get("Content-Encoding"), tt.contentEncoding; g != w {
4053+
if w != nil { // The case where contentEncoding was set explicitly.
4054+
t.Errorf("Content-Encoding mismatch\n\tgot: %q\n\twant: %q", g, w)
4055+
} else if g != "" { // "" should be the equivalent when the contentEncoding is unset.
4056+
t.Errorf("Unexpected Content-Encoding %q", g)
4057+
}
4058+
}
4059+
if g, w := res.Header.Get("Content-Type"), tt.wantContentType; g != w {
4060+
t.Errorf("Content-Type mismatch\n\tgot: %q\n\twant: %q", g, w)
4061+
}
4062+
})
4063+
}
4064+
}

0 commit comments

Comments
 (0)