net/http: abort in-flight response writes when the connection dies

connReader.handleReadErrorLocked already cancels the connection's context
when a read from the client fails, but a handler blocked writing a
response is left to discover the dead connection on its own, via the
write failing. On Oracle Solaris that never happens: once a read has
consumed a socket's pending error, poll(2) reports the fd as POLLIN only,
never POLLOUT, POLLERR or POLLHUP, even though write(2) returns EPIPE
immediately. A handler blocked writing a large response therefore blocks
forever, and with it the connection and anything waiting on it, such as
httptest.Server.Close.

That is what wedges TestTransportGzip/h1 on the solaris-amd64 builders:
the client closes the connection mid-response, the server's background
read consumes the ECONNRESET and exits, and the handler's io.Copy of a
1 MiB body never returns.

Unblock the write instead of waiting for the poller. A read error other
than io.EOF or an expired read deadline means the connection is gone in
both directions, so a response write still in flight is pointless and is
now aborted with a write deadline. io.EOF is left alone, so a client that
half closes can still receive a response.

The Solaris behavior is an OS bug; illumos fixed the same defect in 2014
(illumos 4627, "POLLHUP not generated for disconnected sockets"). This
change also makes such a handler fail promptly rather than leak on any
platform where a peer dies without the write ever being reported ready.

Fixes #78438.

Change-Id: Ibe70d19889d6f7a6dc274b1ee6325d1703efd9eb
Reviewed-on: https://go-review.googlesource.com/c/go/+/822364
LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
Reviewed-by: David Chase <drchase@google.com>
2 files changed
tree: ad8911e23ebfe02a037f2f9d9b91dc84b74bed23
  1. .github/
  2. api/
  3. doc/
  4. lib/
  5. misc/
  6. src/
  7. test/
  8. .gitattributes
  9. .gitignore
  10. codereview.cfg
  11. CONTRIBUTING.md
  12. go.env
  13. LICENSE
  14. PATENTS
  15. README.md
  16. SECURITY.md
README.md

The Go Programming Language

Go is an open source programming language that makes it easy to build simple, reliable, and efficient software.

Gopher image Gopher image by Renee French, licensed under Creative Commons 4.0 Attribution license.

Our canonical Git repository is located at https://go.googlesource.com/go. There is a mirror of the repository at https://github.com/golang/go.

Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file.

Download and Install

Binary Distributions

Official binary distributions are available at https://go.dev/dl/.

After downloading a binary release, visit https://go.dev/doc/install for installation instructions.

Install From Source

If a binary distribution is not available for your combination of operating system and architecture, visit https://go.dev/doc/install/source for source installation instructions.

Contributing

Go is the work of thousands of contributors. We appreciate your help!

To contribute, please read the contribution guidelines at https://go.dev/doc/contribute.

Note that the Go project uses the issue tracker for bug reports and proposals only. See https://go.dev/wiki/Questions for a list of places to ask questions about the Go language.