Skip to content

Commit 1ef6b28

Browse files
neildgopherbot
authored andcommitted
net/http: don't write HEAD response body in ResponseWriter.ReadFrom
Responses to HEAD requests don't have a body. The ResponseWriter automatically discards writes to the response body when responding to a HEAD request. ResponseWriter.ReadFrom was failing to discard writes under some circumstances; fix it to do so. Fixes #68609 Change-Id: I912f6b2b2a535df28ae37b875fcf15b10da1af2b Reviewed-on: https://go-review.googlesource.com/c/go/+/601475 Reviewed-by: Brad Fitzpatrick <[email protected]> Auto-Submit: Damien Neil <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Knyszek <[email protected]>
1 parent ac51262 commit 1ef6b28

File tree

2 files changed

+30
-2
lines changed

2 files changed

+30
-2
lines changed

src/net/http/serve_test.go

+29-1
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ func testHeadResponses(t *testing.T, mode testMode) {
15101510
}
15111511

15121512
// Also exercise the ReaderFrom path
1513-
_, err = io.Copy(w, strings.NewReader("789a"))
1513+
_, err = io.Copy(w, struct{ io.Reader }{strings.NewReader("789a")})
15141514
if err != nil {
15151515
t.Errorf("Copy(ResponseWriter, ...): %v", err)
15161516
}
@@ -1537,6 +1537,34 @@ func testHeadResponses(t *testing.T, mode testMode) {
15371537
}
15381538
}
15391539

1540+
// Ensure ResponseWriter.ReadFrom doesn't write a body in response to a HEAD request.
1541+
// https://go.dev/issue/68609
1542+
func TestHeadReaderFrom(t *testing.T) { run(t, testHeadReaderFrom, []testMode{http1Mode}) }
1543+
func testHeadReaderFrom(t *testing.T, mode testMode) {
1544+
// Body is large enough to exceed the content-sniffing length.
1545+
wantBody := strings.Repeat("a", 4096)
1546+
cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
1547+
w.(io.ReaderFrom).ReadFrom(strings.NewReader(wantBody))
1548+
}))
1549+
res, err := cst.c.Head(cst.ts.URL)
1550+
if err != nil {
1551+
t.Fatal(err)
1552+
}
1553+
res.Body.Close()
1554+
res, err = cst.c.Get(cst.ts.URL)
1555+
if err != nil {
1556+
t.Fatal(err)
1557+
}
1558+
gotBody, err := io.ReadAll(res.Body)
1559+
res.Body.Close()
1560+
if err != nil {
1561+
t.Fatal(err)
1562+
}
1563+
if string(gotBody) != wantBody {
1564+
t.Errorf("got unexpected body len=%v, want %v", len(gotBody), len(wantBody))
1565+
}
1566+
}
1567+
15401568
func TestTLSHandshakeTimeout(t *testing.T) {
15411569
run(t, testTLSHandshakeTimeout, []testMode{https1Mode, http2Mode})
15421570
}

src/net/http/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -611,7 +611,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
611611
w.cw.flush() // make sure Header is written; flush data to rwc
612612

613613
// Now that cw has been flushed, its chunking field is guaranteed initialized.
614-
if !w.cw.chunking && w.bodyAllowed() {
614+
if !w.cw.chunking && w.bodyAllowed() && w.req.Method != "HEAD" {
615615
n0, err := rf.ReadFrom(src)
616616
n += n0
617617
w.written += n0

0 commit comments

Comments
 (0)