GRPCIdleHandlerStateMachineTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  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 NIOCore
  18. import NIOEmbedded
  19. import NIOHTTP2
  20. import XCTest
  21. class GRPCIdleHandlerStateMachineTests: GRPCTestCase {
  22. private func makeClientStateMachine() -> GRPCIdleHandlerStateMachine {
  23. return GRPCIdleHandlerStateMachine(role: .client, logger: self.clientLogger)
  24. }
  25. private func makeNoOpScheduled() -> Scheduled<Void> {
  26. let loop = EmbeddedEventLoop()
  27. return loop.scheduleTask(deadline: .distantFuture) { return () }
  28. }
  29. func testInactiveBeforeSettings() {
  30. var stateMachine = self.makeClientStateMachine()
  31. let op1 = stateMachine.channelInactive()
  32. op1.assertConnectionManager(.inactive)
  33. }
  34. func testInactiveAfterSettings() {
  35. var stateMachine = self.makeClientStateMachine()
  36. let op1 = stateMachine.receiveSettings([])
  37. op1.assertConnectionManager(.ready)
  38. let readyStateMachine = stateMachine
  39. // Inactive with a stream open.
  40. let op2 = stateMachine.streamCreated(withID: 1)
  41. op2.assertDoNothing()
  42. let op3 = stateMachine.channelInactive()
  43. op3.assertConnectionManager(.inactive)
  44. // Inactive with no open streams.
  45. stateMachine = readyStateMachine
  46. let op4 = stateMachine.channelInactive()
  47. op4.assertConnectionManager(.idle)
  48. }
  49. func testInactiveWhenWaitingToIdle() {
  50. var stateMachine = self.makeClientStateMachine()
  51. // Become ready.
  52. let op1 = stateMachine.receiveSettings([])
  53. op1.assertConnectionManager(.ready)
  54. op1.assertScheduleIdleTimeout()
  55. // Schedule the timeout.
  56. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  57. op2.assertDoNothing()
  58. // Become inactive unexpectedly.
  59. let op3 = stateMachine.channelInactive()
  60. op3.assertConnectionManager(.idle)
  61. }
  62. func testInactiveWhenQuiescing() {
  63. var stateMachine = self.makeClientStateMachine()
  64. // Become ready.
  65. let op1 = stateMachine.receiveSettings([])
  66. op1.assertConnectionManager(.ready)
  67. // Try a few combinations: initiator of shutdown, and whether streams are open or not when
  68. // shutdown is initiated.
  69. let readyStateMachine = stateMachine
  70. // (1) Peer initiates shutdown, no streams are open.
  71. do {
  72. let op2 = stateMachine.receiveGoAway()
  73. op2.assertGoAway(streamID: .rootStream)
  74. op2.assertShouldClose()
  75. // We become idle.
  76. let op3 = stateMachine.channelInactive()
  77. op3.assertConnectionManager(.idle)
  78. }
  79. // (2) We initiate shutdown, no streams are open.
  80. stateMachine = readyStateMachine
  81. do {
  82. let op2 = stateMachine.initiateGracefulShutdown()
  83. op2.assertGoAway(streamID: .rootStream)
  84. op2.assertShouldClose()
  85. // We become idle.
  86. let op3 = stateMachine.channelInactive()
  87. op3.assertConnectionManager(.idle)
  88. }
  89. stateMachine = readyStateMachine
  90. _ = stateMachine.streamCreated(withID: 1)
  91. let streamOpenStateMachine = stateMachine
  92. // (3) Peer initiates shutdown, streams are open.
  93. do {
  94. let op2 = stateMachine.receiveGoAway()
  95. op2.assertNoGoAway()
  96. op2.assertShouldNotClose()
  97. // We become inactive.
  98. let op3 = stateMachine.channelInactive()
  99. op3.assertConnectionManager(.inactive)
  100. }
  101. // (4) We initiate shutdown, streams are open.
  102. stateMachine = streamOpenStateMachine
  103. do {
  104. let op2 = stateMachine.initiateGracefulShutdown()
  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.assertShouldNotClose()
  178. // Receive a GOAWAY; no change.
  179. let op3 = stateMachine.receiveGoAway()
  180. op3.assertDoNothing()
  181. // Close the remaining open stream, connection should close as a result.
  182. let op4 = stateMachine.streamClosed(withID: 1)
  183. op4.assertShouldClose()
  184. // Connection closed.
  185. let op5 = stateMachine.channelInactive()
  186. op5.assertConnectionManager(.inactive)
  187. }
  188. func testInitiateGracefulShutdownWhenWaitingToIdle() {
  189. var stateMachine = self.makeClientStateMachine()
  190. // Become 'ready'
  191. let op1 = stateMachine.receiveSettings([])
  192. op1.assertConnectionManager(.ready)
  193. op1.assertScheduleIdleTimeout()
  194. // Schedule the task.
  195. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  196. op2.assertDoNothing()
  197. // Initiate shutdown: cancel the timeout, send a GOAWAY and close.
  198. let op3 = stateMachine.initiateGracefulShutdown()
  199. op3.assertCancelIdleTimeout()
  200. op3.assertGoAway(streamID: .rootStream)
  201. op3.assertShouldClose()
  202. // Closed: become inactive.
  203. let op4 = stateMachine.channelInactive()
  204. op4.assertConnectionManager(.inactive)
  205. }
  206. func testInitiateGracefulShutdownWhenQuiescing() {
  207. var stateMachine = self.makeClientStateMachine()
  208. // Become ready.
  209. let op1 = stateMachine.receiveSettings([])
  210. op1.assertConnectionManager(.ready)
  211. op1.assertScheduleIdleTimeout()
  212. // Open a few streams.
  213. for streamID in stride(from: HTTP2StreamID(1), to: HTTP2StreamID(6), by: 2) {
  214. let op = stateMachine.streamCreated(withID: streamID)
  215. op.assertDoNothing()
  216. }
  217. // Receive a GOAWAY.
  218. let op2 = stateMachine.receiveGoAway()
  219. op2.assertNoGoAway()
  220. // Initiate shutdown from our side: we've already sent GOAWAY and have a stream open, we don't
  221. // need to do anything.
  222. let op3 = stateMachine.initiateGracefulShutdown()
  223. op3.assertDoNothing()
  224. // Close the first couple of streams; should be a no-op.
  225. for streamID in [HTTP2StreamID(1), HTTP2StreamID(3)] {
  226. let op = stateMachine.streamClosed(withID: streamID)
  227. op.assertDoNothing()
  228. }
  229. // Close the final stream.
  230. let op4 = stateMachine.streamClosed(withID: 5)
  231. op4.assertShouldClose()
  232. // Initiate shutdown again: we're closing so this should be a no-op.
  233. let op5 = stateMachine.initiateGracefulShutdown()
  234. op5.assertDoNothing()
  235. // Closed.
  236. let op6 = stateMachine.channelInactive()
  237. op6.assertConnectionManager(.inactive)
  238. }
  239. func testScheduleIdleTaskWhenStreamsAreOpen() {
  240. var stateMachine = self.makeClientStateMachine()
  241. // Become ready.
  242. let op1 = stateMachine.receiveSettings([])
  243. op1.assertConnectionManager(.ready)
  244. op1.assertScheduleIdleTimeout()
  245. // Open a stream before scheduling the task.
  246. let op2 = stateMachine.streamCreated(withID: 1)
  247. op2.assertDoNothing()
  248. // Schedule an idle timeout task: there are open streams so this should be cancelled.
  249. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  250. op3.assertCancelIdleTimeout()
  251. }
  252. func testScheduleIdleTaskWhenQuiescing() {
  253. var stateMachine = self.makeClientStateMachine()
  254. // Become ready.
  255. let op1 = stateMachine.receiveSettings([])
  256. op1.assertConnectionManager(.ready)
  257. op1.assertScheduleIdleTimeout()
  258. // Save the state machine so we can test a few branches.
  259. let readyStateMachine = stateMachine
  260. // (1) Scheduled when quiescing.
  261. let op2 = stateMachine.streamCreated(withID: 1)
  262. op2.assertDoNothing()
  263. // Start shutting down.
  264. _ = stateMachine.initiateGracefulShutdown()
  265. // Schedule an idle timeout task: we're quiescing, so cancel the task.
  266. let op4 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  267. op4.assertCancelIdleTimeout()
  268. // (2) Scheduled when closing.
  269. stateMachine = readyStateMachine
  270. let op5 = stateMachine.initiateGracefulShutdown()
  271. op5.assertGoAway(streamID: .rootStream)
  272. op5.assertShouldClose()
  273. // Schedule an idle timeout task: we're already closing, so cancel the task.
  274. let op6 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  275. op6.assertCancelIdleTimeout()
  276. }
  277. func testIdleTimeoutTaskFiresWhenIdle() {
  278. var stateMachine = self.makeClientStateMachine()
  279. // Become ready.
  280. let op1 = stateMachine.receiveSettings([])
  281. op1.assertConnectionManager(.ready)
  282. op1.assertScheduleIdleTimeout()
  283. // Schedule the task.
  284. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  285. op2.assertDoNothing()
  286. // Fire the task.
  287. let op3 = stateMachine.idleTimeoutTaskFired()
  288. op3.assertGoAway(streamID: .rootStream)
  289. op3.assertShouldClose()
  290. // Close.
  291. let op4 = stateMachine.channelInactive()
  292. op4.assertConnectionManager(.idle)
  293. }
  294. func testIdleTimeoutTaskFiresWhenClosed() {
  295. var stateMachine = self.makeClientStateMachine()
  296. // Become ready.
  297. let op1 = stateMachine.receiveSettings([])
  298. op1.assertConnectionManager(.ready)
  299. op1.assertScheduleIdleTimeout()
  300. // Schedule the task.
  301. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  302. op2.assertDoNothing()
  303. // Close.
  304. let op3 = stateMachine.channelInactive()
  305. op3.assertCancelIdleTimeout()
  306. // Fire the idle timeout task.
  307. let op4 = stateMachine.idleTimeoutTaskFired()
  308. op4.assertDoNothing()
  309. }
  310. func testShutdownNow() {
  311. var stateMachine = self.makeClientStateMachine()
  312. let op1 = stateMachine.shutdownNow()
  313. op1.assertGoAway(streamID: .rootStream)
  314. op1.assertShouldClose()
  315. let op2 = stateMachine.channelInactive()
  316. op2.assertConnectionManager(.inactive)
  317. }
  318. func testShutdownNowWhenWaitingToIdle() {
  319. var stateMachine = self.makeClientStateMachine()
  320. // Become ready.
  321. let op1 = stateMachine.receiveSettings([])
  322. op1.assertConnectionManager(.ready)
  323. op1.assertScheduleIdleTimeout()
  324. // Schedule the task.
  325. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  326. op2.assertDoNothing()
  327. let op3 = stateMachine.shutdownNow()
  328. op3.assertGoAway(streamID: .rootStream)
  329. op3.assertShouldClose()
  330. let op4 = stateMachine.channelInactive()
  331. op4.assertConnectionManager(.inactive)
  332. }
  333. func testShutdownNowWhenQuiescing() {
  334. var stateMachine = self.makeClientStateMachine()
  335. // Become ready.
  336. let op1 = stateMachine.receiveSettings([])
  337. op1.assertConnectionManager(.ready)
  338. op1.assertScheduleIdleTimeout()
  339. // Open a stream.
  340. let op2 = stateMachine.streamCreated(withID: 1)
  341. op2.assertDoNothing()
  342. // Initiate shutdown.
  343. let op3 = stateMachine.initiateGracefulShutdown()
  344. op3.assertNoGoAway()
  345. // Shutdown now.
  346. let op4 = stateMachine.shutdownNow()
  347. op4.assertShouldClose()
  348. }
  349. func testNormalFlow() {
  350. var stateMachine = self.makeClientStateMachine()
  351. // Become ready.
  352. let op1 = stateMachine.receiveSettings([])
  353. op1.assertConnectionManager(.ready)
  354. op1.assertScheduleIdleTimeout()
  355. // Schedule the task.
  356. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  357. op2.assertDoNothing()
  358. // Create a stream to cancel the task.
  359. let op3 = stateMachine.streamCreated(withID: 1)
  360. op3.assertCancelIdleTimeout()
  361. // Close the stream.
  362. let op4 = stateMachine.streamClosed(withID: 1)
  363. op4.assertScheduleIdleTimeout()
  364. // Receive a GOAWAY frame.
  365. let op5 = stateMachine.receiveGoAway()
  366. // We're the client, there are no server initiated streams, so GOAWAY with root stream.
  367. op5.assertGoAway(streamID: 0)
  368. // No open streams, so we can close now.
  369. op5.assertShouldClose()
  370. // Closed.
  371. let op6 = stateMachine.channelInactive()
  372. // The peer initiated shutdown by sending GOAWAY, we'll idle.
  373. op6.assertConnectionManager(.idle)
  374. }
  375. }
  376. extension GRPCIdleHandlerStateMachine.Operations {
  377. func assertDoNothing() {
  378. XCTAssertNil(self.connectionManagerEvent)
  379. XCTAssertNil(self.idleTask)
  380. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  381. XCTAssertFalse(self.shouldCloseChannel)
  382. }
  383. func assertGoAway(streamID: HTTP2StreamID) {
  384. XCTAssertEqual(self.sendGoAwayWithLastPeerInitiatedStreamID, streamID)
  385. }
  386. func assertNoGoAway() {
  387. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  388. }
  389. func assertScheduleIdleTimeout() {
  390. switch self.idleTask {
  391. case .some(.schedule):
  392. ()
  393. case .some(.cancel), .none:
  394. XCTFail("Expected 'schedule' but was '\(String(describing: self.idleTask))'")
  395. }
  396. }
  397. func assertCancelIdleTimeout() {
  398. switch self.idleTask {
  399. case .some(.cancel):
  400. ()
  401. case .some(.schedule), .none:
  402. XCTFail("Expected 'cancel' but was '\(String(describing: self.idleTask))'")
  403. }
  404. }
  405. func assertNoIdleTimeoutTask() {
  406. XCTAssertNil(self.idleTask)
  407. }
  408. func assertConnectionManager(_ event: GRPCIdleHandlerStateMachine.ConnectionManagerEvent) {
  409. XCTAssertEqual(self.connectionManagerEvent, event)
  410. }
  411. func assertNoConnectionManagerEvent() {
  412. XCTAssertNil(self.connectionManagerEvent)
  413. }
  414. func assertShouldClose() {
  415. XCTAssertTrue(self.shouldCloseChannel)
  416. }
  417. func assertShouldNotClose() {
  418. XCTAssertFalse(self.shouldCloseChannel)
  419. }
  420. }