Skip to content

Commit 2ac096c

Browse files
fraenkeldmitshur
authored andcommitted
[release-branch.go1.15-bundle] http2: wait until the request body has been written
When the clientConn is done with a request, if there is a request body, we must wait until the body is written otherwise there can be a race when attempting to rewind the body. Updates golang/go#42539 Change-Id: I77606cc19372eea8bbd8995102354f092b8042be Reviewed-on: https://go-review.googlesource.com/c/net/+/253259 Reviewed-by: Damien Neil <[email protected]> Trust: Emmanuel Odeke <[email protected]> Run-TryBot: Emmanuel Odeke <[email protected]> TryBot-Result: Go Bot <[email protected]> (cherry picked from commit ff519b6) Reviewed-on: https://go-review.googlesource.com/c/net/+/288012 Trust: Damien Neil <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 0704177 commit 2ac096c

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

http2/transport.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -1092,6 +1092,9 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
10921092
// we can keep it.
10931093
bodyWriter.cancel()
10941094
cs.abortRequestBodyWrite(errStopReqBodyWrite)
1095+
if hasBody && !bodyWritten {
1096+
<-bodyWriter.resc
1097+
}
10951098
}
10961099
if re.err != nil {
10971100
cc.forgetStreamID(cs.ID)
@@ -1112,6 +1115,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11121115
} else {
11131116
bodyWriter.cancel()
11141117
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1118+
<-bodyWriter.resc
11151119
}
11161120
cc.forgetStreamID(cs.ID)
11171121
return nil, cs.getStartedWrite(), errTimeout
@@ -1121,6 +1125,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11211125
} else {
11221126
bodyWriter.cancel()
11231127
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1128+
<-bodyWriter.resc
11241129
}
11251130
cc.forgetStreamID(cs.ID)
11261131
return nil, cs.getStartedWrite(), ctx.Err()
@@ -1130,6 +1135,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11301135
} else {
11311136
bodyWriter.cancel()
11321137
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1138+
<-bodyWriter.resc
11331139
}
11341140
cc.forgetStreamID(cs.ID)
11351141
return nil, cs.getStartedWrite(), errRequestCanceled
@@ -1139,6 +1145,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11391145
// forgetStreamID.
11401146
return nil, cs.getStartedWrite(), cs.resetErr
11411147
case err := <-bodyWriter.resc:
1148+
bodyWritten = true
11421149
// Prefer the read loop's response, if available. Issue 16102.
11431150
select {
11441151
case re := <-readLoopResCh:
@@ -1149,7 +1156,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11491156
cc.forgetStreamID(cs.ID)
11501157
return nil, cs.getStartedWrite(), err
11511158
}
1152-
bodyWritten = true
11531159
if d := cc.responseHeaderTimeout(); d != 0 {
11541160
timer := time.NewTimer(d)
11551161
defer timer.Stop()

http2/transport_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -4630,3 +4630,48 @@ func TestTransportRoundtripCloseOnWriteError(t *testing.T) {
46304630
t.Fatal("expected closed")
46314631
}
46324632
}
4633+
4634+
// Issue 31192: A failed request may be retried if the body has not been read
4635+
// already. If the request body has started to be sent, one must wait until it
4636+
// is completed.
4637+
func TestTransportBodyRewindRace(t *testing.T) {
4638+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
4639+
w.Header().Set("Connection", "close")
4640+
w.WriteHeader(http.StatusOK)
4641+
return
4642+
}, optOnlyServer)
4643+
defer st.Close()
4644+
4645+
tr := &http.Transport{
4646+
TLSClientConfig: tlsConfigInsecure,
4647+
MaxConnsPerHost: 1,
4648+
}
4649+
err := ConfigureTransport(tr)
4650+
if err != nil {
4651+
t.Fatal(err)
4652+
}
4653+
client := &http.Client{
4654+
Transport: tr,
4655+
}
4656+
4657+
const clients = 50
4658+
4659+
var wg sync.WaitGroup
4660+
wg.Add(clients)
4661+
for i := 0; i < clients; i++ {
4662+
req, err := http.NewRequest("POST", st.ts.URL, bytes.NewBufferString("abcdef"))
4663+
if err != nil {
4664+
t.Fatalf("unexpect new request error: %v", err)
4665+
}
4666+
4667+
go func() {
4668+
defer wg.Done()
4669+
res, err := client.Do(req)
4670+
if err == nil {
4671+
res.Body.Close()
4672+
}
4673+
}()
4674+
}
4675+
4676+
wg.Wait()
4677+
}

0 commit comments

Comments
 (0)