Skip to content

Commit f72ff5d

Browse files
x/net/http2: return unexpected eof on empty response with non-zero content length
Fixes golang/go#46071
1 parent d2e5035 commit f72ff5d

File tree

2 files changed

+82
-13
lines changed

2 files changed

+82
-13
lines changed

http2/transport.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -2162,28 +2162,33 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
21622162
return nil, nil
21632163
}
21642164

2165-
streamEnded := f.StreamEnded()
2166-
isHead := cs.req.Method == "HEAD"
2167-
if !streamEnded || isHead {
2168-
res.ContentLength = -1
2169-
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2170-
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2171-
res.ContentLength = int64(cl)
2172-
} else {
2173-
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2174-
// more safe smuggling-wise to ignore.
2175-
}
2176-
} else if len(clens) > 1 {
2165+
res.ContentLength = -1
2166+
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2167+
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2168+
res.ContentLength = int64(cl)
2169+
} else {
21772170
// TODO: care? unlike http/1, it won't mess up our framing, so it's
21782171
// more safe smuggling-wise to ignore.
21792172
}
2173+
} else if len(clens) > 1 {
2174+
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2175+
// more safe smuggling-wise to ignore.
21802176
}
21812177

2182-
if streamEnded || isHead {
2178+
if cs.req.Method == "HEAD" {
21832179
res.Body = noBody
21842180
return res, nil
21852181
}
21862182

2183+
if f.StreamEnded() {
2184+
if res.ContentLength > 0 {
2185+
res.Body = missingBody{}
2186+
} else {
2187+
res.Body = noBody
2188+
}
2189+
return res, nil
2190+
}
2191+
21872192
cs.bufPipe.setBuffer(&dataBuffer{expected: res.ContentLength})
21882193
cs.bytesRemain = res.ContentLength
21892194
res.Body = transportResponseBody{cs}
@@ -2726,6 +2731,11 @@ func (t *Transport) logf(format string, args ...interface{}) {
27262731

27272732
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
27282733

2734+
type missingBody struct{}
2735+
2736+
func (missingBody) Close() error { return nil }
2737+
func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
2738+
27292739
func strSliceContains(ss []string, s string) bool {
27302740
for _, v := range ss {
27312741
if v == s {

http2/transport_test.go

+59
Original file line numberDiff line numberDiff line change
@@ -5593,3 +5593,62 @@ func TestTransportTimeoutServerHangs(t *testing.T) {
55935593
}
55945594
ct.run()
55955595
}
5596+
5597+
func TestTransportContentLengthWithoutBody(t *testing.T) {
5598+
contentLength := ""
5599+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
5600+
w.Header().Set("Content-Length", contentLength)
5601+
}, optOnlyServer)
5602+
defer st.Close()
5603+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
5604+
defer tr.CloseIdleConnections()
5605+
5606+
request := func() (*http.Response, string, error) {
5607+
req, _ := http.NewRequest("GET", st.ts.URL, nil)
5608+
res, err := tr.RoundTrip(req)
5609+
if err != nil {
5610+
t.Fatal(err)
5611+
}
5612+
defer res.Body.Close()
5613+
body, err := io.ReadAll(res.Body)
5614+
return res, string(body), err
5615+
}
5616+
5617+
for _, test := range []struct {
5618+
name string
5619+
contentLength string
5620+
wantBody string
5621+
wantErr error
5622+
wantContentLength int64
5623+
}{
5624+
{
5625+
name: "non-zero content length",
5626+
contentLength: "42",
5627+
wantBody: "",
5628+
wantErr: io.ErrUnexpectedEOF,
5629+
wantContentLength: 42,
5630+
},
5631+
{
5632+
name: "zero content length",
5633+
contentLength: "0",
5634+
wantBody: "",
5635+
wantErr: nil,
5636+
wantContentLength: 0,
5637+
},
5638+
} {
5639+
t.Run(test.name, func(t *testing.T) {
5640+
contentLength = test.contentLength
5641+
5642+
res, body, err := request()
5643+
if err != test.wantErr {
5644+
t.Errorf("Expected error %v, got: %v", test.wantErr, err)
5645+
}
5646+
if body != test.wantBody {
5647+
t.Errorf("Expected body %q, got: %q", test.wantBody, body)
5648+
}
5649+
if res.ContentLength != test.wantContentLength {
5650+
t.Errorf("Expected content length %d, got: %d", test.wantContentLength, res.ContentLength)
5651+
}
5652+
})
5653+
}
5654+
}

0 commit comments

Comments
 (0)