|
|
@@ -853,8 +853,13 @@ func (sc *serverConn) serve() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if sc.inGoAway && sc.curOpenStreams() == 0 && !sc.needToSendGoAway && !sc.writingFrame {
|
|
|
- return
|
|
|
+ // Start the shutdown timer after sending a GOAWAY. When sending GOAWAY
|
|
|
+ // with no error code (graceful shutdown), don't start the timer until
|
|
|
+ // all open streams have been completed.
|
|
|
+ sentGoAway := sc.inGoAway && !sc.needToSendGoAway && !sc.writingFrame
|
|
|
+ gracefulShutdownComplete := sc.goAwayCode == ErrCodeNo && sc.curOpenStreams() == 0
|
|
|
+ if sentGoAway && sc.shutdownTimer == nil && (sc.goAwayCode != ErrCodeNo || gracefulShutdownComplete) {
|
|
|
+ sc.shutDownIn(goAwayTimeout)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
@@ -1218,30 +1223,31 @@ func (sc *serverConn) startGracefulShutdown() {
|
|
|
sc.shutdownOnce.Do(func() { sc.sendServeMsg(gracefulShutdownMsg) })
|
|
|
}
|
|
|
|
|
|
+// After sending GOAWAY, the connection will close after goAwayTimeout.
|
|
|
+// If we close the connection immediately after sending GOAWAY, there may
|
|
|
+// be unsent data in our kernel receive buffer, which will cause the kernel
|
|
|
+// to send a TCP RST on close() instead of a FIN. This RST will abort the
|
|
|
+// connection immediately, whether or not the client had received the GOAWAY.
|
|
|
+//
|
|
|
+// Ideally we should delay for at least 1 RTT + epsilon so the client has
|
|
|
+// a chance to read the GOAWAY and stop sending messages. Measuring RTT
|
|
|
+// is hard, so we approximate with 1 second. See golang.org/issue/18701.
|
|
|
+//
|
|
|
+// This is a var so it can be shorter in tests, where all requests uses the
|
|
|
+// loopback interface making the expected RTT very small.
|
|
|
+//
|
|
|
+// TODO: configurable?
|
|
|
+var goAwayTimeout = 1 * time.Second
|
|
|
+
|
|
|
func (sc *serverConn) startGracefulShutdownInternal() {
|
|
|
- sc.goAwayIn(ErrCodeNo, 0)
|
|
|
+ sc.goAway(ErrCodeNo)
|
|
|
}
|
|
|
|
|
|
func (sc *serverConn) goAway(code ErrCode) {
|
|
|
- sc.serveG.check()
|
|
|
- var forceCloseIn time.Duration
|
|
|
- if code != ErrCodeNo {
|
|
|
- forceCloseIn = 250 * time.Millisecond
|
|
|
- } else {
|
|
|
- // TODO: configurable
|
|
|
- forceCloseIn = 1 * time.Second
|
|
|
- }
|
|
|
- sc.goAwayIn(code, forceCloseIn)
|
|
|
-}
|
|
|
-
|
|
|
-func (sc *serverConn) goAwayIn(code ErrCode, forceCloseIn time.Duration) {
|
|
|
sc.serveG.check()
|
|
|
if sc.inGoAway {
|
|
|
return
|
|
|
}
|
|
|
- if forceCloseIn != 0 {
|
|
|
- sc.shutDownIn(forceCloseIn)
|
|
|
- }
|
|
|
sc.inGoAway = true
|
|
|
sc.needToSendGoAway = true
|
|
|
sc.goAwayCode = code
|