2
2
// Use of this source code is governed by a BSD-style
3
3
// license that can be found in the LICENSE file.
4
4
5
- // TODO: replace all <-sc.doneServing with reads from the stream's cw
6
- // instead, and make sure that on close we close all open
7
- // streams. then remove doneServing?
8
-
9
- // TODO: re-audit GOAWAY support. Consider each incoming frame type and
10
- // whether it should be ignored during graceful shutdown.
11
-
12
- // TODO: disconnect idle clients. GFE seems to do 4 minutes. make
13
- // configurable? or maximum number of idle clients and remove the
14
- // oldest?
15
-
16
5
// TODO: turn off the serve goroutine when idle, so
17
6
// an idle conn only has the readFrames goroutine active. (which could
18
7
// also be optimized probably to pin less memory in crypto/tls). This
@@ -114,6 +103,11 @@ type Server struct {
114
103
// PermitProhibitedCipherSuites, if true, permits the use of
115
104
// cipher suites prohibited by the HTTP/2 spec.
116
105
PermitProhibitedCipherSuites bool
106
+
107
+ // IdleTimeout specifies how long until idle clients should be
108
+ // closed with a GOAWAY frame. PING frames are not considered
109
+ // activity for the purposes of IdleTimeout.
110
+ IdleTimeout time.Duration
117
111
}
118
112
119
113
func (s * Server ) maxReadFrameSize () uint32 {
@@ -390,6 +384,8 @@ type serverConn struct {
390
384
goAwayCode ErrCode
391
385
shutdownTimerCh <- chan time.Time // nil until used
392
386
shutdownTimer * time.Timer // nil until used
387
+ idleTimer * time.Timer // nil if unused
388
+ idleTimerCh <- chan time.Time // nil if unused
393
389
394
390
// Owned by the writeFrameAsync goroutine:
395
391
headerWriteBuf bytes.Buffer
@@ -681,6 +677,12 @@ func (sc *serverConn) serve() {
681
677
sc .setConnState (http .StateActive )
682
678
sc .setConnState (http .StateIdle )
683
679
680
+ if sc .srv .IdleTimeout != 0 {
681
+ sc .idleTimer = time .NewTimer (sc .srv .IdleTimeout )
682
+ defer sc .idleTimer .Stop ()
683
+ sc .idleTimerCh = sc .idleTimer .C
684
+ }
685
+
684
686
go sc .readFrames () // closed by defer sc.conn.Close above
685
687
686
688
settingsTimer := time .NewTimer (firstSettingsTimeout )
@@ -709,6 +711,9 @@ func (sc *serverConn) serve() {
709
711
case <- sc .shutdownTimerCh :
710
712
sc .vlogf ("GOAWAY close timer fired; closing conn from %v" , sc .conn .RemoteAddr ())
711
713
return
714
+ case <- sc .idleTimerCh :
715
+ sc .vlogf ("connection is idle" )
716
+ sc .goAway (ErrCodeNo )
712
717
case fn := <- sc .testHookCh :
713
718
fn (loopNum )
714
719
}
@@ -1114,12 +1119,18 @@ func (sc *serverConn) processPing(f *PingFrame) error {
1114
1119
// PROTOCOL_ERROR."
1115
1120
return ConnectionError (ErrCodeProtocol )
1116
1121
}
1122
+ if sc .inGoAway {
1123
+ return nil
1124
+ }
1117
1125
sc .writeFrame (frameWriteMsg {write : writePingAck {f }})
1118
1126
return nil
1119
1127
}
1120
1128
1121
1129
func (sc * serverConn ) processWindowUpdate (f * WindowUpdateFrame ) error {
1122
1130
sc .serveG .check ()
1131
+ if sc .inGoAway {
1132
+ return nil
1133
+ }
1123
1134
switch {
1124
1135
case f .StreamID != 0 : // stream-level flow control
1125
1136
state , st := sc .state (f .StreamID )
@@ -1152,6 +1163,9 @@ func (sc *serverConn) processWindowUpdate(f *WindowUpdateFrame) error {
1152
1163
1153
1164
func (sc * serverConn ) processResetStream (f * RSTStreamFrame ) error {
1154
1165
sc .serveG .check ()
1166
+ if sc .inGoAway {
1167
+ return nil
1168
+ }
1155
1169
1156
1170
state , st := sc .state (f .StreamID )
1157
1171
if state == stateIdle {
@@ -1181,6 +1195,9 @@ func (sc *serverConn) closeStream(st *stream, err error) {
1181
1195
sc .setConnState (http .StateIdle )
1182
1196
}
1183
1197
delete (sc .streams , st .id )
1198
+ if len (sc .streams ) == 0 && sc .srv .IdleTimeout != 0 {
1199
+ sc .idleTimer .Reset (sc .srv .IdleTimeout )
1200
+ }
1184
1201
if p := st .body ; p != nil {
1185
1202
// Return any buffered unread bytes worth of conn-level flow control.
1186
1203
// See golang.org/issue/16481
@@ -1204,6 +1221,9 @@ func (sc *serverConn) processSettings(f *SettingsFrame) error {
1204
1221
}
1205
1222
return nil
1206
1223
}
1224
+ if sc .inGoAway {
1225
+ return nil
1226
+ }
1207
1227
if err := f .ForeachSetting (sc .processSetting ); err != nil {
1208
1228
return err
1209
1229
}
@@ -1275,6 +1295,9 @@ func (sc *serverConn) processSettingInitialWindowSize(val uint32) error {
1275
1295
1276
1296
func (sc * serverConn ) processData (f * DataFrame ) error {
1277
1297
sc .serveG .check ()
1298
+ if sc .inGoAway {
1299
+ return nil
1300
+ }
1278
1301
data := f .Data ()
1279
1302
1280
1303
// "If a DATA frame is received whose stream is not in "open"
@@ -1412,6 +1435,10 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
1412
1435
}
1413
1436
sc .maxStreamID = id
1414
1437
1438
+ if sc .idleTimer != nil {
1439
+ sc .idleTimer .Stop ()
1440
+ }
1441
+
1415
1442
ctx , cancelCtx := contextWithCancel (sc .baseCtx )
1416
1443
st = & stream {
1417
1444
sc : sc ,
@@ -1524,6 +1551,9 @@ func (st *stream) processTrailerHeaders(f *MetaHeadersFrame) error {
1524
1551
}
1525
1552
1526
1553
func (sc * serverConn ) processPriority (f * PriorityFrame ) error {
1554
+ if sc .inGoAway {
1555
+ return nil
1556
+ }
1527
1557
adjustStreamPriority (sc .streams , f .StreamID , f .PriorityParam )
1528
1558
return nil
1529
1559
}
0 commit comments