Skip to content

Commit 028e125

Browse files
AlexanderYastrebovdmitshur
authored andcommitted
[internal-branch.go1.17-vendor] http2: return unexpected eof on empty response with non-zero content length
Updates golang/go#49077 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/+/357689 Run-TryBot: Damien Neil <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 5388f2f commit 028e125

File tree

2 files changed

+76
-13
lines changed

2 files changed

+76
-13
lines changed

http2/transport.go

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2122,28 +2122,33 @@ func (rl *clientConnReadLoop) handleResponse(cs *clientStream, f *MetaHeadersFra
21222122
return nil, nil
21232123
}
21242124

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

2142-
if streamEnded || isHead {
2138+
if cs.req.Method == "HEAD" {
21432139
res.Body = noBody
21442140
return res, nil
21452141
}
21462142

2143+
if f.StreamEnded() {
2144+
if res.ContentLength > 0 {
2145+
res.Body = missingBody{}
2146+
} else {
2147+
res.Body = noBody
2148+
}
2149+
return res, nil
2150+
}
2151+
21472152
cs.bufPipe.setBuffer(&dataBuffer{expected: res.ContentLength})
21482153
cs.bytesRemain = res.ContentLength
21492154
res.Body = transportResponseBody{cs}
@@ -2684,6 +2689,11 @@ func (t *Transport) logf(format string, args ...interface{}) {
26842689

26852690
var noBody io.ReadCloser = ioutil.NopCloser(bytes.NewReader(nil))
26862691

2692+
type missingBody struct{}
2693+
2694+
func (missingBody) Close() error { return nil }
2695+
func (missingBody) Read([]byte) (int, error) { return 0, io.ErrUnexpectedEOF }
2696+
26872697
func strSliceContains(ss []string, s string) bool {
26882698
for _, v := range ss {
26892699
if v == s {

http2/transport_test.go

Lines changed: 53 additions & 0 deletions
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)