@@ -108,6 +108,23 @@ type Transport struct {
108
108
// waiting for their turn.
109
109
StrictMaxConcurrentStreams bool
110
110
111
+ // PingPeriod controls how often pings are sent on idle connections to
112
+ // check the liveness of the connection. The connection will be closed
113
+ // if response is not received within PingTimeout.
114
+ // 0 means no periodic pings. Defaults to 0.
115
+ PingPeriod time.Duration
116
+ // PingTimeout is the timeout after which the connection will be closed
117
+ // if a response to Ping is not received.
118
+ // 0 means no periodic pings. Defaults to 0.
119
+ PingTimeout time.Duration
120
+ // ReadIdleTimeout is the timeout after which the periodic ping for
121
+ // connection health check will begin if no frame is received on the
122
+ // connection.
123
+ // The health check will stop once there is frame received on the
124
+ // connection.
125
+ // Defaults to 60s.
126
+ ReadIdleTimeout time.Duration
127
+
111
128
// t1, if non-nil, is the standard library Transport using
112
129
// this transport. Its settings are used (but not its
113
130
// RoundTrip method, etc).
@@ -140,10 +157,6 @@ func ConfigureTransport(t1 *http.Transport) error {
140
157
141
158
func configureTransport (t1 * http.Transport ) (* Transport , error ) {
142
159
connPool := new (clientConnPool )
143
- // TODO: figure out a way to allow user to configure pingPeriod and
144
- // pingTimeout.
145
- connPool .pingPeriod = 5 * time .Second
146
- connPool .pingTimeout = 1 * time .Second
147
160
t2 := & Transport {
148
161
ConnPool : noDialClientConnPool {connPool },
149
162
t1 : t1 ,
@@ -244,7 +257,7 @@ type ClientConn struct {
244
257
wmu sync.Mutex // held while writing; acquire AFTER mu if holding both
245
258
werr error // first write error that has occurred
246
259
247
- healthCheckCancel chan struct {}
260
+ healthCheckStopCh chan struct {}
248
261
}
249
262
250
263
// clientStream is the state for a single HTTP/2 stream. One of these
@@ -680,42 +693,47 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, erro
680
693
return cc , nil
681
694
}
682
695
683
- func (cc * ClientConn ) healthCheck (cancel chan struct {}) {
684
- // TODO: CHAO: configurable
685
- pingPeriod := 15 * time .Second
696
+ func (cc * ClientConn ) healthCheck (stop chan struct {}) {
697
+ pingPeriod := cc .t .PingPeriod
698
+ pingTimeout := cc .t .PingTimeout
699
+ if pingPeriod == 0 || pingTimeout == 0 {
700
+ return
701
+ }
686
702
ticker := time .NewTicker (pingPeriod )
687
703
defer ticker .Stop ()
688
704
for {
689
705
select {
690
- case <- cancel :
706
+ case <- stop :
691
707
return
692
708
case <- ticker .C :
693
- ctx , _ := context .WithTimeout (context .Background (), p . pingTimeout )
709
+ ctx , cancel := context .WithTimeout (context .Background (), pingTimeout )
694
710
err := cc .Ping (ctx )
711
+ cancel ()
695
712
if err != nil {
696
713
cc .closeForLostPing ()
697
714
cc .t .connPool ().MarkDead (cc )
715
+ return
698
716
}
699
717
}
700
718
}
701
719
}
702
720
703
721
func (cc * ClientConn ) startHealthCheck () {
704
- if cc .healthCheckCancel != nil {
722
+ if cc .healthCheckStopCh != nil {
705
723
// a health check is already running
706
724
return
707
725
}
708
- cc .healthCheckCancel = make (chan struct {})
709
- go cc .healthCheck (cc .healthCheckCancel )
726
+ cc .healthCheckStopCh = make (chan struct {})
727
+ go cc .healthCheck (cc .healthCheckStopCh )
710
728
}
711
729
712
730
func (cc * ClientConn ) stopHealthCheck () {
713
- if cc .healthCheckCancel == nil {
731
+ if cc .healthCheckStopCh == nil {
714
732
// no health check running
715
733
return
716
734
}
717
- close (cc .healthCheckCancel )
718
- cc .healthCheckCancel = nil
735
+ close (cc .healthCheckStopCh )
736
+ cc .healthCheckStopCh = nil
719
737
}
720
738
721
739
func (cc * ClientConn ) setGoAway (f * GoAwayFrame ) {
@@ -1757,25 +1775,16 @@ func (rl *clientConnReadLoop) cleanup() {
1757
1775
cc .mu .Unlock ()
1758
1776
}
1759
1777
1760
- func ReadFrameAndProbablyStartOrStopPingLoop () {
1761
- select {
1762
- case <- timer :
1763
- // start ping loop
1764
- case <- read :
1765
- // stop ping loop
1766
- }
1767
- }
1768
-
1769
1778
type frameAndError struct {
1770
1779
f Frame
1771
1780
err error
1772
1781
}
1773
1782
1774
- func nonBlockingReadFrame (f * Framer ) chan frameAndError {
1775
- feCh := make (chan FrameAndError )
1783
+ func nonBlockingReadFrame (fr * Framer ) chan frameAndError {
1784
+ feCh := make (chan frameAndError )
1776
1785
go func () {
1777
1786
f , err := fr .ReadFrame ()
1778
- feCh <- frameAndError {frame : f , err : err }
1787
+ feCh <- frameAndError {f : f , err : err }
1779
1788
}()
1780
1789
return feCh
1781
1790
}
@@ -1788,15 +1797,18 @@ func (rl *clientConnReadLoop) run() error {
1788
1797
for {
1789
1798
var fe frameAndError
1790
1799
feCh := nonBlockingReadFrame (cc .fr )
1791
- // TODO: CHAO: make it configurable
1792
- readIdleTimer := time .NewTimer (15 * time .Second )
1800
+ to := cc .t .ReadIdleTimeout
1801
+ if to == 0 {
1802
+ to = 60 * time .Second
1803
+ }
1804
+ readIdleTimer := time .NewTimer (to )
1793
1805
select {
1794
- case fe <- feCh :
1806
+ case fe = <- feCh :
1795
1807
cc .stopHealthCheck ()
1796
1808
readIdleTimer .Stop ()
1797
1809
case <- readIdleTimer .C :
1798
1810
cc .startHealthCheck ()
1799
- fe <- feCh
1811
+ fe = <- feCh
1800
1812
}
1801
1813
f , err := fe .f , fe .err
1802
1814
if err != nil {
0 commit comments