Skip to content

Commit e5dd05d

Browse files
AlexanderYastrebovdmitshur
authored andcommitted
[internal-branch.go1.16-vendor] http2: return unexpected eof on empty response with non-zero content length
Updates golang/go#49076 Change-Id: I8b8262ba8e28a129f6aacfca43b7b8d9e605a0ce GitHub-Last-Rev: 11b92cc GitHub-Pull-Request: #114 Reviewed-on: https://go-review.googlesource.com/c/net/+/354141 Reviewed-by: Damien Neil <[email protected]> Trust: Damien Neil <[email protected]> Trust: Alexander Rakoczy <[email protected]> Run-TryBot: Alexander Rakoczy <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-on: https://go-review.googlesource.com/c/net/+/357094 Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 640e170 commit e5dd05d

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
@@ -2126,28 +2126,33 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
21262126
return nil, nil
21272127
}
21282128

2129-
streamEnded := f.StreamEnded()
2130-
isHead := cs.req.Method == "HEAD"
2131-
if !streamEnded || isHead {
2132-
res.ContentLength = -1
2133-
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2134-
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2135-
res.ContentLength = int64(cl)
2136-
} else {
2137-
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2138-
// more safe smuggling-wise to ignore.
2139-
}
2140-
} else if len(clens) > 1 {
2129+
res.ContentLength = -1
2130+
if clens := res.Header["Content-Length"]; len(clens) == 1 {
2131+
if cl, err := strconv.ParseUint(clens[0], 10, 63); err == nil {
2132+
res.ContentLength = int64(cl)
2133+
} else {
21412134
// TODO: care? unlike http/1, it won't mess up our framing, so it's
21422135
// more safe smuggling-wise to ignore.
21432136
}
2137+
} else if len(clens) > 1 {
2138+
// TODO: care? unlike http/1, it won't mess up our framing, so it's
2139+
// more safe smuggling-wise to ignore.
21442140
}
21452141

2146-
if streamEnded || isHead {
2142+
if cs.req.Method == "HEAD" {
21472143
res.Body = noBody
21482144
return res, nil
21492145
}
21502146

2147+
if f.StreamEnded() {
2148+
if res.ContentLength > 0 {
2149+
res.Body = missingBody{}
2150+
} else {
2151+
res.Body = noBody
2152+
}
2153+
return res, nil
2154+
}
2155+
21512156
cs.bufPipe.setBuffer(&dataBuffer{expected: res.ContentLength})
21522157
cs.bytesRemain = res.ContentLength
21532158
res.Body = transportResponseBody{cs}
@@ -2688,6 +2693,11 @@ func (t *Transport) logf(format string, args ...interface{}) {
26882693

26892694
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
26902695

2696+
type missingBody struct{}
2697+
2698+
func (missingBody) Close() error { return nil }
2699+
func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
2700+
26912701
func strSliceContains(ss []string, s string) bool {
26922702
for _, v := range ss {
26932703
if v == s {

http2/transport_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -5496,3 +5496,56 @@ func TestTransportTimeoutServerHangs(t *testing.T) {
54965496
}
54975497
ct.run()
54985498
}
5499+
5500+
func TestTransportContentLengthWithoutBody(t *testing.T) {
5501+
contentLength := ""
5502+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
5503+
w.Header().Set("Content-Length", contentLength)
5504+
}, optOnlyServer)
5505+
defer st.Close()
5506+
tr := &Transport{TLSClientConfig: tlsConfigInsecure}
5507+
defer tr.CloseIdleConnections()
5508+
5509+
for _, test := range []struct {
5510+
name string
5511+
contentLength string
5512+
wantBody string
5513+
wantErr error
5514+
wantContentLength int64
5515+
}{
5516+
{
5517+
name: "non-zero content length",
5518+
contentLength: "42",
5519+
wantErr: io.ErrUnexpectedEOF,
5520+
wantContentLength: 42,
5521+
},
5522+
{
5523+
name: "zero content length",
5524+
contentLength: "0",
5525+
wantErr: nil,
5526+
wantContentLength: 0,
5527+
},
5528+
} {
5529+
t.Run(test.name, func(t *testing.T) {
5530+
contentLength = test.contentLength
5531+
5532+
req, _ := http.NewRequest("GET", st.ts.URL, nil)
5533+
res, err := tr.RoundTrip(req)
5534+
if err != nil {
5535+
t.Fatal(err)
5536+
}
5537+
defer res.Body.Close()
5538+
body, err := io.ReadAll(res.Body)
5539+
5540+
if err != test.wantErr {
5541+
t.Errorf("Expected error %v, got: %v", test.wantErr, err)
5542+
}
5543+
if len(body) > 0 {
5544+
t.Errorf("Expected empty body, got: %v", body)
5545+
}
5546+
if res.ContentLength != test.wantContentLength {
5547+
t.Errorf("Expected content length %d, got: %d", test.wantContentLength, res.ContentLength)
5548+
}
5549+
})
5550+
}
5551+
}

0 commit comments

Comments
 (0)