@@ -29,6 +29,7 @@ import (
2929 "bufio"
3030 "bytes"
3131 "context"
32+ "crypto/rand"
3233 "crypto/tls"
3334 "errors"
3435 "fmt"
@@ -127,6 +128,16 @@ type Server struct {
127128 // If zero or negative, there is no timeout.
128129 IdleTimeout time.Duration
129130
131+ // ReadIdleTimeout is the timeout after which a health check using a ping
132+ // frame will be carried out if no frame is received on the connection.
133+ // If zero, no health check is performed.
134+ ReadIdleTimeout time.Duration
135+
136+ // PingTimeout is the timeout after which the connection will be closed
137+ // if a response to a ping is not received.
138+ // If zero, a default of 15 seconds is used.
139+ PingTimeout time.Duration
140+
130141 // WriteByteTimeout is the timeout after which a connection will be
131142 // closed if no data can be written to it. The timeout begins when data is
132143 // available to write, and is extended whenever any bytes are written.
@@ -644,9 +655,12 @@ type serverConn struct {
644655 inGoAway bool // we've started to or sent GOAWAY
645656 inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
646657 needToSendGoAway bool // we need to schedule a GOAWAY frame write
658+ pingSent bool
659+ sentPingData [8 ]byte
647660 goAwayCode ErrCode
648661 shutdownTimer timer // nil until used
649662 idleTimer timer // nil if unused
663+ readIdleTimer timer // nil if unused
650664
651665 // Owned by the writeFrameAsync goroutine:
652666 headerWriteBuf bytes.Buffer
@@ -974,11 +988,17 @@ func (sc *serverConn) serve() {
974988 defer sc .idleTimer .Stop ()
975989 }
976990
991+ if sc .srv .ReadIdleTimeout > 0 {
992+ sc .readIdleTimer = sc .srv .afterFunc (sc .srv .ReadIdleTimeout , sc .onReadIdleTimer )
993+ defer sc .readIdleTimer .Stop ()
994+ }
995+
977996 go sc .readFrames () // closed by defer sc.conn.Close above
978997
979998 settingsTimer := sc .srv .afterFunc (firstSettingsTimeout , sc .onSettingsTimer )
980999 defer settingsTimer .Stop ()
9811000
1001+ lastFrameTime := sc .srv .now ()
9821002 loopNum := 0
9831003 for {
9841004 loopNum ++
@@ -992,6 +1012,7 @@ func (sc *serverConn) serve() {
9921012 case res := <- sc .wroteFrameCh :
9931013 sc .wroteFrame (res )
9941014 case res := <- sc .readFrameCh :
1015+ lastFrameTime = sc .srv .now ()
9951016 // Process any written frames before reading new frames from the client since a
9961017 // written frame could have triggered a new stream to be started.
9971018 if sc .writingFrameAsync {
@@ -1023,6 +1044,8 @@ func (sc *serverConn) serve() {
10231044 case idleTimerMsg :
10241045 sc .vlogf ("connection is idle" )
10251046 sc .goAway (ErrCodeNo )
1047+ case readIdleTimerMsg :
1048+ sc .handlePingTimer (lastFrameTime )
10261049 case shutdownTimerMsg :
10271050 sc .vlogf ("GOAWAY close timer fired; closing conn from %v" , sc .conn .RemoteAddr ())
10281051 return
@@ -1061,19 +1084,51 @@ func (sc *serverConn) serve() {
10611084 }
10621085}
10631086
1087+ func (sc * serverConn ) handlePingTimer (lastFrameReadTime time.Time ) {
1088+ if sc .pingSent {
1089+ sc .vlogf ("timeout waiting for PING response" )
1090+ sc .conn .Close ()
1091+ return
1092+ }
1093+
1094+ pingAt := lastFrameReadTime .Add (sc .srv .ReadIdleTimeout )
1095+ now := sc .srv .now ()
1096+ if pingAt .After (now ) {
1097+ // We received frames since arming the ping timer.
1098+ // Reset it for the next possible timeout.
1099+ sc .readIdleTimer .Reset (pingAt .Sub (now ))
1100+ return
1101+ }
1102+
1103+ sc .pingSent = true
1104+ // Ignore crypto/rand.Read errors: It generally can't fail, and worse case if it does
1105+ // is we send a PING frame containing 0s.
1106+ _ , _ = rand .Read (sc .sentPingData [:])
1107+ sc .writeFrame (FrameWriteRequest {
1108+ write : & writePing {data : sc .sentPingData },
1109+ })
1110+ pingTimeout := sc .srv .PingTimeout
1111+ if pingTimeout <= 0 {
1112+ pingTimeout = 15 * time .Second
1113+ }
1114+ sc .readIdleTimer .Reset (pingTimeout )
1115+ }
1116+
10641117type serverMessage int
10651118
10661119// Message values sent to serveMsgCh.
10671120var (
10681121 settingsTimerMsg = new (serverMessage )
10691122 idleTimerMsg = new (serverMessage )
1123+ readIdleTimerMsg = new (serverMessage )
10701124 shutdownTimerMsg = new (serverMessage )
10711125 gracefulShutdownMsg = new (serverMessage )
10721126 handlerDoneMsg = new (serverMessage )
10731127)
10741128
10751129func (sc * serverConn ) onSettingsTimer () { sc .sendServeMsg (settingsTimerMsg ) }
10761130func (sc * serverConn ) onIdleTimer () { sc .sendServeMsg (idleTimerMsg ) }
1131+ func (sc * serverConn ) onReadIdleTimer () { sc .sendServeMsg (readIdleTimerMsg ) }
10771132func (sc * serverConn ) onShutdownTimer () { sc .sendServeMsg (shutdownTimerMsg ) }
10781133
10791134func (sc * serverConn ) sendServeMsg (msg interface {}) {
@@ -1604,6 +1659,11 @@ func (sc *serverConn) processFrame(f Frame) error {
16041659func (sc * serverConn ) processPing (f * PingFrame ) error {
16051660 sc .serveG .check ()
16061661 if f .IsAck () {
1662+ if sc .pingSent && sc .sentPingData == f .Data {
1663+ // This is a response to a PING we sent.
1664+ sc .pingSent = false
1665+ sc .readIdleTimer .Reset (sc .srv .ReadIdleTimeout )
1666+ }
16071667 // 6.7 PING: " An endpoint MUST NOT respond to PING frames
16081668 // containing this flag."
16091669 return nil
0 commit comments