ConnectionManagerTests.swift 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  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 ConnectionManagerTests: GRPCTestCase {
  21. private let loop = EmbeddedEventLoop()
  22. private let recorder = RecordingConnectivityDelegate()
  23. private var defaultConfiguration: ClientConnection.Configuration {
  24. return ClientConnection.Configuration(
  25. target: .unixDomainSocket("/ignored"),
  26. eventLoopGroup: self.loop,
  27. connectivityStateDelegate: self.recorder,
  28. connectionBackoff: nil,
  29. backgroundActivityLogger: self.clientLogger
  30. )
  31. }
  32. override func tearDown() {
  33. XCTAssertNoThrow(try self.loop.syncShutdownGracefully())
  34. super.tearDown()
  35. }
  36. private func waitForStateChange<Result>(
  37. from: ConnectivityState,
  38. to: ConnectivityState,
  39. timeout: DispatchTimeInterval = .seconds(1),
  40. body: () throws -> Result
  41. ) rethrows -> Result {
  42. self.recorder.expectChange {
  43. XCTAssertEqual($0, Change(from: from, to: to))
  44. }
  45. let result = try body()
  46. self.recorder.waitForExpectedChanges(timeout: timeout)
  47. return result
  48. }
  49. private func waitForStateChanges<Result>(
  50. _ changes: [Change],
  51. timeout: DispatchTimeInterval = .seconds(1),
  52. body: () throws -> Result
  53. ) rethrows -> Result {
  54. self.recorder.expectChanges(changes.count) {
  55. XCTAssertEqual($0, changes)
  56. }
  57. let result = try body()
  58. self.recorder.waitForExpectedChanges(timeout: timeout)
  59. return result
  60. }
  61. }
  62. extension ConnectionManagerTests {
  63. func testIdleShutdown() throws {
  64. let manager = ConnectionManager(configuration: self.defaultConfiguration, logger: self.logger)
  65. try self.waitForStateChange(from: .idle, to: .shutdown) {
  66. let shutdown = manager.shutdown()
  67. self.loop.run()
  68. XCTAssertNoThrow(try shutdown.wait())
  69. }
  70. // Getting a channel should fail.
  71. let channel = manager.getChannel()
  72. self.loop.run()
  73. XCTAssertThrowsError(try channel.wait())
  74. }
  75. func testConnectFromIdleFailsWithNoReconnect() {
  76. let channelPromise = self.loop.makePromise(of: Channel.self)
  77. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  78. return channelPromise.futureResult
  79. }
  80. let channel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  81. let channel = manager.getChannel()
  82. self.loop.run()
  83. return channel
  84. }
  85. self.waitForStateChange(from: .connecting, to: .shutdown) {
  86. channelPromise.fail(DoomedChannelError())
  87. }
  88. XCTAssertThrowsError(try channel.wait()) {
  89. XCTAssertTrue($0 is DoomedChannelError)
  90. }
  91. }
  92. func testConnectAndDisconnect() throws {
  93. let channelPromise = self.loop.makePromise(of: Channel.self)
  94. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  95. return channelPromise.futureResult
  96. }
  97. // Start the connection.
  98. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  99. let readyChannel = manager.getChannel()
  100. self.loop.run()
  101. return readyChannel
  102. }
  103. // Setup the real channel and activate it.
  104. let channel = EmbeddedChannel(
  105. handler: GRPCIdleHandler(mode: .client(manager)),
  106. loop: self.loop
  107. )
  108. channelPromise.succeed(channel)
  109. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  110. // Write a settings frame on the root stream; this'll make the channel 'ready'.
  111. try self.waitForStateChange(from: .connecting, to: .ready) {
  112. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  113. XCTAssertNoThrow(try channel.writeInbound(frame))
  114. }
  115. // Close the channel.
  116. try self.waitForStateChange(from: .ready, to: .shutdown) {
  117. // Now the channel should be available: shut it down,
  118. XCTAssertNoThrow(try readyChannel.flatMap { $0.close(mode: .all) }.wait())
  119. }
  120. }
  121. func testConnectAndIdle() throws {
  122. let channelPromise = self.loop.makePromise(of: Channel.self)
  123. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  124. return channelPromise.futureResult
  125. }
  126. // Start the connection.
  127. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  128. let readyChannel = manager.getChannel()
  129. self.loop.run()
  130. return readyChannel
  131. }
  132. // Setup the channel.
  133. let channel = EmbeddedChannel(
  134. handler: GRPCIdleHandler(mode: .client(manager)),
  135. loop: self.loop
  136. )
  137. channelPromise.succeed(channel)
  138. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  139. // Write a settings frame on the root stream; this'll make the channel 'ready'.
  140. try self.waitForStateChange(from: .connecting, to: .ready) {
  141. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  142. XCTAssertNoThrow(try channel.writeInbound(frame))
  143. // Wait for the channel, it _must_ be ready now.
  144. XCTAssertNoThrow(try readyChannel.wait())
  145. }
  146. // Go idle. This will shutdown the channel.
  147. try self.waitForStateChange(from: .ready, to: .idle) {
  148. self.loop.advanceTime(by: .minutes(5))
  149. XCTAssertNoThrow(try readyChannel.flatMap { $0.closeFuture }.wait())
  150. }
  151. // Now shutdown.
  152. try self.waitForStateChange(from: .idle, to: .shutdown) {
  153. let shutdown = manager.shutdown()
  154. self.loop.run()
  155. XCTAssertNoThrow(try shutdown.wait())
  156. }
  157. }
  158. func testIdleTimeoutWhenThereAreActiveStreams() throws {
  159. let channelPromise = self.loop.makePromise(of: Channel.self)
  160. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  161. return channelPromise.futureResult
  162. }
  163. // Start the connection.
  164. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  165. let readyChannel = manager.getChannel()
  166. self.loop.run()
  167. return readyChannel
  168. }
  169. // Setup the channel.
  170. let channel = EmbeddedChannel(
  171. handler: GRPCIdleHandler(mode: .client(manager)),
  172. loop: self.loop
  173. )
  174. channelPromise.succeed(channel)
  175. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  176. // Write a settings frame on the root stream; this'll make the channel 'ready'.
  177. try self.waitForStateChange(from: .connecting, to: .ready) {
  178. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  179. XCTAssertNoThrow(try channel.writeInbound(frame))
  180. // Wait for the channel, it _must_ be ready now.
  181. XCTAssertNoThrow(try readyChannel.wait())
  182. }
  183. // "create" a stream; the details don't matter here.
  184. let streamCreated = NIOHTTP2StreamCreatedEvent(
  185. streamID: 1,
  186. localInitialWindowSize: nil,
  187. remoteInitialWindowSize: nil
  188. )
  189. channel.pipeline.fireUserInboundEventTriggered(streamCreated)
  190. // Wait for the idle timeout: this should _not_ cause the channel to idle.
  191. self.loop.advanceTime(by: .minutes(5))
  192. // Now we're going to close the stream and wait for an idle timeout and then shutdown.
  193. self.waitForStateChange(from: .ready, to: .idle) {
  194. // Close the stream.
  195. let streamClosed = StreamClosedEvent(streamID: 1, reason: nil)
  196. channel.pipeline.fireUserInboundEventTriggered(streamClosed)
  197. // ... wait for the idle timeout,
  198. self.loop.advanceTime(by: .minutes(5))
  199. }
  200. // Now shutdown.
  201. try self.waitForStateChange(from: .idle, to: .shutdown) {
  202. let shutdown = manager.shutdown()
  203. self.loop.run()
  204. XCTAssertNoThrow(try shutdown.wait())
  205. }
  206. }
  207. func testConnectAndThenBecomeInactive() throws {
  208. let channelPromise = self.loop.makePromise(of: Channel.self)
  209. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  210. return channelPromise.futureResult
  211. }
  212. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  213. let readyChannel = manager.getChannel()
  214. self.loop.run()
  215. return readyChannel
  216. }
  217. // Setup the channel.
  218. let channel = EmbeddedChannel(
  219. handler: GRPCIdleHandler(mode: .client(manager)),
  220. loop: self.loop
  221. )
  222. channelPromise.succeed(channel)
  223. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  224. try self.waitForStateChange(from: .connecting, to: .shutdown) {
  225. // Okay: now close the channel; the `readyChannel` future has not been completed yet.
  226. XCTAssertNoThrow(try channel.close(mode: .all).wait())
  227. }
  228. // We failed to get a channel and we don't have reconnect configured: we should be shutdown and
  229. // the `readyChannel` should error.
  230. XCTAssertThrowsError(try readyChannel.wait())
  231. }
  232. func testConnectOnSecondAttempt() throws {
  233. let channelPromise: EventLoopPromise<Channel> = self.loop.makePromise()
  234. let channelFutures: [EventLoopFuture<Channel>] = [
  235. self.loop.makeFailedFuture(DoomedChannelError()),
  236. channelPromise.futureResult
  237. ]
  238. var channelFutureIterator = channelFutures.makeIterator()
  239. var configuration = self.defaultConfiguration
  240. configuration.connectionBackoff = .oneSecondFixed
  241. let manager = ConnectionManager.testingOnly(configuration: configuration, logger: self.logger) {
  242. guard let next = channelFutureIterator.next() else {
  243. XCTFail("Too many channels requested")
  244. return self.loop.makeFailedFuture(DoomedChannelError())
  245. }
  246. return next
  247. }
  248. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChanges([
  249. Change(from: .idle, to: .connecting),
  250. Change(from: .connecting, to: .transientFailure)
  251. ]) {
  252. // Get a channel.
  253. let readyChannel = manager.getChannel()
  254. self.loop.run()
  255. return readyChannel
  256. }
  257. // Get a channel from the manager: it is a future for the same channel.
  258. let anotherReadyChannel = manager.getChannel()
  259. self.loop.run()
  260. // Move time forwards by a second to start the next connection attempt.
  261. self.waitForStateChange(from: .transientFailure, to: .connecting) {
  262. self.loop.advanceTime(by: .seconds(1))
  263. }
  264. // Setup the actual channel and complete the promise.
  265. let channel = EmbeddedChannel(
  266. handler: GRPCIdleHandler(mode: .client(manager)),
  267. loop: self.loop
  268. )
  269. channelPromise.succeed(channel)
  270. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  271. // Write a SETTINGS frame on the root stream.
  272. try self.waitForStateChange(from: .connecting, to: .ready) {
  273. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  274. XCTAssertNoThrow(try channel.writeInbound(frame))
  275. }
  276. // Wait for the channel, it _must_ be ready now.
  277. XCTAssertNoThrow(try readyChannel.wait())
  278. XCTAssertNoThrow(try anotherReadyChannel.wait())
  279. // Now shutdown.
  280. try self.waitForStateChange(from: .ready, to: .shutdown) {
  281. let shutdown = manager.shutdown()
  282. self.loop.run()
  283. XCTAssertNoThrow(try shutdown.wait())
  284. }
  285. }
  286. func testShutdownWhileConnecting() throws {
  287. let channelPromise = self.loop.makePromise(of: Channel.self)
  288. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  289. return channelPromise.futureResult
  290. }
  291. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  292. let readyChannel = manager.getChannel()
  293. self.loop.run()
  294. return readyChannel
  295. }
  296. // Now shutdown.
  297. try self.waitForStateChange(from: .connecting, to: .shutdown) {
  298. let shutdown = manager.shutdown()
  299. self.loop.run()
  300. XCTAssertNoThrow(try shutdown.wait())
  301. }
  302. // The channel we were requesting should fail.
  303. XCTAssertThrowsError(try readyChannel.wait())
  304. // We still have our channel promise to fulfil: if it succeeds then it too should be closed.
  305. channelPromise.succeed(EmbeddedChannel(loop: self.loop))
  306. let channel = try channelPromise.futureResult.wait()
  307. self.loop.run()
  308. XCTAssertNoThrow(try channel.closeFuture.wait())
  309. }
  310. func testShutdownWhileTransientFailure() throws {
  311. var configuration = self.defaultConfiguration
  312. configuration.connectionBackoff = .oneSecondFixed
  313. let manager = ConnectionManager.testingOnly(configuration: configuration, logger: self.logger) {
  314. return self.loop.makeFailedFuture(DoomedChannelError())
  315. }
  316. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChanges([
  317. Change(from: .idle, to: .connecting),
  318. Change(from: .connecting, to: .transientFailure)
  319. ]) {
  320. // Get a channel.
  321. let readyChannel = manager.getChannel()
  322. self.loop.run()
  323. return readyChannel
  324. }
  325. // Now shutdown.
  326. try self.waitForStateChange(from: .transientFailure, to: .shutdown) {
  327. let shutdown = manager.shutdown()
  328. self.loop.run()
  329. XCTAssertNoThrow(try shutdown.wait())
  330. }
  331. // The channel we were requesting should fail.
  332. XCTAssertThrowsError(try readyChannel.wait())
  333. }
  334. func testShutdownWhileActive() throws {
  335. let channelPromise = self.loop.makePromise(of: Channel.self)
  336. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  337. return channelPromise.futureResult
  338. }
  339. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  340. let readyChannel = manager.getChannel()
  341. self.loop.run()
  342. return readyChannel
  343. }
  344. // Prepare the channel
  345. let channel = EmbeddedChannel(
  346. handler: GRPCIdleHandler(mode: .client(manager)),
  347. loop: self.loop
  348. )
  349. channelPromise.succeed(channel)
  350. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  351. // (No state change expected here: active is an internal state.)
  352. // Now shutdown.
  353. try self.waitForStateChange(from: .connecting, to: .shutdown) {
  354. let shutdown = manager.shutdown()
  355. self.loop.run()
  356. XCTAssertNoThrow(try shutdown.wait())
  357. }
  358. // The channel we were requesting should fail.
  359. XCTAssertThrowsError(try readyChannel.wait())
  360. }
  361. func testShutdownWhileShutdown() throws {
  362. let manager = ConnectionManager(configuration: self.defaultConfiguration, logger: self.logger)
  363. try self.waitForStateChange(from: .idle, to: .shutdown) {
  364. let firstShutdown = manager.shutdown()
  365. self.loop.run()
  366. XCTAssertNoThrow(try firstShutdown.wait())
  367. }
  368. let secondShutdown = manager.shutdown()
  369. self.loop.run()
  370. XCTAssertNoThrow(try secondShutdown.wait())
  371. }
  372. func testTransientFailureWhileActive() throws {
  373. var configuration = self.defaultConfiguration
  374. configuration.connectionBackoff = .oneSecondFixed
  375. let channelPromise: EventLoopPromise<Channel> = self.loop.makePromise()
  376. let channelFutures: [EventLoopFuture<Channel>] = [
  377. channelPromise.futureResult,
  378. self.loop.makeFailedFuture(DoomedChannelError())
  379. ]
  380. var channelFutureIterator = channelFutures.makeIterator()
  381. let manager = ConnectionManager.testingOnly(configuration: configuration, logger: self.logger) {
  382. guard let next = channelFutureIterator.next() else {
  383. XCTFail("Too many channels requested")
  384. return self.loop.makeFailedFuture(DoomedChannelError())
  385. }
  386. return next
  387. }
  388. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  389. let readyChannel = manager.getChannel()
  390. self.loop.run()
  391. return readyChannel
  392. }
  393. // Prepare the channel
  394. let firstChannel = EmbeddedChannel(
  395. handler: GRPCIdleHandler(mode: .client(manager)),
  396. loop: self.loop
  397. )
  398. channelPromise.succeed(firstChannel)
  399. XCTAssertNoThrow(try firstChannel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  400. // (No state change expected here: active is an internal state.)
  401. // Close the channel (simulate e.g. TLS handshake failed)
  402. try self.waitForStateChange(from: .connecting, to: .transientFailure) {
  403. XCTAssertNoThrow(try firstChannel.close().wait())
  404. }
  405. // Start connecting again.
  406. self.waitForStateChanges([
  407. Change(from: .transientFailure, to: .connecting),
  408. Change(from: .connecting, to: .transientFailure)
  409. ]) {
  410. self.loop.advanceTime(by: .seconds(1))
  411. }
  412. // Now shutdown
  413. try self.waitForStateChange(from: .transientFailure, to: .shutdown) {
  414. let shutdown = manager.shutdown()
  415. self.loop.run()
  416. XCTAssertNoThrow(try shutdown.wait())
  417. }
  418. // The channel never came up: it should be throw.
  419. XCTAssertThrowsError(try readyChannel.wait())
  420. }
  421. func testTransientFailureWhileReady() throws {
  422. var configuration = self.defaultConfiguration
  423. configuration.connectionBackoff = .oneSecondFixed
  424. let firstChannelPromise: EventLoopPromise<Channel> = self.loop.makePromise()
  425. let secondChannelPromise: EventLoopPromise<Channel> = self.loop.makePromise()
  426. let channelFutures: [EventLoopFuture<Channel>] = [
  427. firstChannelPromise.futureResult,
  428. secondChannelPromise.futureResult
  429. ]
  430. var channelFutureIterator = channelFutures.makeIterator()
  431. let manager = ConnectionManager.testingOnly(configuration: configuration, logger: self.logger) {
  432. guard let next = channelFutureIterator.next() else {
  433. XCTFail("Too many channels requested")
  434. return self.loop.makeFailedFuture(DoomedChannelError())
  435. }
  436. return next
  437. }
  438. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  439. let readyChannel = manager.getChannel()
  440. self.loop.run()
  441. return readyChannel
  442. }
  443. // Prepare the first channel
  444. let firstChannel = EmbeddedChannel(
  445. handler: GRPCIdleHandler(mode: .client(manager)),
  446. loop: self.loop
  447. )
  448. firstChannelPromise.succeed(firstChannel)
  449. XCTAssertNoThrow(try firstChannel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  450. // Write a SETTINGS frame on the root stream.
  451. try self.waitForStateChange(from: .connecting, to: .ready) {
  452. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  453. XCTAssertNoThrow(try firstChannel.writeInbound(frame))
  454. }
  455. // Channel should now be ready.
  456. XCTAssertNoThrow(try readyChannel.wait())
  457. // Kill the first channel.
  458. try self.waitForStateChange(from: .ready, to: .transientFailure) {
  459. XCTAssertNoThrow(try firstChannel.close().wait())
  460. }
  461. // Run to start connecting again.
  462. self.waitForStateChange(from: .transientFailure, to: .connecting) {
  463. self.loop.advanceTime(by: .seconds(1))
  464. }
  465. // Prepare the second channel
  466. let secondChannel = EmbeddedChannel(
  467. handler: GRPCIdleHandler(mode: .client(manager)),
  468. loop: self.loop
  469. )
  470. secondChannelPromise.succeed(secondChannel)
  471. XCTAssertNoThrow(try secondChannel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  472. // Write a SETTINGS frame on the root stream.
  473. try self.waitForStateChange(from: .connecting, to: .ready) {
  474. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  475. XCTAssertNoThrow(try secondChannel.writeInbound(frame))
  476. }
  477. // Now shutdown
  478. try self.waitForStateChange(from: .ready, to: .shutdown) {
  479. let shutdown = manager.shutdown()
  480. self.loop.run()
  481. XCTAssertNoThrow(try shutdown.wait())
  482. }
  483. }
  484. func testGoAwayWhenReady() throws {
  485. let channelPromise = self.loop.makePromise(of: Channel.self)
  486. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  487. return channelPromise.futureResult
  488. }
  489. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  490. let readyChannel = manager.getChannel()
  491. self.loop.run()
  492. return readyChannel
  493. }
  494. // Setup the channel.
  495. let channel = EmbeddedChannel(
  496. handler: GRPCIdleHandler(mode: .client(manager)),
  497. loop: self.loop
  498. )
  499. channelPromise.succeed(channel)
  500. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  501. try self.waitForStateChange(from: .connecting, to: .ready) {
  502. // Write a SETTINGS frame on the root stream.
  503. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  504. XCTAssertNoThrow(try channel.writeInbound(frame))
  505. }
  506. // Wait for the channel, it _must_ be ready now.
  507. XCTAssertNoThrow(try readyChannel.wait())
  508. // Send a GO_AWAY; the details don't matter. This will cause the connection to go idle and the
  509. // channel to close.
  510. try self.waitForStateChange(from: .ready, to: .idle) {
  511. let goAway = HTTP2Frame(
  512. streamID: .rootStream,
  513. payload: .goAway(lastStreamID: 1, errorCode: .noError, opaqueData: nil)
  514. )
  515. XCTAssertNoThrow(try channel.writeInbound(goAway))
  516. }
  517. self.loop.run()
  518. XCTAssertNoThrow(try channel.closeFuture.wait())
  519. // Now shutdown
  520. try self.waitForStateChange(from: .idle, to: .shutdown) {
  521. let shutdown = manager.shutdown()
  522. self.loop.run()
  523. XCTAssertNoThrow(try shutdown.wait())
  524. }
  525. }
  526. func testDoomedOptimisticChannelFromIdle() {
  527. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  528. return self.loop.makeFailedFuture(DoomedChannelError())
  529. }
  530. let candidate = manager.getOptimisticChannel()
  531. self.loop.run()
  532. XCTAssertThrowsError(try candidate.wait())
  533. }
  534. func testDoomedOptimisticChannelFromConnecting() throws {
  535. let promise = self.loop.makePromise(of: Channel.self)
  536. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  537. return promise.futureResult
  538. }
  539. self.waitForStateChange(from: .idle, to: .connecting) {
  540. // Trigger channel creation, and a connection attempt, we don't care about the channel.
  541. _ = manager.getChannel()
  542. self.loop.run()
  543. }
  544. // We're connecting: get an optimistic channel.
  545. let optimisticChannel = manager.getOptimisticChannel()
  546. self.loop.run()
  547. // Fail the promise.
  548. promise.fail(DoomedChannelError())
  549. XCTAssertThrowsError(try optimisticChannel.wait())
  550. }
  551. func testOptimisticChannelFromTransientFailure() throws {
  552. var configuration = self.defaultConfiguration
  553. configuration.connectionBackoff = ConnectionBackoff()
  554. let manager = ConnectionManager.testingOnly(configuration: configuration, logger: self.logger) {
  555. return self.loop.makeFailedFuture(DoomedChannelError())
  556. }
  557. self.waitForStateChanges([
  558. Change(from: .idle, to: .connecting),
  559. Change(from: .connecting, to: .transientFailure)
  560. ]) {
  561. // Trigger channel creation, and a connection attempt, we don't care about the channel.
  562. _ = manager.getChannel()
  563. self.loop.run()
  564. }
  565. // Now we're sitting in transient failure. Get a channel optimistically.
  566. let optimisticChannel = manager.getOptimisticChannel()
  567. self.loop.run()
  568. XCTAssertThrowsError(try optimisticChannel.wait())
  569. }
  570. func testOptimisticChannelFromShutdown() throws {
  571. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  572. return self.loop.makeFailedFuture(DoomedChannelError())
  573. }
  574. let shutdown = manager.shutdown()
  575. self.loop.run()
  576. XCTAssertNoThrow(try shutdown.wait())
  577. // Get a channel optimistically. It'll fail, obviously.
  578. let channel = manager.getOptimisticChannel()
  579. self.loop.run()
  580. XCTAssertThrowsError(try channel.wait())
  581. }
  582. func testDoubleIdle() throws {
  583. class CloseDroppingHandler: ChannelOutboundHandler {
  584. typealias OutboundIn = Any
  585. func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise<Void>?) {
  586. promise?.fail(GRPCStatus(code: .unavailable, message: "Purposefully dropping channel close"))
  587. }
  588. }
  589. let channelPromise = self.loop.makePromise(of: Channel.self)
  590. let manager = ConnectionManager.testingOnly(configuration: self.defaultConfiguration, logger: self.logger) {
  591. return channelPromise.futureResult
  592. }
  593. // Start the connection.
  594. let readyChannel: EventLoopFuture<Channel> = self.waitForStateChange(from: .idle, to: .connecting) {
  595. let readyChannel = manager.getChannel()
  596. self.loop.run()
  597. return readyChannel
  598. }
  599. // Setup the real channel and activate it.
  600. let channel = EmbeddedChannel(loop: self.loop)
  601. XCTAssertNoThrow(try channel.pipeline.addHandlers([
  602. CloseDroppingHandler(),
  603. GRPCIdleHandler(mode: .client(manager))
  604. ]).wait())
  605. channelPromise.succeed(channel)
  606. self.loop.run()
  607. XCTAssertNoThrow(try channel.connect(to: SocketAddress(unixDomainSocketPath: "/ignored")).wait())
  608. // Write a SETTINGS frame on the root stream.
  609. try self.waitForStateChange(from: .connecting, to: .ready) {
  610. let frame = HTTP2Frame(streamID: .rootStream, payload: .settings(.settings([])))
  611. XCTAssertNoThrow(try channel.writeInbound(frame))
  612. }
  613. // The channel should now be ready.
  614. XCTAssertNoThrow(try readyChannel.wait())
  615. // Send a GO_AWAY; the details don't matter. This will cause the connection to go idle and the
  616. // channel to close.
  617. try self.waitForStateChange(from: .ready, to: .idle) {
  618. let goAway = HTTP2Frame(
  619. streamID: .rootStream,
  620. payload: .goAway(lastStreamID: 1, errorCode: .noError, opaqueData: nil)
  621. )
  622. XCTAssertNoThrow(try channel.writeInbound(goAway))
  623. }
  624. // We dropped the close; now wait for the scheduled idle to fire.
  625. //
  626. // Previously doing this this would fail a precondition.
  627. self.loop.advanceTime(by: .minutes(5))
  628. }
  629. }
  630. internal struct Change: Hashable, CustomStringConvertible {
  631. var from: ConnectivityState
  632. var to: ConnectivityState
  633. var description: String {
  634. return "\(self.from) → \(self.to)"
  635. }
  636. }
  637. internal class RecordingConnectivityDelegate: ConnectivityStateDelegate {
  638. private let serialQueue = DispatchQueue(label: "io.grpc.testing")
  639. private let semaphore = DispatchSemaphore(value: 0)
  640. private var expectation: Expectation = .noExpectation
  641. private enum Expectation {
  642. /// We have no expectation of any changes. We'll just ignore any changes.
  643. case noExpectation
  644. /// We expect one change.
  645. case one((Change) -> ())
  646. /// We expect 'count' changes.
  647. case some(count: Int, recorded: [Change], ([Change]) -> ())
  648. var count: Int {
  649. switch self {
  650. case .noExpectation:
  651. return 0
  652. case .one:
  653. return 1
  654. case .some(let count, _, _):
  655. return count
  656. }
  657. }
  658. }
  659. func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) {
  660. self.serialQueue.async {
  661. switch self.expectation {
  662. case .one(let verify):
  663. // We don't care about future changes.
  664. self.expectation = .noExpectation
  665. // Verify and notify.
  666. verify(Change(from: oldState, to: newState))
  667. self.semaphore.signal()
  668. case .some(let count, var recorded, let verify):
  669. recorded.append(Change(from: oldState, to: newState))
  670. if recorded.count == count {
  671. // We don't care about future changes.
  672. self.expectation = .noExpectation
  673. // Verify and notify.
  674. verify(recorded)
  675. self.semaphore.signal()
  676. } else {
  677. // Still need more responses.
  678. self.expectation = .some(count: count, recorded: recorded, verify)
  679. }
  680. case .noExpectation:
  681. // Ignore any changes.
  682. ()
  683. }
  684. }
  685. }
  686. func expectChanges(_ count: Int, verify: @escaping ([Change]) -> ()) {
  687. self.serialQueue.async {
  688. self.expectation = .some(count: count, recorded: [], verify)
  689. }
  690. }
  691. func expectChange(verify: @escaping (Change) -> ()) {
  692. self.serialQueue.async {
  693. self.expectation = .one(verify)
  694. }
  695. }
  696. func waitForExpectedChanges(timeout: DispatchTimeInterval) {
  697. let result = self.semaphore.wait(timeout: .now() + timeout)
  698. switch result {
  699. case .success:
  700. ()
  701. case .timedOut:
  702. XCTFail("Timed out before verifying \(self.expectation.count) change(s)")
  703. }
  704. }
  705. }
  706. fileprivate extension ConnectionBackoff {
  707. static let oneSecondFixed = ConnectionBackoff(
  708. initialBackoff: 1.0,
  709. maximumBackoff: 1.0,
  710. multiplier: 1.0,
  711. jitter: 0.0
  712. )
  713. }
  714. fileprivate struct DoomedChannelError: Error {}