GRPCIdleHandlerStateMachineTests.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. * Copyright 2020, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import NIOCore
  17. import NIOEmbedded
  18. import NIOHTTP2
  19. import XCTest
  20. @testable import GRPC
  21. class GRPCIdleHandlerStateMachineTests: GRPCTestCase {
  22. private func makeClientStateMachine() -> GRPCIdleHandlerStateMachine {
  23. return GRPCIdleHandlerStateMachine(role: .client, logger: self.clientLogger)
  24. }
  25. private func makeServerStateMachine() -> GRPCIdleHandlerStateMachine {
  26. return GRPCIdleHandlerStateMachine(role: .server, logger: self.serverLogger)
  27. }
  28. private func makeNoOpScheduled() -> Scheduled<Void> {
  29. let loop = EmbeddedEventLoop()
  30. return loop.scheduleTask(deadline: .distantFuture) { return () }
  31. }
  32. func testInactiveBeforeSettings() {
  33. var stateMachine = self.makeClientStateMachine()
  34. let op1 = stateMachine.channelInactive()
  35. op1.assertConnectionManager(.inactive)
  36. }
  37. func testInactiveAfterSettings() {
  38. var stateMachine = self.makeClientStateMachine()
  39. let op1 = stateMachine.receiveSettings([])
  40. op1.assertConnectionManager(.ready)
  41. let readyStateMachine = stateMachine
  42. // Inactive with a stream open.
  43. let op2 = stateMachine.streamCreated(withID: 1)
  44. op2.assertDoNothing()
  45. let op3 = stateMachine.channelInactive()
  46. op3.assertConnectionManager(.inactive)
  47. // Inactive with no open streams.
  48. stateMachine = readyStateMachine
  49. let op4 = stateMachine.channelInactive()
  50. op4.assertConnectionManager(.idle)
  51. }
  52. func testInactiveWhenWaitingToIdle() {
  53. var stateMachine = self.makeClientStateMachine()
  54. // Become ready.
  55. let op1 = stateMachine.receiveSettings([])
  56. op1.assertConnectionManager(.ready)
  57. op1.assertScheduleIdleTimeout()
  58. // Schedule the timeout.
  59. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  60. op2.assertDoNothing()
  61. // Become inactive unexpectedly.
  62. let op3 = stateMachine.channelInactive()
  63. op3.assertConnectionManager(.idle)
  64. }
  65. func testInactiveWhenQuiescing() {
  66. var stateMachine = self.makeClientStateMachine()
  67. // Become ready.
  68. let op1 = stateMachine.receiveSettings([])
  69. op1.assertConnectionManager(.ready)
  70. // Try a few combinations: initiator of shutdown, and whether streams are open or not when
  71. // shutdown is initiated.
  72. let readyStateMachine = stateMachine
  73. // (1) Peer initiates shutdown, no streams are open.
  74. do {
  75. let op2 = stateMachine.receiveGoAway()
  76. op2.assertGoAway(streamID: .rootStream)
  77. op2.assertShouldClose()
  78. // We become idle.
  79. let op3 = stateMachine.channelInactive()
  80. op3.assertConnectionManager(.idle)
  81. }
  82. // (2) We initiate shutdown, no streams are open.
  83. stateMachine = readyStateMachine
  84. do {
  85. let op2 = stateMachine.initiateGracefulShutdown()
  86. op2.assertGoAway(streamID: .rootStream)
  87. op2.assertShouldClose()
  88. // We become idle.
  89. let op3 = stateMachine.channelInactive()
  90. op3.assertConnectionManager(.idle)
  91. }
  92. stateMachine = readyStateMachine
  93. _ = stateMachine.streamCreated(withID: 1)
  94. let streamOpenStateMachine = stateMachine
  95. // (3) Peer initiates shutdown, streams are open.
  96. do {
  97. let op2 = stateMachine.receiveGoAway()
  98. op2.assertNoGoAway()
  99. op2.assertShouldNotClose()
  100. // We become inactive.
  101. let op3 = stateMachine.channelInactive()
  102. op3.assertConnectionManager(.inactive)
  103. }
  104. // (4) We initiate shutdown, streams are open.
  105. stateMachine = streamOpenStateMachine
  106. do {
  107. let op2 = stateMachine.initiateGracefulShutdown()
  108. op2.assertShouldNotClose()
  109. // We become inactive.
  110. let op3 = stateMachine.channelInactive()
  111. op3.assertConnectionManager(.inactive)
  112. }
  113. }
  114. func testReceiveSettings() {
  115. var stateMachine = self.makeClientStateMachine()
  116. // No open streams.
  117. let op1 = stateMachine.receiveSettings([])
  118. op1.assertConnectionManager(.ready)
  119. op1.assertScheduleIdleTimeout()
  120. // Open streams.
  121. stateMachine = self.makeClientStateMachine()
  122. let op2 = stateMachine.streamCreated(withID: 1)
  123. op2.assertDoNothing()
  124. let op3 = stateMachine.receiveSettings([])
  125. // No idle timeout to cancel.
  126. op3.assertConnectionManager(.ready)
  127. op3.assertNoIdleTimeoutTask()
  128. }
  129. func testReceiveSettingsWhenWaitingToIdle() {
  130. var stateMachine = self.makeClientStateMachine()
  131. // Become ready.
  132. let op1 = stateMachine.receiveSettings([])
  133. op1.assertConnectionManager(.ready)
  134. op1.assertScheduleIdleTimeout()
  135. // Receive more settings.
  136. let op2 = stateMachine.receiveSettings([])
  137. op2.assertDoNothing()
  138. // Schedule the timeout.
  139. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  140. op3.assertDoNothing()
  141. // More settings.
  142. let op4 = stateMachine.receiveSettings([])
  143. op4.assertDoNothing()
  144. }
  145. func testReceiveGoAwayWhenWaitingToIdle() {
  146. var stateMachine = self.makeClientStateMachine()
  147. // Become ready.
  148. let op1 = stateMachine.receiveSettings([])
  149. op1.assertConnectionManager(.ready)
  150. op1.assertScheduleIdleTimeout()
  151. // Schedule the timeout.
  152. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  153. op2.assertDoNothing()
  154. // Receive a GOAWAY frame.
  155. let op3 = stateMachine.receiveGoAway()
  156. op3.assertGoAway(streamID: .rootStream)
  157. op3.assertShouldClose()
  158. op3.assertCancelIdleTimeout()
  159. // Close; we were going to go idle anyway.
  160. let op4 = stateMachine.channelInactive()
  161. op4.assertConnectionManager(.idle)
  162. }
  163. func testInitiateGracefulShutdownWithNoOpenStreams() {
  164. var stateMachine = self.makeClientStateMachine()
  165. // No open streams: so GOAWAY and close.
  166. let op1 = stateMachine.initiateGracefulShutdown()
  167. op1.assertGoAway(streamID: .rootStream)
  168. op1.assertShouldClose()
  169. // Closed.
  170. let op2 = stateMachine.channelInactive()
  171. op2.assertConnectionManager(.inactive)
  172. }
  173. func testInitiateGracefulShutdownWithOpenStreams() {
  174. var stateMachine = self.makeClientStateMachine()
  175. // Open a stream.
  176. let op1 = stateMachine.streamCreated(withID: 1)
  177. op1.assertDoNothing()
  178. // Initiate shutdown.
  179. let op2 = stateMachine.initiateGracefulShutdown()
  180. op2.assertShouldNotClose()
  181. // Receive a GOAWAY; no change.
  182. let op3 = stateMachine.receiveGoAway()
  183. op3.assertDoNothing()
  184. // Close the remaining open stream, connection should close as a result.
  185. let op4 = stateMachine.streamClosed(withID: 1)
  186. op4.assertShouldClose()
  187. // Connection closed.
  188. let op5 = stateMachine.channelInactive()
  189. op5.assertConnectionManager(.inactive)
  190. }
  191. func testInitiateGracefulShutdownWhenWaitingToIdle() {
  192. var stateMachine = self.makeClientStateMachine()
  193. // Become 'ready'
  194. let op1 = stateMachine.receiveSettings([])
  195. op1.assertConnectionManager(.ready)
  196. op1.assertScheduleIdleTimeout()
  197. // Schedule the task.
  198. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  199. op2.assertDoNothing()
  200. // Initiate shutdown: cancel the timeout, send a GOAWAY and close.
  201. let op3 = stateMachine.initiateGracefulShutdown()
  202. op3.assertCancelIdleTimeout()
  203. op3.assertGoAway(streamID: .rootStream)
  204. op3.assertShouldClose()
  205. // Closed: become inactive.
  206. let op4 = stateMachine.channelInactive()
  207. op4.assertConnectionManager(.inactive)
  208. }
  209. func testInitiateGracefulShutdownWhenQuiescing() {
  210. var stateMachine = self.makeClientStateMachine()
  211. // Become ready.
  212. let op1 = stateMachine.receiveSettings([])
  213. op1.assertConnectionManager(.ready)
  214. op1.assertScheduleIdleTimeout()
  215. // Open a few streams.
  216. for streamID in stride(from: HTTP2StreamID(1), to: HTTP2StreamID(6), by: 2) {
  217. let op = stateMachine.streamCreated(withID: streamID)
  218. op.assertDoNothing()
  219. }
  220. // Receive a GOAWAY.
  221. let op2 = stateMachine.receiveGoAway()
  222. op2.assertNoGoAway()
  223. // Initiate shutdown from our side: we've already sent GOAWAY and have a stream open, we don't
  224. // need to do anything.
  225. let op3 = stateMachine.initiateGracefulShutdown()
  226. op3.assertDoNothing()
  227. // Close the first couple of streams; should be a no-op.
  228. for streamID in [HTTP2StreamID(1), HTTP2StreamID(3)] {
  229. let op = stateMachine.streamClosed(withID: streamID)
  230. op.assertDoNothing()
  231. }
  232. // Close the final stream.
  233. let op4 = stateMachine.streamClosed(withID: 5)
  234. op4.assertShouldClose()
  235. // Initiate shutdown again: we're closing so this should be a no-op.
  236. let op5 = stateMachine.initiateGracefulShutdown()
  237. op5.assertDoNothing()
  238. // Closed.
  239. let op6 = stateMachine.channelInactive()
  240. op6.assertConnectionManager(.inactive)
  241. }
  242. func testScheduleIdleTaskWhenStreamsAreOpen() {
  243. var stateMachine = self.makeClientStateMachine()
  244. // Become ready.
  245. let op1 = stateMachine.receiveSettings([])
  246. op1.assertConnectionManager(.ready)
  247. op1.assertScheduleIdleTimeout()
  248. // Open a stream before scheduling the task.
  249. let op2 = stateMachine.streamCreated(withID: 1)
  250. op2.assertDoNothing()
  251. // Schedule an idle timeout task: there are open streams so this should be cancelled.
  252. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  253. op3.assertCancelIdleTimeout()
  254. }
  255. func testScheduleIdleTaskWhenQuiescing() {
  256. var stateMachine = self.makeClientStateMachine()
  257. // Become ready.
  258. let op1 = stateMachine.receiveSettings([])
  259. op1.assertConnectionManager(.ready)
  260. op1.assertScheduleIdleTimeout()
  261. // Save the state machine so we can test a few branches.
  262. let readyStateMachine = stateMachine
  263. // (1) Scheduled when quiescing.
  264. let op2 = stateMachine.streamCreated(withID: 1)
  265. op2.assertDoNothing()
  266. // Start shutting down.
  267. _ = stateMachine.initiateGracefulShutdown()
  268. // Schedule an idle timeout task: we're quiescing, so cancel the task.
  269. let op4 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  270. op4.assertCancelIdleTimeout()
  271. // (2) Scheduled when closing.
  272. stateMachine = readyStateMachine
  273. let op5 = stateMachine.initiateGracefulShutdown()
  274. op5.assertGoAway(streamID: .rootStream)
  275. op5.assertShouldClose()
  276. // Schedule an idle timeout task: we're already closing, so cancel the task.
  277. let op6 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  278. op6.assertCancelIdleTimeout()
  279. }
  280. func testIdleTimeoutTaskFiresWhenIdle() {
  281. var stateMachine = self.makeClientStateMachine()
  282. // Become ready.
  283. let op1 = stateMachine.receiveSettings([])
  284. op1.assertConnectionManager(.ready)
  285. op1.assertScheduleIdleTimeout()
  286. // Schedule the task.
  287. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  288. op2.assertDoNothing()
  289. // Fire the task.
  290. let op3 = stateMachine.idleTimeoutTaskFired()
  291. op3.assertGoAway(streamID: .rootStream)
  292. op3.assertShouldClose()
  293. // Close.
  294. let op4 = stateMachine.channelInactive()
  295. op4.assertConnectionManager(.idle)
  296. }
  297. func testIdleTimeoutTaskFiresWhenClosed() {
  298. var stateMachine = self.makeClientStateMachine()
  299. // Become ready.
  300. let op1 = stateMachine.receiveSettings([])
  301. op1.assertConnectionManager(.ready)
  302. op1.assertScheduleIdleTimeout()
  303. // Schedule the task.
  304. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  305. op2.assertDoNothing()
  306. // Close.
  307. let op3 = stateMachine.channelInactive()
  308. op3.assertCancelIdleTimeout()
  309. // Fire the idle timeout task.
  310. let op4 = stateMachine.idleTimeoutTaskFired()
  311. op4.assertDoNothing()
  312. }
  313. func testShutdownNow() {
  314. var stateMachine = self.makeClientStateMachine()
  315. let op1 = stateMachine.shutdownNow()
  316. op1.assertGoAway(streamID: .rootStream)
  317. op1.assertShouldClose()
  318. let op2 = stateMachine.channelInactive()
  319. op2.assertConnectionManager(.inactive)
  320. }
  321. func testShutdownNowWhenWaitingToIdle() {
  322. var stateMachine = self.makeClientStateMachine()
  323. // Become ready.
  324. let op1 = stateMachine.receiveSettings([])
  325. op1.assertConnectionManager(.ready)
  326. op1.assertScheduleIdleTimeout()
  327. // Schedule the task.
  328. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  329. op2.assertDoNothing()
  330. let op3 = stateMachine.shutdownNow()
  331. op3.assertGoAway(streamID: .rootStream)
  332. op3.assertShouldClose()
  333. let op4 = stateMachine.channelInactive()
  334. op4.assertConnectionManager(.inactive)
  335. }
  336. func testShutdownNowWhenQuiescing() {
  337. var stateMachine = self.makeClientStateMachine()
  338. // Become ready.
  339. let op1 = stateMachine.receiveSettings([])
  340. op1.assertConnectionManager(.ready)
  341. op1.assertScheduleIdleTimeout()
  342. // Open a stream.
  343. let op2 = stateMachine.streamCreated(withID: 1)
  344. op2.assertDoNothing()
  345. // Initiate shutdown.
  346. let op3 = stateMachine.initiateGracefulShutdown()
  347. op3.assertNoGoAway()
  348. // Shutdown now.
  349. let op4 = stateMachine.shutdownNow()
  350. op4.assertShouldClose()
  351. }
  352. func testNormalFlow() {
  353. var stateMachine = self.makeClientStateMachine()
  354. // Become ready.
  355. let op1 = stateMachine.receiveSettings([])
  356. op1.assertConnectionManager(.ready)
  357. op1.assertScheduleIdleTimeout()
  358. // Schedule the task.
  359. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  360. op2.assertDoNothing()
  361. // Create a stream to cancel the task.
  362. let op3 = stateMachine.streamCreated(withID: 1)
  363. op3.assertCancelIdleTimeout()
  364. // Close the stream.
  365. let op4 = stateMachine.streamClosed(withID: 1)
  366. op4.assertScheduleIdleTimeout()
  367. // Receive a GOAWAY frame.
  368. let op5 = stateMachine.receiveGoAway()
  369. // We're the client, there are no server initiated streams, so GOAWAY with root stream.
  370. op5.assertGoAway(streamID: 0)
  371. // No open streams, so we can close now.
  372. op5.assertShouldClose()
  373. // Closed.
  374. let op6 = stateMachine.channelInactive()
  375. // The peer initiated shutdown by sending GOAWAY, we'll idle.
  376. op6.assertConnectionManager(.idle)
  377. }
  378. func testClientSendsGoAwayAndOpensStream() {
  379. var stateMachine = self.makeServerStateMachine()
  380. let op1 = stateMachine.receiveSettings([])
  381. op1.assertConnectionManager(.ready)
  382. op1.assertScheduleIdleTimeout()
  383. // Schedule the idle timeout.
  384. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  385. op2.assertDoNothing()
  386. // Create a stream to cancel the task.
  387. let op3 = stateMachine.streamCreated(withID: 1)
  388. op3.assertCancelIdleTimeout()
  389. // Receive a GOAWAY frame from the client.
  390. let op4 = stateMachine.receiveGoAway()
  391. op4.assertGoAway(streamID: .maxID)
  392. op4.assertShouldPingAfterGoAway()
  393. // Create another stream. This is fine, the client hasn't ack'd the ping yet.
  394. let op5 = stateMachine.streamCreated(withID: 7)
  395. op5.assertDoNothing()
  396. // Receiving the ping is handled by a different state machine which will tell us to ratchet
  397. // down the go away stream ID.
  398. let op6 = stateMachine.ratchetDownGoAwayStreamID()
  399. op6.assertGoAway(streamID: 7)
  400. op6.assertShouldNotPingAfterGoAway()
  401. let op7 = stateMachine.streamClosed(withID: 7)
  402. op7.assertDoNothing()
  403. let op8 = stateMachine.streamClosed(withID: 1)
  404. op8.assertShouldClose()
  405. }
  406. func testRatchetDownStreamIDWhenNotQuiescing() {
  407. var stateMachine = self.makeServerStateMachine()
  408. _ = stateMachine.receiveSettings([])
  409. // from the 'operating' state.
  410. stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
  411. // move to the 'waiting to idle' state.
  412. let promise = EmbeddedEventLoop().makePromise(of: Void.self)
  413. let task = Scheduled(promise: promise, cancellationTask: {})
  414. stateMachine.scheduledIdleTimeoutTask(task).assertDoNothing()
  415. promise.succeed(())
  416. stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
  417. // move to 'closing'
  418. _ = stateMachine.idleTimeoutTaskFired()
  419. stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
  420. // move to 'closed'
  421. _ = stateMachine.channelInactive()
  422. stateMachine.ratchetDownGoAwayStreamID().assertDoNothing()
  423. }
  424. }
  425. extension GRPCIdleHandlerStateMachine.Operations {
  426. func assertDoNothing() {
  427. XCTAssertNil(self.connectionManagerEvent)
  428. XCTAssertNil(self.idleTask)
  429. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  430. XCTAssertFalse(self.shouldCloseChannel)
  431. XCTAssertFalse(self.shouldPingAfterGoAway)
  432. }
  433. func assertGoAway(streamID: HTTP2StreamID) {
  434. XCTAssertEqual(self.sendGoAwayWithLastPeerInitiatedStreamID, streamID)
  435. }
  436. func assertNoGoAway() {
  437. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  438. }
  439. func assertScheduleIdleTimeout() {
  440. switch self.idleTask {
  441. case .some(.schedule):
  442. ()
  443. case .some(.cancel), .none:
  444. XCTFail("Expected 'schedule' but was '\(String(describing: self.idleTask))'")
  445. }
  446. }
  447. func assertCancelIdleTimeout() {
  448. switch self.idleTask {
  449. case .some(.cancel):
  450. ()
  451. case .some(.schedule), .none:
  452. XCTFail("Expected 'cancel' but was '\(String(describing: self.idleTask))'")
  453. }
  454. }
  455. func assertNoIdleTimeoutTask() {
  456. XCTAssertNil(self.idleTask)
  457. }
  458. func assertConnectionManager(_ event: GRPCIdleHandlerStateMachine.ConnectionManagerEvent) {
  459. XCTAssertEqual(self.connectionManagerEvent, event)
  460. }
  461. func assertNoConnectionManagerEvent() {
  462. XCTAssertNil(self.connectionManagerEvent)
  463. }
  464. func assertShouldClose() {
  465. XCTAssertTrue(self.shouldCloseChannel)
  466. }
  467. func assertShouldNotClose() {
  468. XCTAssertFalse(self.shouldCloseChannel)
  469. }
  470. func assertShouldPingAfterGoAway() {
  471. XCTAssert(self.shouldPingAfterGoAway)
  472. }
  473. func assertShouldNotPingAfterGoAway() {
  474. XCTAssertFalse(self.shouldPingAfterGoAway)
  475. }
  476. }