idleLRU connLRU
reqMu sync.Mutex
- reqCanceler map[*Request]func()
+ reqCanceler map[*Request]func(error)
altMu sync.RWMutex
altProto map[string]RoundTripper // nil or map of URI scheme => RoundTripper
// cancelable context instead. CancelRequest cannot cancel HTTP/2
// requests.
func (t *Transport) CancelRequest(req *Request) {
+ t.cancelRequest(req, errRequestCanceled)
+}
+
+// Cancel an in-flight request, recording the error value.
+func (t *Transport) cancelRequest(req *Request, err error) {
t.reqMu.Lock()
cancel := t.reqCanceler[req]
delete(t.reqCanceler, req)
t.reqMu.Unlock()
if cancel != nil {
- cancel()
+ cancel(err)
}
}
}
}
-func (t *Transport) setReqCanceler(r *Request, fn func()) {
+func (t *Transport) setReqCanceler(r *Request, fn func(error)) {
t.reqMu.Lock()
defer t.reqMu.Unlock()
if t.reqCanceler == nil {
- t.reqCanceler = make(map[*Request]func())
+ t.reqCanceler = make(map[*Request]func(error))
}
if fn != nil {
t.reqCanceler[r] = fn
// for the request, we don't set the function and return false.
// Since CancelRequest will clear the canceler, we can use the return value to detect if
// the request was canceled since the last setReqCancel call.
-func (t *Transport) replaceReqCanceler(r *Request, fn func()) bool {
+func (t *Transport) replaceReqCanceler(r *Request, fn func(error)) bool {
t.reqMu.Lock()
defer t.reqMu.Unlock()
_, ok := t.reqCanceler[r]
// set request canceler to some non-nil function so we
// can detect whether it was cleared between now and when
// we enter roundTrip
- t.setReqCanceler(req, func() {})
+ t.setReqCanceler(req, func(error) {})
return pc, nil
}
}()
}
- cancelc := make(chan struct{})
- t.setReqCanceler(req, func() { close(cancelc) })
+ cancelc := make(chan error, 1)
+ t.setReqCanceler(req, func(err error) { cancelc <- err })
go func() {
pc, err := t.dialConn(ctx, cm)
select {
case <-req.Cancel:
case <-req.Context().Done():
- case <-cancelc:
+ return nil, req.Context().Err()
+ case err := <-cancelc:
+ if err == errRequestCanceled {
+ err = errRequestCanceledConn
+ }
+ return nil, err
default:
// It wasn't an error due to cancelation, so
// return the original error message:
return nil, errRequestCanceledConn
case <-req.Context().Done():
handlePendingDial()
- return nil, errRequestCanceledConn
- case <-cancelc:
+ return nil, req.Context().Err()
+ case err := <-cancelc:
handlePendingDial()
- return nil, errRequestCanceledConn
+ if err == errRequestCanceled {
+ err = errRequestCanceledConn
+ }
+ return nil, err
}
}
mu sync.Mutex // guards following fields
numExpectedResponses int
closed error // set non-nil when conn is closed, before closech is closed
+ canceledErr error // set non-nil if conn is canceled
broken bool // an error has happened on this connection; marked broken so it's not reused.
- canceled bool // whether this conn was broken due a CancelRequest
reused bool // whether conn has had successful request/response and is being reused.
// mutateHeaderFunc is an optional func to modify extra
// headers on each outbound request before it's written. (the
return b
}
-// isCanceled reports whether this connection was closed due to CancelRequest.
-func (pc *persistConn) isCanceled() bool {
+// canceled returns non-nil if the connection was closed due to
+// CancelRequest or due to context cancelation.
+func (pc *persistConn) canceled() error {
pc.mu.Lock()
defer pc.mu.Unlock()
- return pc.canceled
+ return pc.canceledErr
}
// isReused reports whether this connection is in a known broken state.
return
}
-func (pc *persistConn) cancelRequest() {
+func (pc *persistConn) cancelRequest(err error) {
pc.mu.Lock()
defer pc.mu.Unlock()
- pc.canceled = true
+ pc.canceledErr = err
pc.closeLocked(errRequestCanceled)
}
if err == nil {
return nil
}
- if pc.isCanceled() {
- return errRequestCanceled
+ if err := pc.canceled(); err != nil {
+ return err
}
if err == errServerClosedIdle {
return err
// its pc.closech channel close, indicating the persistConn is dead.
// (after closech is closed, pc.closed is valid).
func (pc *persistConn) mapRoundTripErrorAfterClosed(startBytesWritten int64) error {
- if pc.isCanceled() {
- return errRequestCanceled
+ if err := pc.canceled(); err != nil {
+ return err
}
err := pc.closed
if err == errServerClosedIdle {
waitForBodyRead <- isEOF
if isEOF {
<-eofc // see comment above eofc declaration
- } else if err != nil && pc.isCanceled() {
- return errRequestCanceled
+ } else if err != nil {
+ if cerr := pc.canceled(); cerr != nil {
+ return cerr
+ }
}
return err
},
pc.t.CancelRequest(rc.req)
case <-rc.req.Context().Done():
alive = false
- pc.t.CancelRequest(rc.req)
+ pc.t.cancelRequest(rc.req, rc.req.Context().Err())
case <-pc.closech:
alive = false
}
select {
case err := <-writeErrCh:
if err != nil {
- if pc.isCanceled() {
- err = errRequestCanceled
+ if cerr := pc.canceled(); cerr != nil {
+ err = cerr
}
re = responseAndError{err: err}
pc.close(fmt.Errorf("write error: %v", err))
case <-cancelChan:
pc.t.CancelRequest(req.Request)
cancelChan = nil
- ctxDoneChan = nil
case <-ctxDoneChan:
- pc.t.CancelRequest(req.Request)
+ pc.t.cancelRequest(req.Request, req.Context().Err())
cancelChan = nil
ctxDoneChan = nil
}