-
Notifications
You must be signed in to change notification settings - Fork 18.4k
Closed
Labels
FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.The path to resolution is known, but the work has not been done.
Milestone
Description
Let's start with the reproduction case:
func TestHandlerAbortRacesBodyRead(t *testing.T) {
setParallel(t)
defer afterTest(t)
ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, req *Request) {
go io.Copy(io.Discard, req.Body)
panic(ErrAbortHandler)
}))
defer ts.Close()
var wg sync.WaitGroup
for i := 0; i < 2; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
const reqLen = 6 * 1024 * 1024
req, _ := NewRequest("POST", ts.URL, &io.LimitedReader{R: neverEnding('x'), N: reqLen})
req.ContentLength = reqLen
resp, _ := ts.Client().Transport.RoundTrip(req)
if resp != nil {
resp.Body.Close()
}
}
}()
}
wg.Wait()
}
There might be something simpler, but this consistently panics for me on multiple systems. The above is a bit contrived, but the panic consistently occurs in some much less contrived scenarios with httputil.ReverseProxy
, which likes to panic(ErrAbortHandler)
when it encounters a problem copying a proxied response body.
The root cause is (I think) that we don't close the request body after a handler panic. We do close the *conn
, returning its bufio.Reader
and bufio.Writer
to their sync.Pool
s. But the request body has an indirect reference to the *conn
's reader, and while the handler has exited, some other goroutine may still be reading from it.
Metadata
Metadata
Assignees
Labels
FrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.The path to resolution is known, but the work has not been done.