GRPCIdleHandlerStateMachineTests.swift 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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.assertGoAway(streamID: .rootStream)
  106. op2.assertShouldNotClose()
  107. // We become inactive.
  108. let op3 = stateMachine.channelInactive()
  109. op3.assertConnectionManager(.inactive)
  110. }
  111. }
  112. func testReceiveSettings() {
  113. var stateMachine = self.makeClientStateMachine()
  114. // No open streams.
  115. let op1 = stateMachine.receiveSettings([])
  116. op1.assertConnectionManager(.ready)
  117. op1.assertScheduleIdleTimeout()
  118. // Open streams.
  119. stateMachine = self.makeClientStateMachine()
  120. let op2 = stateMachine.streamCreated(withID: 1)
  121. op2.assertDoNothing()
  122. let op3 = stateMachine.receiveSettings([])
  123. // No idle timeout to cancel.
  124. op3.assertConnectionManager(.ready)
  125. op3.assertNoIdleTimeoutTask()
  126. }
  127. func testReceiveSettingsWhenWaitingToIdle() {
  128. var stateMachine = self.makeClientStateMachine()
  129. // Become ready.
  130. let op1 = stateMachine.receiveSettings([])
  131. op1.assertConnectionManager(.ready)
  132. op1.assertScheduleIdleTimeout()
  133. // Receive more settings.
  134. let op2 = stateMachine.receiveSettings([])
  135. op2.assertDoNothing()
  136. // Schedule the timeout.
  137. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  138. op3.assertDoNothing()
  139. // More settings.
  140. let op4 = stateMachine.receiveSettings([])
  141. op4.assertDoNothing()
  142. }
  143. func testReceiveGoAwayWhenWaitingToIdle() {
  144. var stateMachine = self.makeClientStateMachine()
  145. // Become ready.
  146. let op1 = stateMachine.receiveSettings([])
  147. op1.assertConnectionManager(.ready)
  148. op1.assertScheduleIdleTimeout()
  149. // Schedule the timeout.
  150. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  151. op2.assertDoNothing()
  152. // Receive a GOAWAY frame.
  153. let op3 = stateMachine.receiveGoAway()
  154. op3.assertGoAway(streamID: .rootStream)
  155. op3.assertShouldClose()
  156. op3.assertCancelIdleTimeout()
  157. // Close; we were going to go idle anyway.
  158. let op4 = stateMachine.channelInactive()
  159. op4.assertConnectionManager(.idle)
  160. }
  161. func testInitiateGracefulShutdownWithNoOpenStreams() {
  162. var stateMachine = self.makeClientStateMachine()
  163. // No open streams: so GOAWAY and close.
  164. let op1 = stateMachine.initiateGracefulShutdown()
  165. op1.assertGoAway(streamID: .rootStream)
  166. op1.assertShouldClose()
  167. // Closed.
  168. let op2 = stateMachine.channelInactive()
  169. op2.assertConnectionManager(.inactive)
  170. }
  171. func testInitiateGracefulShutdownWithOpenStreams() {
  172. var stateMachine = self.makeClientStateMachine()
  173. // Open a stream.
  174. let op1 = stateMachine.streamCreated(withID: 1)
  175. op1.assertDoNothing()
  176. // Initiate shutdown.
  177. let op2 = stateMachine.initiateGracefulShutdown()
  178. op2.assertGoAway(streamID: .rootStream)
  179. op2.assertShouldNotClose()
  180. // Receive a GOAWAY; no change.
  181. let op3 = stateMachine.receiveGoAway()
  182. op3.assertDoNothing()
  183. // Close the remaining open stream, connection should close as a result.
  184. let op4 = stateMachine.streamClosed(withID: 1)
  185. op4.assertShouldClose()
  186. // Connection closed.
  187. let op5 = stateMachine.channelInactive()
  188. op5.assertConnectionManager(.inactive)
  189. }
  190. func testInitiateGracefulShutdownWhenWaitingToIdle() {
  191. var stateMachine = self.makeClientStateMachine()
  192. // Become 'ready'
  193. let op1 = stateMachine.receiveSettings([])
  194. op1.assertConnectionManager(.ready)
  195. op1.assertScheduleIdleTimeout()
  196. // Schedule the task.
  197. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  198. op2.assertDoNothing()
  199. // Initiate shutdown: cancel the timeout, send a GOAWAY and close.
  200. let op3 = stateMachine.initiateGracefulShutdown()
  201. op3.assertCancelIdleTimeout()
  202. op3.assertGoAway(streamID: .rootStream)
  203. op3.assertShouldClose()
  204. // Closed: become inactive.
  205. let op4 = stateMachine.channelInactive()
  206. op4.assertConnectionManager(.inactive)
  207. }
  208. func testInitiateGracefulShutdownWhenQuiescing() {
  209. var stateMachine = self.makeClientStateMachine()
  210. // Become ready.
  211. let op1 = stateMachine.receiveSettings([])
  212. op1.assertConnectionManager(.ready)
  213. op1.assertScheduleIdleTimeout()
  214. // Open a few streams.
  215. for streamID in stride(from: HTTP2StreamID(1), to: HTTP2StreamID(6), by: 2) {
  216. let op = stateMachine.streamCreated(withID: streamID)
  217. op.assertDoNothing()
  218. }
  219. // Receive a GOAWAY.
  220. let op2 = stateMachine.receiveGoAway()
  221. op2.assertNoGoAway()
  222. // Initiate shutdown from our side: we've already sent GOAWAY and have a stream open, we don't
  223. // need to do anything.
  224. let op3 = stateMachine.initiateGracefulShutdown()
  225. op3.assertDoNothing()
  226. // Close the first couple of streams; should be a no-op.
  227. for streamID in [HTTP2StreamID(1), HTTP2StreamID(3)] {
  228. let op = stateMachine.streamClosed(withID: streamID)
  229. op.assertDoNothing()
  230. }
  231. // Close the final stream.
  232. let op4 = stateMachine.streamClosed(withID: 5)
  233. op4.assertShouldClose()
  234. // Initiate shutdown again: we're closing so this should be a no-op.
  235. let op5 = stateMachine.initiateGracefulShutdown()
  236. op5.assertDoNothing()
  237. // Closed.
  238. let op6 = stateMachine.channelInactive()
  239. op6.assertConnectionManager(.inactive)
  240. }
  241. func testScheduleIdleTaskWhenStreamsAreOpen() {
  242. var stateMachine = self.makeClientStateMachine()
  243. // Become ready.
  244. let op1 = stateMachine.receiveSettings([])
  245. op1.assertConnectionManager(.ready)
  246. op1.assertScheduleIdleTimeout()
  247. // Open a stream before scheduling the task.
  248. let op2 = stateMachine.streamCreated(withID: 1)
  249. op2.assertDoNothing()
  250. // Schedule an idle timeout task: there are open streams so this should be cancelled.
  251. let op3 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  252. op3.assertCancelIdleTimeout()
  253. }
  254. func testScheduleIdleTaskWhenQuiescing() {
  255. var stateMachine = self.makeClientStateMachine()
  256. // Become ready.
  257. let op1 = stateMachine.receiveSettings([])
  258. op1.assertConnectionManager(.ready)
  259. op1.assertScheduleIdleTimeout()
  260. // Save the state machine so we can test a few branches.
  261. let readyStateMachine = stateMachine
  262. // (1) Scheduled when quiescing.
  263. let op2 = stateMachine.streamCreated(withID: 1)
  264. op2.assertDoNothing()
  265. // Start shutting down.
  266. let op3 = stateMachine.initiateGracefulShutdown()
  267. op3.assertGoAway(streamID: .rootStream)
  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.assertGoAway(streamID: .rootStream)
  348. // Shutdown now.
  349. let op4 = stateMachine.shutdownNow()
  350. op4.assertNoGoAway() // already sent.
  351. op4.assertShouldClose()
  352. }
  353. func testNormalFlow() {
  354. var stateMachine = self.makeClientStateMachine()
  355. // Become ready.
  356. let op1 = stateMachine.receiveSettings([])
  357. op1.assertConnectionManager(.ready)
  358. op1.assertScheduleIdleTimeout()
  359. // Schedule the task.
  360. let op2 = stateMachine.scheduledIdleTimeoutTask(self.makeNoOpScheduled())
  361. op2.assertDoNothing()
  362. // Create a stream to cancel the task.
  363. let op3 = stateMachine.streamCreated(withID: 1)
  364. op3.assertCancelIdleTimeout()
  365. // Close the stream.
  366. let op4 = stateMachine.streamClosed(withID: 1)
  367. op4.assertScheduleIdleTimeout()
  368. // Receive a GOAWAY frame.
  369. let op5 = stateMachine.receiveGoAway()
  370. // We're the client, there are no server initiated streams, so GOAWAY with root stream.
  371. op5.assertGoAway(streamID: 0)
  372. // No open streams, so we can close now.
  373. op5.assertShouldClose()
  374. // Closed.
  375. let op6 = stateMachine.channelInactive()
  376. // The peer initiated shutdown by sending GOAWAY, we'll idle.
  377. op6.assertConnectionManager(.idle)
  378. }
  379. }
  380. extension GRPCIdleHandlerStateMachine.Operations {
  381. func assertDoNothing() {
  382. XCTAssertNil(self.connectionManagerEvent)
  383. XCTAssertNil(self.idleTask)
  384. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  385. XCTAssertFalse(self.shouldCloseChannel)
  386. }
  387. func assertGoAway(streamID: HTTP2StreamID) {
  388. XCTAssertEqual(self.sendGoAwayWithLastPeerInitiatedStreamID, streamID)
  389. }
  390. func assertNoGoAway() {
  391. XCTAssertNil(self.sendGoAwayWithLastPeerInitiatedStreamID)
  392. }
  393. func assertScheduleIdleTimeout() {
  394. switch self.idleTask {
  395. case .some(.schedule):
  396. ()
  397. case .some(.cancel), .none:
  398. XCTFail("Expected 'schedule' but was '\(String(describing: self.idleTask))'")
  399. }
  400. }
  401. func assertCancelIdleTimeout() {
  402. switch self.idleTask {
  403. case .some(.cancel):
  404. ()
  405. case .some(.schedule), .none:
  406. XCTFail("Expected 'cancel' but was '\(String(describing: self.idleTask))'")
  407. }
  408. }
  409. func assertNoIdleTimeoutTask() {
  410. XCTAssertNil(self.idleTask)
  411. }
  412. func assertConnectionManager(_ event: GRPCIdleHandlerStateMachine.ConnectionManagerEvent) {
  413. XCTAssertEqual(self.connectionManagerEvent, event)
  414. }
  415. func assertNoConnectionManagerEvent() {
  416. XCTAssertNil(self.connectionManagerEvent)
  417. }
  418. func assertShouldClose() {
  419. XCTAssertTrue(self.shouldCloseChannel)
  420. }
  421. func assertShouldNotClose() {
  422. XCTAssertFalse(self.shouldCloseChannel)
  423. }
  424. }