func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
var code func()
if err == nil {
err = io.EOF
code = cs.copyTrailers
}
cs.bufPipe.closeWithErrorAndCode(err, code)
// The above line closes resp.Body. At that point it should be safe for
// callers to reuse the req. However, req.Header is read below, which
// can race with reuses of req.
delete(rl.activeRes, cs.ID)
if isConnectionCloseRequest(cs.req) { // req.Header read here
rl.closeWhenIdle = true
}
select {
case cs.resc <- resAndError{err: err}:
default:
}
}
func isConnectionCloseRequest(req *http.Request) bool {
return req.Close || httplex.HeaderValuesContainsToken(req.Header["Connection"], "close")
}
The simple fix is to move cs.bufPipe.closeWithErrorAndCode after the isConnectionCloseRequest call, however, we should audit that cs.req is never used after cs.bufPipe is closed as there may be other instances of this race.
Repro here: https://go-review.googlesource.com/c/29243/10/http2/transport_test.go
The relevant code from transport.go:
The simple fix is to move
cs.bufPipe.closeWithErrorAndCode
after theisConnectionCloseRequest
call, however, we should audit thatcs.req
is never used aftercs.bufPipe
is closed as there may be other instances of this race.