@@ -29,6 +29,7 @@ import (
29
29
"bufio"
30
30
"bytes"
31
31
"context"
32
+ "crypto/rand"
32
33
"crypto/tls"
33
34
"errors"
34
35
"fmt"
@@ -127,6 +128,16 @@ type Server struct {
127
128
// If zero or negative, there is no timeout.
128
129
IdleTimeout time.Duration
129
130
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
+
130
141
// WriteByteTimeout is the timeout after which a connection will be
131
142
// closed if no data can be written to it. The timeout begins when data is
132
143
// available to write, and is extended whenever any bytes are written.
@@ -644,9 +655,12 @@ type serverConn struct {
644
655
inGoAway bool // we've started to or sent GOAWAY
645
656
inFrameScheduleLoop bool // whether we're in the scheduleFrameWrite loop
646
657
needToSendGoAway bool // we need to schedule a GOAWAY frame write
658
+ pingSent bool
659
+ sentPingData [8 ]byte
647
660
goAwayCode ErrCode
648
661
shutdownTimer timer // nil until used
649
662
idleTimer timer // nil if unused
663
+ readIdleTimer timer // nil if unused
650
664
651
665
// Owned by the writeFrameAsync goroutine:
652
666
headerWriteBuf bytes.Buffer
@@ -974,11 +988,17 @@ func (sc *serverConn) serve() {
974
988
defer sc .idleTimer .Stop ()
975
989
}
976
990
991
+ if sc .srv .ReadIdleTimeout > 0 {
992
+ sc .readIdleTimer = sc .srv .afterFunc (sc .srv .ReadIdleTimeout , sc .onReadIdleTimer )
993
+ defer sc .readIdleTimer .Stop ()
994
+ }
995
+
977
996
go sc .readFrames () // closed by defer sc.conn.Close above
978
997
979
998
settingsTimer := sc .srv .afterFunc (firstSettingsTimeout , sc .onSettingsTimer )
980
999
defer settingsTimer .Stop ()
981
1000
1001
+ lastFrameTime := sc .srv .now ()
982
1002
loopNum := 0
983
1003
for {
984
1004
loopNum ++
@@ -992,6 +1012,7 @@ func (sc *serverConn) serve() {
992
1012
case res := <- sc .wroteFrameCh :
993
1013
sc .wroteFrame (res )
994
1014
case res := <- sc .readFrameCh :
1015
+ lastFrameTime = sc .srv .now ()
995
1016
// Process any written frames before reading new frames from the client since a
996
1017
// written frame could have triggered a new stream to be started.
997
1018
if sc .writingFrameAsync {
@@ -1023,6 +1044,8 @@ func (sc *serverConn) serve() {
1023
1044
case idleTimerMsg :
1024
1045
sc .vlogf ("connection is idle" )
1025
1046
sc .goAway (ErrCodeNo )
1047
+ case readIdleTimerMsg :
1048
+ sc .handlePingTimer (lastFrameTime )
1026
1049
case shutdownTimerMsg :
1027
1050
sc .vlogf ("GOAWAY close timer fired; closing conn from %v" , sc .conn .RemoteAddr ())
1028
1051
return
@@ -1061,19 +1084,51 @@ func (sc *serverConn) serve() {
1061
1084
}
1062
1085
}
1063
1086
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
+
1064
1117
type serverMessage int
1065
1118
1066
1119
// Message values sent to serveMsgCh.
1067
1120
var (
1068
1121
settingsTimerMsg = new (serverMessage )
1069
1122
idleTimerMsg = new (serverMessage )
1123
+ readIdleTimerMsg = new (serverMessage )
1070
1124
shutdownTimerMsg = new (serverMessage )
1071
1125
gracefulShutdownMsg = new (serverMessage )
1072
1126
handlerDoneMsg = new (serverMessage )
1073
1127
)
1074
1128
1075
1129
func (sc * serverConn ) onSettingsTimer () { sc .sendServeMsg (settingsTimerMsg ) }
1076
1130
func (sc * serverConn ) onIdleTimer () { sc .sendServeMsg (idleTimerMsg ) }
1131
+ func (sc * serverConn ) onReadIdleTimer () { sc .sendServeMsg (readIdleTimerMsg ) }
1077
1132
func (sc * serverConn ) onShutdownTimer () { sc .sendServeMsg (shutdownTimerMsg ) }
1078
1133
1079
1134
func (sc * serverConn ) sendServeMsg (msg interface {}) {
@@ -1604,6 +1659,11 @@ func (sc *serverConn) processFrame(f Frame) error {
1604
1659
func (sc * serverConn ) processPing (f * PingFrame ) error {
1605
1660
sc .serveG .check ()
1606
1661
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
+ }
1607
1667
// 6.7 PING: " An endpoint MUST NOT respond to PING frames
1608
1668
// containing this flag."
1609
1669
return nil
0 commit comments