Skip to content

Commit 44b7c21

Browse files
fraenkelandybons
authored andcommitted
http2: Discard data reads on HEAD requests
If a server returns a DATA frame while procesing a HEAD request, the client will discard the data. Fixes golang/go#22376 Change-Id: Ief9c17ddfe51cc17f7f6326c87330ac9d8b9d3ff Reviewed-on: https://go-review.googlesource.com/72551 Run-TryBot: Tom Bergan <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Tom Bergan <[email protected]> Reviewed-on: https://go-review.googlesource.com/88655 Run-TryBot: Andrew Bonventre <[email protected]> Reviewed-by: Brad Fitzpatrick <[email protected]>
1 parent ab54850 commit 44b7c21

File tree

2 files changed

+62
-0
lines changed

2 files changed

+62
-0
lines changed

http2/transport.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,6 +1703,14 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
17031703
return nil
17041704
}
17051705
if f.Length > 0 {
1706+
if cs.req.Method == "HEAD" && len(data) > 0 {
1707+
cc.logf("protocol error: received DATA on a HEAD request")
1708+
rl.endStreamError(cs, StreamError{
1709+
StreamID: f.StreamID,
1710+
Code: ErrCodeProtocol,
1711+
})
1712+
return nil
1713+
}
17061714
// Check connection-level flow control.
17071715
cc.mu.Lock()
17081716
if cs.inflow.available() >= int32(f.Length) {

http2/transport_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2026,6 +2026,60 @@ func TestTransportReadHeadResponse(t *testing.T) {
20262026
ct.run()
20272027
}
20282028

2029+
func TestTransportReadHeadResponseWithBody(t *testing.T) {
2030+
response := "redirecting to /elsewhere"
2031+
ct := newClientTester(t)
2032+
clientDone := make(chan struct{})
2033+
ct.client = func() error {
2034+
defer close(clientDone)
2035+
req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2036+
res, err := ct.tr.RoundTrip(req)
2037+
if err != nil {
2038+
return err
2039+
}
2040+
if res.ContentLength != int64(len(response)) {
2041+
return fmt.Errorf("Content-Length = %d; want %d", res.ContentLength, len(response))
2042+
}
2043+
slurp, err := ioutil.ReadAll(res.Body)
2044+
if err != nil {
2045+
return fmt.Errorf("ReadAll: %v", err)
2046+
}
2047+
if len(slurp) > 0 {
2048+
return fmt.Errorf("Unexpected non-empty ReadAll body: %q", slurp)
2049+
}
2050+
return nil
2051+
}
2052+
ct.server = func() error {
2053+
ct.greet()
2054+
for {
2055+
f, err := ct.fr.ReadFrame()
2056+
if err != nil {
2057+
t.Logf("ReadFrame: %v", err)
2058+
return nil
2059+
}
2060+
hf, ok := f.(*HeadersFrame)
2061+
if !ok {
2062+
continue
2063+
}
2064+
var buf bytes.Buffer
2065+
enc := hpack.NewEncoder(&buf)
2066+
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
2067+
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: strconv.Itoa(len(response))})
2068+
ct.fr.WriteHeaders(HeadersFrameParam{
2069+
StreamID: hf.StreamID,
2070+
EndHeaders: true,
2071+
EndStream: false,
2072+
BlockFragment: buf.Bytes(),
2073+
})
2074+
ct.fr.WriteData(hf.StreamID, true, []byte(response))
2075+
2076+
<-clientDone
2077+
return nil
2078+
}
2079+
}
2080+
ct.run()
2081+
}
2082+
20292083
type neverEnding byte
20302084

20312085
func (b neverEnding) Read(p []byte) (int, error) {

0 commit comments

Comments
 (0)