Переглянути джерело

Ignore state machine inputs in unexpected states (#1374)

Motivation:

When we ratchet down the last stream ID in a GOAWAY frame, we should
only do it if we're quiescing. If we aren't quiescing then we should
just ignore the request to do it.

Modifications:

- Ignore requests to ratchet down the last stream ID in a GOAWAY if
  we're not quiescing.

Result:

Fewer traps.
George Barnett 3 роки тому
батько
коміт
593fe0fe93

+ 2 - 4
Sources/GRPC/GRPCIdleHandlerStateMachine.swift

@@ -516,10 +516,8 @@ struct GRPCIdleHandlerStateMachine {
     case let .quiescing(state):
       let streamID = state.lastPeerInitiatedStreamID
       operations.sendGoAwayFrame(lastPeerInitiatedStreamID: streamID)
-    case .operating, .waitingToIdle:
-      // We can only ratchet down the stream ID if we're already quiescing.
-      preconditionFailure()
-    case .closing, .closed:
+    case .operating, .waitingToIdle, .closing, .closed:
+      // We can only need to ratchet down the stream ID if we're already quiescing.
       ()
     }
 

+ 1 - 1
Sources/GRPC/Version.swift

@@ -22,7 +22,7 @@ internal enum Version {
   internal static let minor = 7
 
   /// The patch version.
-  internal static let patch = 2
+  internal static let patch = 3
 
   /// The version string.
   internal static let versionString = "\(major).\(minor).\(patch)"

+ 23 - 0
Tests/GRPCTests/GRPCIdleHandlerStateMachineTests.swift

@@ -510,6 +510,29 @@ class GRPCIdleHandlerStateMachineTests: GRPCTestCase {
     let op8 = stateMachine.streamClosed(withID: 1)
     op8.assertShouldClose()
   }
+
+  func testRatchetDownStreamIDWhenNotQuiescing() {
+    var stateMachine = self.makeServerStateMachine()
+    _ = stateMachine.receiveSettings([])
+
+    // from the 'operating' state.
+    stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
+
+    // move to the 'waiting to idle' state.
+    let promise = EmbeddedEventLoop().makePromise(of: Void.self)
+    let task = Scheduled(promise: promise, cancellationTask: {})
+    stateMachine.scheduledIdleTimeoutTask(task).assertDoNothing()
+    promise.succeed(())
+    stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
+
+    // move to 'closing'
+    _ = stateMachine.idleTimeoutTaskFired()
+    stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
+
+    // move to 'closed'
+    _ = stateMachine.channelInactive()
+    stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
+  }
 }
 
 extension GRPCIdleHandlerStateMachine.Operations {