Skip to content

Commit

Permalink
http2: fix transport data race on reused *http.Request objects
Browse files Browse the repository at this point in the history
Based on golang/go#19653, it should be possible to reuse an http.Request
object after the outstanding request has completed. This CL fixes a race
in the http/2 library that occurs when a caller tries to reuse an
http.Request just after the request completed.

The new test failed with -race before this CL and passes after this CL.
Verified with -count 10000.

Updates golang/go#21316

Change-Id: I014cf9cefd0dd21f6f41763ba554d23ddc7fca40
Reviewed-on: https://go-review.googlesource.com/75530
Reviewed-by: Brad Fitzpatrick <[email protected]>
Run-TryBot: Brad Fitzpatrick <[email protected]>
TryBot-Result: Gobot Gobot <[email protected]>
  • Loading branch information
tombergan committed Nov 2, 2017
1 parent 7c2699e commit 5c5449c
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 2 deletions.
4 changes: 2 additions & 2 deletions transport.go
Original file line number Diff line number Diff line change
Expand Up @@ -1911,11 +1911,11 @@ func (rl *clientConnReadLoop) endStreamError(cs *clientStream, err error) {
err = io.EOF
code = cs.copyTrailers
}
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)
if isConnectionCloseRequest(cs.req) {
rl.closeWhenIdle = true
}
cs.bufPipe.closeWithErrorAndCode(err, code)
delete(rl.activeRes, cs.ID)

select {
case cs.resc <- resAndError{err: err}:
Expand Down
28 changes: 28 additions & 0 deletions transport_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3097,6 +3097,34 @@ func TestTransportCancelDataResponseRace(t *testing.T) {
}
}

// Issue 21316: It should be safe to reuse an http.Request after the
// request has completed.
func TestTransportNoRaceOnRequestObjectAfterRequestComplete(t *testing.T) {
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
io.WriteString(w, "body")
}, optOnlyServer)
defer st.Close()

tr := &Transport{TLSClientConfig: tlsConfigInsecure}
defer tr.CloseIdleConnections()

req, _ := http.NewRequest("GET", st.ts.URL, nil)
resp, err := tr.RoundTrip(req)
if err != nil {
t.Fatal(err)
}
if _, err = io.Copy(ioutil.Discard, resp.Body); err != nil {
t.Fatalf("error reading response body: %v", err)
}
if err := resp.Body.Close(); err != nil {
t.Fatalf("error closing response body: %v", err)
}

// This access of req.Header should not race with code in the transport.
req.Header = http.Header{}
}

func TestTransportRetryAfterGOAWAY(t *testing.T) {
var dialer struct {
sync.Mutex
Expand Down

0 comments on commit 5c5449c

Please sign in to comment.