Skip to content

Commit 92447d5

Browse files
fraenkeltombergan
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]>
1 parent 5561cd9 commit 92447d5

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
@@ -1851,6 +1851,14 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
18511851
return nil
18521852
}
18531853
if f.Length > 0 {
1854+
if cs.req.Method == "HEAD" && len(data) > 0 {
1855+
cc.logf("protocol error: received DATA on a HEAD request")
1856+
rl.endStreamError(cs, StreamError{
1857+
StreamID: f.StreamID,
1858+
Code: ErrCodeProtocol,
1859+
})
1860+
return nil
1861+
}
18541862
// Check connection-level flow control.
18551863
cc.mu.Lock()
18561864
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
@@ -2290,6 +2290,60 @@ func TestTransportReadHeadResponse(t *testing.T) {
22902290
ct.run()
22912291
}
22922292

2293+
func TestTransportReadHeadResponseWithBody(t *testing.T) {
2294+
response := "redirecting to /elsewhere"
2295+
ct := newClientTester(t)
2296+
clientDone := make(chan struct{})
2297+
ct.client = func() error {
2298+
defer close(clientDone)
2299+
req, _ := http.NewRequest("HEAD", "https://dummy.tld/", nil)
2300+
res, err := ct.tr.RoundTrip(req)
2301+
if err != nil {
2302+
return err
2303+
}
2304+
if res.ContentLength != int64(len(response)) {
2305+
return fmt.Errorf("Content-Length = %d; want %d", res.ContentLength, len(response))
2306+
}
2307+
slurp, err := ioutil.ReadAll(res.Body)
2308+
if err != nil {
2309+
return fmt.Errorf("ReadAll: %v", err)
2310+
}
2311+
if len(slurp) > 0 {
2312+
return fmt.Errorf("Unexpected non-empty ReadAll body: %q", slurp)
2313+
}
2314+
return nil
2315+
}
2316+
ct.server = func() error {
2317+
ct.greet()
2318+
for {
2319+
f, err := ct.fr.ReadFrame()
2320+
if err != nil {
2321+
t.Logf("ReadFrame: %v", err)
2322+
return nil
2323+
}
2324+
hf, ok := f.(*HeadersFrame)
2325+
if !ok {
2326+
continue
2327+
}
2328+
var buf bytes.Buffer
2329+
enc := hpack.NewEncoder(&buf)
2330+
enc.WriteField(hpack.HeaderField{Name: ":status", Value: "200"})
2331+
enc.WriteField(hpack.HeaderField{Name: "content-length", Value: strconv.Itoa(len(response))})
2332+
ct.fr.WriteHeaders(HeadersFrameParam{
2333+
StreamID: hf.StreamID,
2334+
EndHeaders: true,
2335+
EndStream: false,
2336+
BlockFragment: buf.Bytes(),
2337+
})
2338+
ct.fr.WriteData(hf.StreamID, true, []byte(response))
2339+
2340+
<-clientDone
2341+
return nil
2342+
}
2343+
}
2344+
ct.run()
2345+
}
2346+
22932347
type neverEnding byte
22942348

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

0 commit comments

Comments
 (0)