diff --git a/http2/transport.go b/http2/transport.go index 0402766b21..589a18fe82 100644 --- a/http2/transport.go +++ b/http2/transport.go @@ -116,13 +116,13 @@ type Transport struct { // PingTimeout is the timeout after which the connection will be closed // if a response to Ping is not received. - // 0 means no periodic pings. Defaults to 0. + // Defaults to PingPeriod/2 PingTimeout time.Duration // ReadIdleTimeout is the timeout after which the periodic ping for // connection health check will begin if no frame is received on the // connection. - // The health check will stop once there is frame received on the + // The health check will stop once there is a frame received on the // connection. // Defaults to 60s. ReadIdleTimeout time.Duration @@ -158,6 +158,14 @@ func (t *Transport) readIdleTimeout() time.Duration { return to } +func (t *Transport) pingTimeout() time.Duration { + if t.PingTimeout == 0 { + return t.PingPeriod / 2 + } + return t.PingTimeout + +} + // ConfigureTransport configures a net/http HTTP/1 Transport to use HTTP/2. // It returns an error if t1 has already been HTTP/2-enabled. func ConfigureTransport(t1 *http.Transport) error { @@ -267,7 +275,8 @@ type ClientConn struct { wmu sync.Mutex // held while writing; acquire AFTER mu if holding both werr error // first write error that has occurred - healthCheckStopCh chan struct{} + hmu sync.Mutex // guard the healthCheckStopCh + healthCheckStopCh chan struct{} // A close-only channel to stop the health check. } // clientStream is the state for a single HTTP/2 stream. One of these @@ -705,7 +714,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro func (cc *ClientConn) healthCheck(stop chan struct{}) { pingPeriod := cc.t.PingPeriod - pingTimeout := cc.t.PingTimeout + pingTimeout := cc.t.pingTimeout() if pingPeriod == 0 || pingTimeout == 0 { return } @@ -729,6 +738,8 @@ func (cc *ClientConn) healthCheck(stop chan struct{}) { } func (cc *ClientConn) startHealthCheck() { + cc.hmu.Lock() + defer cc.hmu.Unlock() if cc.healthCheckStopCh != nil { // a health check is already running return @@ -738,6 +749,8 @@ func (cc *ClientConn) startHealthCheck() { } func (cc *ClientConn) stopHealthCheck() { + cc.hmu.Lock() + defer cc.hmu.Unlock() if cc.healthCheckStopCh == nil { // no health check running return @@ -934,7 +947,7 @@ func (cc *ClientConn) Close() error { // closes the client connection immediately. In-flight requests are interrupted. func (cc *ClientConn) closeForLostPing() error { - err := errors.New("http2: client connection force closed because ping frame is not responded") + err := errors.New("http2: client connection force closed because ping frame was not answered") return cc.closeForError(err) }