Skip to content

Commit 854a2f8

Browse files
fraenkeldmitshur
authored andcommitted
net/http: add connections back that haven't been canceled
Issue #41600 fixed the issue when a second request canceled a connection while the first request was still in roundTrip. This uncovered a second issue where a request was being canceled (in roundtrip) but the connection was put back into the idle pool for a subsequent request. The fix is the similar except its now in readLoop instead of roundTrip. A persistent connection is only added back if it successfully removed the cancel function; otherwise we know the roundTrip has started cancelRequest. Fixes #42942 Change-Id: Ia56add20880ccd0c1ab812d380d8628e45f6f44c Reviewed-on: https://go-review.googlesource.com/c/go/+/274973 Trust: Dmitri Shuralyov <[email protected]> Trust: Damien Neil <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent 6fa06d9 commit 854a2f8

File tree

1 file changed

+12
-10
lines changed

1 file changed

+12
-10
lines changed

src/net/http/transport.go

+12-10
Original file line numberDiff line numberDiff line change
@@ -784,14 +784,17 @@ func (t *Transport) CancelRequest(req *Request) {
784784
}
785785

786786
// Cancel an in-flight request, recording the error value.
787-
func (t *Transport) cancelRequest(key cancelKey, err error) {
787+
// Returns whether the request was canceled.
788+
func (t *Transport) cancelRequest(key cancelKey, err error) bool {
788789
t.reqMu.Lock()
789790
cancel := t.reqCanceler[key]
790791
delete(t.reqCanceler, key)
791792
t.reqMu.Unlock()
792793
if cancel != nil {
793794
cancel(err)
794795
}
796+
797+
return cancel != nil
795798
}
796799

797800
//
@@ -2127,18 +2130,17 @@ func (pc *persistConn) readLoop() {
21272130
}
21282131

21292132
if !hasBody || bodyWritable {
2130-
pc.t.setReqCanceler(rc.cancelKey, nil)
2133+
replaced := pc.t.replaceReqCanceler(rc.cancelKey, nil)
21312134

21322135
// Put the idle conn back into the pool before we send the response
21332136
// so if they process it quickly and make another request, they'll
21342137
// get this same conn. But we use the unbuffered channel 'rc'
21352138
// to guarantee that persistConn.roundTrip got out of its select
21362139
// potentially waiting for this persistConn to close.
2137-
// but after
21382140
alive = alive &&
21392141
!pc.sawEOF &&
21402142
pc.wroteRequest() &&
2141-
tryPutIdleConn(trace)
2143+
replaced && tryPutIdleConn(trace)
21422144

21432145
if bodyWritable {
21442146
closeErr = errCallerOwnsConn
@@ -2200,12 +2202,12 @@ func (pc *persistConn) readLoop() {
22002202
// reading the response body. (or for cancellation or death)
22012203
select {
22022204
case bodyEOF := <-waitForBodyRead:
2203-
pc.t.setReqCanceler(rc.cancelKey, nil) // before pc might return to idle pool
2205+
replaced := pc.t.replaceReqCanceler(rc.cancelKey, nil) // before pc might return to idle pool
22042206
alive = alive &&
22052207
bodyEOF &&
22062208
!pc.sawEOF &&
22072209
pc.wroteRequest() &&
2208-
tryPutIdleConn(trace)
2210+
replaced && tryPutIdleConn(trace)
22092211
if bodyEOF {
22102212
eofc <- struct{}{}
22112213
}
@@ -2600,6 +2602,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
26002602
cancelChan := req.Request.Cancel
26012603
ctxDoneChan := req.Context().Done()
26022604
pcClosed := pc.closech
2605+
canceled := false
26032606
for {
26042607
testHookWaitResLoop()
26052608
select {
@@ -2621,8 +2624,7 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
26212624
}
26222625
case <-pcClosed:
26232626
pcClosed = nil
2624-
// check if we are still using the connection
2625-
if pc.t.replaceReqCanceler(req.cancelKey, nil) {
2627+
if canceled || pc.t.replaceReqCanceler(req.cancelKey, nil) {
26262628
if debugRoundTrip {
26272629
req.logf("closech recv: %T %#v", pc.closed, pc.closed)
26282630
}
@@ -2646,10 +2648,10 @@ func (pc *persistConn) roundTrip(req *transportRequest) (resp *Response, err err
26462648
}
26472649
return re.res, nil
26482650
case <-cancelChan:
2649-
pc.t.cancelRequest(req.cancelKey, errRequestCanceled)
2651+
canceled = pc.t.cancelRequest(req.cancelKey, errRequestCanceled)
26502652
cancelChan = nil
26512653
case <-ctxDoneChan:
2652-
pc.t.cancelRequest(req.cancelKey, req.Context().Err())
2654+
canceled = pc.t.cancelRequest(req.cancelKey, req.Context().Err())
26532655
cancelChan = nil
26542656
ctxDoneChan = nil
26552657
}

0 commit comments

Comments
 (0)