Skip to content

Commit c9647c0

Browse files
fraenkelodeke-em
authored andcommitted
net/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. Fixes golang/go#31192 Fixes golang/go#41234 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]>
1 parent daf6c75 commit c9647c0

File tree

2 files changed

+52
-1
lines changed

2 files changed

+52
-1
lines changed

transport.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,9 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11481148
// we can keep it.
11491149
bodyWriter.cancel()
11501150
cs.abortRequestBodyWrite(errStopReqBodyWrite)
1151+
if hasBody && !bodyWritten {
1152+
<-bodyWriter.resc
1153+
}
11511154
}
11521155
if re.err != nil {
11531156
cc.forgetStreamID(cs.ID)
@@ -1168,6 +1171,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11681171
} else {
11691172
bodyWriter.cancel()
11701173
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1174+
<-bodyWriter.resc
11711175
}
11721176
cc.forgetStreamID(cs.ID)
11731177
return nil, cs.getStartedWrite(), errTimeout
@@ -1177,6 +1181,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11771181
} else {
11781182
bodyWriter.cancel()
11791183
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1184+
<-bodyWriter.resc
11801185
}
11811186
cc.forgetStreamID(cs.ID)
11821187
return nil, cs.getStartedWrite(), ctx.Err()
@@ -1186,6 +1191,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11861191
} else {
11871192
bodyWriter.cancel()
11881193
cs.abortRequestBodyWrite(errStopReqBodyWriteAndCancel)
1194+
<-bodyWriter.resc
11891195
}
11901196
cc.forgetStreamID(cs.ID)
11911197
return nil, cs.getStartedWrite(), errRequestCanceled
@@ -1195,6 +1201,7 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
11951201
// forgetStreamID.
11961202
return nil, cs.getStartedWrite(), cs.resetErr
11971203
case err := <-bodyWriter.resc:
1204+
bodyWritten = true
11981205
// Prefer the read loop's response, if available. Issue 16102.
11991206
select {
12001207
case re := <-readLoopResCh:
@@ -1205,7 +1212,6 @@ func (cc *ClientConn) roundTrip(req *http.Request) (res *http.Response, gotErrAf
12051212
cc.forgetStreamID(cs.ID)
12061213
return nil, cs.getStartedWrite(), err
12071214
}
1208-
bodyWritten = true
12091215
if d := cc.responseHeaderTimeout(); d != 0 {
12101216
timer := time.NewTimer(d)
12111217
defer timer.Stop()

transport_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -4861,3 +4861,48 @@ func TestTransportRoundtripCloseOnWriteError(t *testing.T) {
48614861
t.Fatal("expected closed")
48624862
}
48634863
}
4864+
4865+
// Issue 31192: A failed request may be retried if the body has not been read
4866+
// already. If the request body has started to be sent, one must wait until it
4867+
// is completed.
4868+
func TestTransportBodyRewindRace(t *testing.T) {
4869+
st := newServerTester(t, func(w http.ResponseWriter, r *http.Request) {
4870+
w.Header().Set("Connection", "close")
4871+
w.WriteHeader(http.StatusOK)
4872+
return
4873+
}, optOnlyServer)
4874+
defer st.Close()
4875+
4876+
tr := &http.Transport{
4877+
TLSClientConfig: tlsConfigInsecure,
4878+
MaxConnsPerHost: 1,
4879+
}
4880+
err := ConfigureTransport(tr)
4881+
if err != nil {
4882+
t.Fatal(err)
4883+
}
4884+
client := &http.Client{
4885+
Transport: tr,
4886+
}
4887+
4888+
const clients = 50
4889+
4890+
var wg sync.WaitGroup
4891+
wg.Add(clients)
4892+
for i := 0; i < clients; i++ {
4893+
req, err := http.NewRequest("POST", st.ts.URL, bytes.NewBufferString("abcdef"))
4894+
if err != nil {
4895+
t.Fatalf("unexpect new request error: %v", err)
4896+
}
4897+
4898+
go func() {
4899+
defer wg.Done()
4900+
res, err := client.Do(req)
4901+
if err == nil {
4902+
res.Body.Close()
4903+
}
4904+
}()
4905+
}
4906+
4907+
wg.Wait()
4908+
}

0 commit comments

Comments
 (0)