Closed
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.