Skip to content

Commit 11b92cc

Browse files
x/net/http2: return ErrUnexpectedEOF on empty response with non-zero content length
Fixes golang/go#46071
1 parent 59d4e92 commit 11b92cc

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

http2/transport.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -2217,28 +2217,33 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
22172217
return nil, nil
22182218
}
22192219

2220-
streamEnded := f.StreamEnded()
2221-
isHead := cs.req.Method == "HEAD"
2222-
if !streamEnded || isHead {
2223-
res.ContentLength = -1
2224-
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2225-
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2226-
res.ContentLength = int64(cl)
2227-
} else {
2228-
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2229-
// more safe smuggling-wise to ignore.
2230-
}
2231-
} else if len(clens) > 1 {
2220+
res.ContentLength = -1
2221+
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2222+
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2223+
res.ContentLength = int64(cl)
2224+
} else {
22322225
// TODO: care? unlike http/1, it won't mess up our framing, so it's
22332226
// more safe smuggling-wise to ignore.
22342227
}
2228+
} else if len(clens) > 1 {
2229+
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2230+
// more safe smuggling-wise to ignore.
22352231
}
22362232

2237-
if streamEnded || isHead {
2233+
if cs.req.Method == "HEAD" {
22382234
res.Body = noBody
22392235
return res, nil
22402236
}
22412237

2238+
if f.StreamEnded() {
2239+
if res.ContentLength > 0 {
2240+
res.Body = missingBody{}
2241+
} else {
2242+
res.Body = noBody
2243+
}
2244+
return res, nil
2245+
}
2246+
22422247
cs.bufPipe.setBuffer(&dataBuffer{expected: res.ContentLength})
22432248
cs.bytesRemain = res.ContentLength
22442249
res.Body = transportResponseBody{cs}
@@ -2786,6 +2791,11 @@ func (t *Transport) logf(format string, args ...interface{}) {
27862791

27872792
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
27882793

2794+
type missingBody struct{}
2795+
2796+
func (missingBody) Close() error { return nil }
2797+
func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
2798+
27892799
func strSliceContains(ss []string, s string) bool {
27902800
for _, v := range ss {
27912801
if v == s {

http2/transport_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -5623,3 +5623,56 @@ func TestTransportTimeoutServerHangs(t *testing.T) {
56235623
}
56245624
ct.run()
56255625
}
5626+
5627+
func TestTransportContentLengthWithoutBody(t *testing.T) {
5628+
contentLength := ""
5629+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
5630+
w.Header().Set("Content-Length", contentLength)
5631+
}, optOnlyServer)
5632+
defer st.Close()
5633+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
5634+
defer tr.CloseIdleConnections()
5635+
5636+
for _, test := range []struct {
5637+
name string
5638+
contentLength string
5639+
wantBody string
5640+
wantErr error
5641+
wantContentLength int64
5642+
}{
5643+
{
5644+
name: "non-zero content length",
5645+
contentLength: "42",
5646+
wantErr: io.ErrUnexpectedEOF,
5647+
wantContentLength: 42,
5648+
},
5649+
{
5650+
name: "zero content length",
5651+
contentLength: "0",
5652+
wantErr: nil,
5653+
wantContentLength: 0,
5654+
},
5655+
} {
5656+
t.Run(test.name, func(t *testing.T) {
5657+
contentLength = test.contentLength
5658+
5659+
req, _ := http.NewRequest("GET", st.ts.URL, nil)
5660+
res, err := tr.RoundTrip(req)
5661+
if err != nil {
5662+
t.Fatal(err)
5663+
}
5664+
defer res.Body.Close()
5665+
body, err := io.ReadAll(res.Body)
5666+
5667+
if err != test.wantErr {
5668+
t.Errorf("Expected error %v, got: %v", test.wantErr, err)
5669+
}
5670+
if len(body) > 0 {
5671+
t.Errorf("Expected empty body, got: %v", body)
5672+
}
5673+
if res.ContentLength != test.wantContentLength {
5674+
t.Errorf("Expected content length %d, got: %d", test.wantContentLength, res.ContentLength)
5675+
}
5676+
})
5677+
}
5678+
}

0 commit comments

Comments
 (0)