GRPCIdleHandlerStateMachineTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  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. @testable import GRPC
  17. import NIO
  18. import NIOHTTP2
  19. import XCTest
  20. class GRPCIdleHandlerStateMachineTests: GRPCTestCase {
  21. private func makeClientStateMachine() -> GRPCIdleHandlerStateMachine {
  22. return GRPCIdleHandlerStateMachine(role: .client, logger: self.clientLogger)
  23. }
  24. private func makeNoOpScheduled() -> Scheduled<Void> {
  25. let loop = EmbeddedEventLoop()
  26. return loop.scheduleTask(deadline: .distantFuture) { return () }
  27. }
  28. func testInactiveBeforeSettings() {
  29. var stateMachine = self.makeClientStateMachine()
  30. let op1 = stateMachine.channelInactive()
  31. op1.assertConnectionManager(.inactive)
  32. }
  33. func testInactiveAfterSettings() {
  34. var stateMachine = self.makeClientStateMachine()
  35. let op1 = stateMachine.receiveSettings([])
  36. op1.assertConnectionManager(.ready)
  37. let readyStateMachine = stateMachine
  38. // Inactive with a stream open.
  39. let op2 = stateMachine.streamCreated(withID: 1)
  40. op2.assertDoNothing()
  41. let op3 = stateMachine.channelInactive()
  42. op3.assertConnectionManager(.inactive)
  43. // Inactive with no open streams.
  44. stateMachine = readyStateMachine
  45. let op4 = stateMachine.channelInactive()
  46. op4.assertConnectionManager(.idle)
  47. }
  48. func testInactiveWhenWaitingToIdle() {
  49. var stateMachine = self.makeClientStateMachine()
  50. // Become ready.
  51. let op1 = stateMachine.receiveSettings([])
  52. op1.assertConnectionManager(.ready)
  53. op1.assertScheduleIdleTimeout()
  54. // Schedule the timeout.
  55. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  56. op2.assertDoNothing()
  57. // Become inactive unexpectedly.
  58. let op3 = stateMachine.channelInactive()
  59. op3.assertConnectionManager(.idle)
  60. }
  61. func testInactiveWhenQuiescing() {
  62. var stateMachine = self.makeClientStateMachine()
  63. // Become ready.
  64. let op1 = stateMachine.receiveSettings([])
  65. op1.assertConnectionManager(.ready)
  66. // Try a few combinations: initiator of shutdown, and whether streams are open or not when
  67. // shutdown is initiated.
  68. let readyStateMachine = stateMachine
  69. // (1) Peer initiates shutdown, no streams are open.
  70. do {
  71. let op2 = stateMachine.receiveGoAway()
  72. op2.assertGoAway(streamID: .rootStream)
  73. op2.assertShouldClose()
  74. // We become idle.
  75. let op3 = stateMachine.channelInactive()
  76. op3.assertConnectionManager(.idle)
  77. }
  78. // (2) We initiate shutdown, no streams are open.
  79. stateMachine = readyStateMachine
  80. do {
  81. let op2 = stateMachine.initiateGracefulShutdown()
  82. op2.assertGoAway(streamID: .rootStream)
  83. op2.assertShouldClose()
  84. // We become idle.
  85. let op3 = stateMachine.channelInactive()
  86. op3.assertConnectionManager(.idle)
  87. }
  88. stateMachine = readyStateMachine
  89. _ = stateMachine.streamCreated(withID: 1)
  90. let streamOpenStateMachine = stateMachine
  91. // (3) Peer initiates shutdown, streams are open.
  92. do {
  93. let op2 = stateMachine.receiveGoAway()
  94. op2.assertNoGoAway()
  95. op2.assertShouldNotClose()
  96. // We become inactive.
  97. let op3 = stateMachine.channelInactive()
  98. op3.assertConnectionManager(.inactive)
  99. }
  100. // (4) We initiate shutdown, streams are open.
  101. stateMachine = streamOpenStateMachine
  102. do {
  103. let op2 = stateMachine.initiateGracefulShutdown()
  104. op2.assertGoAway(streamID: .rootStream)
  105. op2.assertShouldNotClose()
  106. // We become inactive.
  107. let op3 = stateMachine.channelInactive()
  108. op3.assertConnectionManager(.inactive)
  109. }
  110. }
  111. func testReceiveSettings() {
  112. var stateMachine = self.makeClientStateMachine()
  113. // No open streams.
  114. let op1 = stateMachine.receiveSettings([])
  115. op1.assertConnectionManager(.ready)
  116. op1.assertScheduleIdleTimeout()
  117. // Open streams.
  118. stateMachine = self.makeClientStateMachine()
  119. let op2 = stateMachine.streamCreated(withID: 1)
  120. op2.assertDoNothing()
  121. let op3 = stateMachine.receiveSettings([])
  122. // No idle timeout to cancel.
  123. op3.assertConnectionManager(.ready)
  124. op3.assertNoIdleTimeoutTask()
  125. }
  126. func testReceiveSettingsWhenWaitingToIdle() {
  127. var stateMachine = self.makeClientStateMachine()
  128. // Become ready.
  129. let op1 = stateMachine.receiveSettings([])
  130. op1.assertConnectionManager(.ready)
  131. op1.assertScheduleIdleTimeout()
  132. // Receive more settings.
  133. let op2 = stateMachine.receiveSettings([])
  134. op2.assertDoNothing()
  135. // Schedule the timeout.
  136. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  137. op3.assertDoNothing()
  138. // More settings.
  139. let op4 = stateMachine.receiveSettings([])
  140. op4.assertDoNothing()
  141. }
  142. func testReceiveGoAwayWhenWaitingToIdle() {
  143. var stateMachine = self.makeClientStateMachine()
  144. // Become ready.
  145. let op1 = stateMachine.receiveSettings([])
  146. op1.assertConnectionManager(.ready)
  147. op1.assertScheduleIdleTimeout()
  148. // Schedule the timeout.
  149. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  150. op2.assertDoNothing()
  151. // Receive a GOAWAY frame.
  152. let op3 = stateMachine.receiveGoAway()
  153. op3.assertGoAway(streamID: .rootStream)
  154. op3.assertShouldClose()
  155. op3.assertCancelIdleTimeout()
  156. // Close; we were going to go idle anyway.
  157. let op4 = stateMachine.channelInactive()
  158. op4.assertConnectionManager(.idle)
  159. }
  160. func testInitiateGracefulShutdownWithNoOpenStreams() {
  161. var stateMachine = self.makeClientStateMachine()
  162. // No open streams: so GOAWAY and close.
  163. let op1 = stateMachine.initiateGracefulShutdown()
  164. op1.assertGoAway(streamID: .rootStream)
  165. op1.assertShouldClose()
  166. // Closed.
  167. let op2 = stateMachine.channelInactive()
  168. op2.assertConnectionManager(.inactive)
  169. }
  170. func testInitiateGracefulShutdownWithOpenStreams() {
  171. var stateMachine = self.makeClientStateMachine()
  172. // Open a stream.
  173. let op1 = stateMachine.streamCreated(withID: 1)
  174. op1.assertDoNothing()
  175. // Initiate shutdown.
  176. let op2 = stateMachine.initiateGracefulShutdown()
  177. op2.assertGoAway(streamID: .rootStream)
  178. op2.assertShouldNotClose()
  179. // Receive a GOAWAY; no change.
  180. let op3 = stateMachine.receiveGoAway()
  181. op3.assertDoNothing()
  182. // Close the remaining open stream, connection should close as a result.
  183. let op4 = stateMachine.streamClosed(withID: 1)
  184. op4.assertShouldClose()
  185. // Connection closed.
  186. let op5 = stateMachine.channelInactive()
  187. op5.assertConnectionManager(.inactive)
  188. }
  189. func testInitiateGracefulShutdownWhenWaitingToIdle() {
  190. var stateMachine = self.makeClientStateMachine()
  191. // Become 'ready'
  192. let op1 = stateMachine.receiveSettings([])
  193. op1.assertConnectionManager(.ready)
  194. op1.assertScheduleIdleTimeout()
  195. // Schedule the task.
  196. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  197. op2.assertDoNothing()
  198. // Initiate shutdown: cancel the timeout, send a GOAWAY and close.
  199. let op3 = stateMachine.initiateGracefulShutdown()
  200. op3.assertCancelIdleTimeout()
  201. op3.assertGoAway(streamID: .rootStream)
  202. op3.assertShouldClose()
  203. // Closed: become inactive.
  204. let op4 = stateMachine.channelInactive()
  205. op4.assertConnectionManager(.inactive)
  206. }
  207. func testInitiateGracefulShutdownWhenQuiescing() {
  208. var stateMachine = self.makeClientStateMachine()
  209. // Become ready.
  210. let op1 = stateMachine.receiveSettings([])
  211. op1.assertConnectionManager(.ready)
  212. op1.assertScheduleIdleTimeout()
  213. // Open a few streams.
  214. for streamID in stride(from: HTTP2StreamID(1), to: HTTP2StreamID(6), by: 2) {
  215. let op = stateMachine.streamCreated(withID: streamID)
  216. op.assertDoNothing()
  217. }
  218. // Receive a GOAWAY.
  219. let op2 = stateMachine.receiveGoAway()
  220. op2.assertNoGoAway()
  221. // Initiate shutdown from our side: we've already sent GOAWAY and have a stream open, we don't
  222. // need to do anything.
  223. let op3 = stateMachine.initiateGracefulShutdown()
  224. op3.assertDoNothing()
  225. // Close the first couple of streams; should be a no-op.
  226. for streamID in [HTTP2StreamID(1), HTTP2StreamID(3)] {
  227. let op = stateMachine.streamClosed(withID: streamID)
  228. op.assertDoNothing()
  229. }
  230. // Close the final stream.
  231. let op4 = stateMachine.streamClosed(withID: 5)
  232. op4.assertShouldClose()
  233. // Initiate shutdown again: we're closing so this should be a no-op.
  234. let op5 = stateMachine.initiateGracefulShutdown()
  235. op5.assertDoNothing()
  236. // Closed.
  237. let op6 = stateMachine.channelInactive()
  238. op6.assertConnectionManager(.inactive)
  239. }
  240. func testScheduleIdleTaskWhenStreamsAreOpen() {
  241. var stateMachine = self.makeClientStateMachine()
  242. // Become ready.
  243. let op1 = stateMachine.receiveSettings([])
  244. op1.assertConnectionManager(.ready)
  245. op1.assertScheduleIdleTimeout()
  246. // Open a stream before scheduling the task.
  247. let op2 = stateMachine.streamCreated(withID: 1)
  248. op2.assertDoNothing()
  249. // Schedule an idle timeout task: there are open streams so this should be cancelled.
  250. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  251. op3.assertCancelIdleTimeout()
  252. }
  253. func testScheduleIdleTaskWhenQuiescing() {
  254. var stateMachine = self.makeClientStateMachine()
  255. // Become ready.
  256. let op1 = stateMachine.receiveSettings([])
  257. op1.assertConnectionManager(.ready)
  258. op1.assertScheduleIdleTimeout()
  259. // Save the state machine so we can test a few branches.
  260. let readyStateMachine = stateMachine
  261. // (1) Scheduled when quiescing.
  262. let op2 = stateMachine.streamCreated(withID: 1)
  263. op2.assertDoNothing()
  264. // Start shutting down.
  265. let op3 = stateMachine.initiateGracefulShutdown()
  266. op3.assertGoAway(streamID: .rootStream)
  267. // Schedule an idle timeout task: we're quiescing, so cancel the task.
  268. let op4 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  269. op4.assertCancelIdleTimeout()
  270. // (2) Scheduled when closing.
  271. stateMachine = readyStateMachine
  272. let op5 = stateMachine.initiateGracefulShutdown()
  273. op5.assertGoAway(streamID: .rootStream)
  274. op5.assertShouldClose()
  275. // Schedule an idle timeout task: we're already closing, so cancel the task.
  276. let op6 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  277. op6.assertCancelIdleTimeout()
  278. }
  279. func testIdleTimeoutTaskFiresWhenIdle() {
  280. var stateMachine = self.makeClientStateMachine()
  281. // Become ready.
  282. let op1 = stateMachine.receiveSettings([])
  283. op1.assertConnectionManager(.ready)
  284. op1.assertScheduleIdleTimeout()
  285. // Schedule the task.
  286. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  287. op2.assertDoNothing()
  288. // Fire the task.
  289. let op3 = stateMachine.idleTimeoutTaskFired()
  290. op3.assertGoAway(streamID: .rootStream)
  291. op3.assertShouldClose()
  292. // Close.
  293. let op4 = stateMachine.channelInactive()
  294. op4.assertConnectionManager(.idle)
  295. }
  296. func testIdleTimeoutTaskFiresWhenClosed() {
  297. var stateMachine = self.makeClientStateMachine()
  298. // Become ready.
  299. let op1 = stateMachine.receiveSettings([])
  300. op1.assertConnectionManager(.ready)
  301. op1.assertScheduleIdleTimeout()
  302. // Schedule the task.
  303. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  304. op2.assertDoNothing()
  305. // Close.
  306. let op3 = stateMachine.channelInactive()
  307. op3.assertCancelIdleTimeout()
  308. // Fire the idle timeout task.
  309. let op4 = stateMachine.idleTimeoutTaskFired()
  310. op4.assertDoNothing()
  311. }
  312. func testShutdownNow() {
  313. var stateMachine = self.makeClientStateMachine()
  314. let op1 = stateMachine.shutdownNow()
  315. op1.assertGoAway(streamID: .rootStream)
  316. op1.assertShouldClose()
  317. let op2 = stateMachine.channelInactive()
  318. op2.assertConnectionManager(.inactive)
  319. }
  320. func testShutdownNowWhenWaitingToIdle() {
  321. var stateMachine = self.makeClientStateMachine()
  322. // Become ready.
  323. let op1 = stateMachine.receiveSettings([])
  324. op1.assertConnectionManager(.ready)
  325. op1.assertScheduleIdleTimeout()
  326. // Schedule the task.
  327. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  328. op2.assertDoNothing()
  329. let op3 = stateMachine.shutdownNow()
  330. op3.assertGoAway(streamID: .rootStream)
  331. op3.assertShouldClose()
  332. let op4 = stateMachine.channelInactive()
  333. op4.assertConnectionManager(.inactive)
  334. }
  335. func testShutdownNowWhenQuiescing() {
  336. var stateMachine = self.makeClientStateMachine()
  337. // Become ready.
  338. let op1 = stateMachine.receiveSettings([])
  339. op1.assertConnectionManager(.ready)
  340. op1.assertScheduleIdleTimeout()
  341. // Open a stream.
  342. let op2 = stateMachine.streamCreated(withID: 1)
  343. op2.assertDoNothing()
  344. // Initiate shutdown.
  345. let op3 = stateMachine.initiateGracefulShutdown()
  346. op3.assertGoAway(streamID: .rootStream)
  347. // Shutdown now.
  348. let op4 = stateMachine.shutdownNow()
  349. op4.assertNoGoAway() // already sent.
  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. }
  379. extension GRPCIdleHandlerStateMachine.Operations {
  380. func assertDoNothing() {
  381. XCTAssertNil(self.connectionManagerEvent)
  382. XCTAssertNil(self.idleTask)
  383. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  384. XCTAssertFalse(self.shouldCloseChannel)
  385. }
  386. func assertGoAway(streamID: HTTP2StreamID) {
  387. XCTAssertEqual(self.sendGoAwayWithLastPeerInitiatedStreamID, streamID)
  388. }
  389. func assertNoGoAway() {
  390. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  391. }
  392. func assertScheduleIdleTimeout() {
  393. switch self.idleTask {
  394. case .some(.schedule):
  395. ()
  396. case .some(.cancel), .none:
  397. XCTFail("Expected 'schedule' but was '\(String(describing: self.idleTask))'")
  398. }
  399. }
  400. func assertCancelIdleTimeout() {
  401. switch self.idleTask {
  402. case .some(.cancel):
  403. ()
  404. case .some(.schedule), .none:
  405. XCTFail("Expected 'cancel' but was '\(String(describing: self.idleTask))'")
  406. }
  407. }
  408. func assertNoIdleTimeoutTask() {
  409. XCTAssertNil(self.idleTask)
  410. }
  411. func assertConnectionManager(_ event: GRPCIdleHandlerStateMachine.ConnectionManagerEvent) {
  412. XCTAssertEqual(self.connectionManagerEvent, event)
  413. }
  414. func assertNoConnectionManagerEvent() {
  415. XCTAssertNil(self.connectionManagerEvent)
  416. }
  417. func assertShouldClose() {
  418. XCTAssertTrue(self.shouldCloseChannel)
  419. }
  420. func assertShouldNotClose() {
  421. XCTAssertFalse(self.shouldCloseChannel)
  422. }
  423. }