Open
Description
If the client loses its connection to the server, and multiple requests are sent. There would be only one dial start connecting to the server. If the dial timeout due to network issue, only the first request who creates the dial would be finished, and the other requests would continue trying to connect to the server even if their context has been canceled due to timeout.
For getClientConn multi request would creat one call to dial server.
func (p *clientConnPool) getClientConn(req *http.Request, addr string, dialOnMiss bool) (*ClientConn, error) {
//...
for {
//....
// if there is no active connection
traceGetConn(req, addr)
// for a specific addr, only one call would be created.
call := p.getStartDialLocked(req.Context(), addr)
p.mu.Unlock()
<-call.done
// If should retry dial for the request, which do not respect to request's context.
if shouldRetryDial(call, req) {
continue
}
cc, err := call.res, call.err
if err != nil {
return nil, err
}
if cc.ReserveNewRequest() {
return cc, nil
}
}
}
For shouldRetryDial function, even if the request's context has been canceled, the result can be true.
func shouldRetryDial(call *dialCall, req *http.Request) bool {
if call.err == nil {
// No error, no need to retry
return false
}
if call.ctx == req.Context() {
// If the call has the same context as the request, the dial
// should not be retried, since any cancellation will have come
// from this request.
return false
}
if !errors.Is(call.err, context.Canceled) && !errors.Is(call.err, context.DeadlineExceeded) {
// If the call error is not because of a context cancellation or a deadline expiry,
// the dial should not be retried.
return false
}
// Only retry if the error is a context cancellation error or deadline expiry
// and the context associated with the call was canceled or expired.
return call.ctx.Err() != nil
}