Skip to content

Commit

Permalink
net/http: don't write HEAD response body in ResponseWriter.ReadFrom
Browse files Browse the repository at this point in the history
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 <bradfitz@golang.org>
Auto-Submit: Damien Neil <dneil@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Knyszek <mknyszek@google.com>
  • Loading branch information
neild authored and pull[bot] committed Sep 26, 2024
1 parent 6083fe4 commit 466a463
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 2 deletions.
30 changes: 29 additions & 1 deletion src/net/http/serve_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1510,7 +1510,7 @@ func testHeadResponses(t *testing.T, mode testMode) {
}

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

// Ensure ResponseWriter.ReadFrom doesn't write a body in response to a HEAD request.
// https://go.dev/issue/68609
func TestHeadReaderFrom(t *testing.T) { run(t, testHeadReaderFrom, []testMode{http1Mode}) }
func testHeadReaderFrom(t *testing.T, mode testMode) {
// Body is large enough to exceed the content-sniffing length.
wantBody := strings.Repeat("a", 4096)
cst := newClientServerTest(t, mode, HandlerFunc(func(w ResponseWriter, r *Request) {
w.(io.ReaderFrom).ReadFrom(strings.NewReader(wantBody))
}))
res, err := cst.c.Head(cst.ts.URL)
if err != nil {
t.Fatal(err)
}
res.Body.Close()
res, err = cst.c.Get(cst.ts.URL)
if err != nil {
t.Fatal(err)
}
gotBody, err := io.ReadAll(res.Body)
res.Body.Close()
if err != nil {
t.Fatal(err)
}
if string(gotBody) != wantBody {
t.Errorf("got unexpected body len=%v, want %v", len(gotBody), len(wantBody))
}
}

func TestTLSHandshakeTimeout(t *testing.T) {
run(t, testTLSHandshakeTimeout, []testMode{https1Mode, http2Mode})
}
Expand Down
2 changes: 1 addition & 1 deletion src/net/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ func (w *response) ReadFrom(src io.Reader) (n int64, err error) {
w.cw.flush() // make sure Header is written; flush data to rwc

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

0 comments on commit 466a463

Please sign in to comment.