WebSocketTests.swift 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. //
  2. // WebSocketTests.swift
  3. // Alamofire
  4. //
  5. // Created by Jon Shier on 1/17/21.
  6. // Copyright © 2021 Alamofire. All rights reserved.
  7. //
  8. #if canImport(Darwin) && !canImport(FoundationNetworking)
  9. import Alamofire
  10. import Foundation
  11. import XCTest
  12. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  13. final class WebSocketTests: BaseTestCase {
  14. // override var skipVersion: SkipVersion { .twenty }
  15. func testThatWebSocketsCanReceiveMessageEvents() {
  16. // Given
  17. let didConnect = expectation(description: "didConnect")
  18. let didReceiveMessage = expectation(description: "didReceiveMessage")
  19. let didDisconnect = expectation(description: "didDisconnect")
  20. let didComplete = expectation(description: "didComplete")
  21. let session = stored(Session())
  22. var connectedProtocol: String?
  23. var message: URLSessionWebSocketTask.Message?
  24. var closeCode: URLSessionWebSocketTask.CloseCode?
  25. var closeReason: Data?
  26. var receivedCompletion: WebSocketRequest.Completion?
  27. // When
  28. session.websocketRequest(.websocket()).streamMessageEvents { event in
  29. switch event.kind {
  30. case let .connected(`protocol`):
  31. connectedProtocol = `protocol`
  32. didConnect.fulfill()
  33. case let .receivedMessage(receivedMessage):
  34. message = receivedMessage
  35. didReceiveMessage.fulfill()
  36. case let .disconnected(code, reason):
  37. closeCode = code
  38. closeReason = reason
  39. didDisconnect.fulfill()
  40. case let .completed(completion):
  41. receivedCompletion = completion
  42. didComplete.fulfill()
  43. }
  44. }
  45. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  46. timeout: timeout,
  47. enforceOrder: false)
  48. // Then
  49. XCTAssertNil(connectedProtocol)
  50. XCTAssertNotNil(message)
  51. XCTAssertEqual(closeCode, .normalClosure)
  52. XCTAssertNil(closeReason)
  53. XCTAssertNil(receivedCompletion?.error)
  54. }
  55. func testThatWebSocketsCanReceiveAMessage() {
  56. // Given
  57. let didReceiveMessage = expectation(description: "didReceiveMessage")
  58. let session = stored(Session())
  59. var receivedMessage: URLSessionWebSocketTask.Message?
  60. // When
  61. session.websocketRequest(.websocket()).streamMessages { message in
  62. receivedMessage = message
  63. didReceiveMessage.fulfill()
  64. }
  65. waitForExpectations(timeout: timeout)
  66. // Then
  67. XCTAssertNotNil(receivedMessage)
  68. XCTAssertNotNil(receivedMessage?.data)
  69. }
  70. func testThatWebSocketsCanReceiveADecodableMessage() {
  71. // Given
  72. let didConnect = expectation(description: "didConnect")
  73. let didReceiveMessage = expectation(description: "didReceiveMessage")
  74. let didDisconnect = expectation(description: "didDisconnect")
  75. let didComplete = expectation(description: "didComplete")
  76. let session = stored(Session())
  77. var connectedProtocol: String?
  78. var message: TestResponse?
  79. var closeCode: URLSessionWebSocketTask.CloseCode?
  80. var closeReason: Data?
  81. var receivedCompletion: WebSocketRequest.Completion?
  82. // When
  83. session.websocketRequest(.websocketCount(1)).streamDecodableEvents(TestResponse.self) { event in
  84. switch event.kind {
  85. case let .connected(`protocol`):
  86. connectedProtocol = `protocol`
  87. didConnect.fulfill()
  88. case let .receivedMessage(receivedMessage):
  89. message = receivedMessage
  90. didReceiveMessage.fulfill()
  91. case let .serializerFailed(error):
  92. XCTFail("websocket message serialization failed with error: \(error)")
  93. case let .disconnected(code, reason):
  94. closeCode = code
  95. closeReason = reason
  96. didDisconnect.fulfill()
  97. case let .completed(completion):
  98. receivedCompletion = completion
  99. didComplete.fulfill()
  100. }
  101. }
  102. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  103. timeout: timeout,
  104. enforceOrder: false)
  105. // Then
  106. XCTAssertNil(connectedProtocol)
  107. XCTAssertNotNil(message)
  108. XCTAssertEqual(closeCode, .normalClosure)
  109. XCTAssertNil(closeReason)
  110. XCTAssertNil(receivedCompletion?.error)
  111. }
  112. func testThatWebSocketsCanReceiveADecodableValue() {
  113. // Given
  114. let didReceiveValue = expectation(description: "didReceiveMessage")
  115. let session = stored(Session())
  116. var receivedValue: TestResponse?
  117. // When
  118. session.websocketRequest(.websocket()).streamDecodable(TestResponse.self) { value in
  119. receivedValue = value
  120. didReceiveValue.fulfill()
  121. }
  122. waitForExpectations(timeout: timeout)
  123. // Then
  124. XCTAssertNotNil(receivedValue)
  125. }
  126. func testThatWebSocketsCanReceiveAMessageWithAProtocol() {
  127. // Given
  128. let didConnect = expectation(description: "didConnect")
  129. let didReceiveMessage = expectation(description: "didReceiveMessage")
  130. let didDisconnect = expectation(description: "didDisconnect")
  131. let didComplete = expectation(description: "didComplete")
  132. let session = stored(Session())
  133. let `protocol` = "protocol"
  134. var connectedProtocol: String?
  135. var message: URLSessionWebSocketTask.Message?
  136. var closeCode: URLSessionWebSocketTask.CloseCode?
  137. var closeReason: Data?
  138. var receivedCompletion: WebSocketRequest.Completion?
  139. // When
  140. session.websocketRequest(.websocket(), protocol: `protocol`).streamMessageEvents { event in
  141. switch event.kind {
  142. case let .connected(`protocol`):
  143. connectedProtocol = `protocol`
  144. didConnect.fulfill()
  145. case let .receivedMessage(receivedMessage):
  146. message = receivedMessage
  147. didReceiveMessage.fulfill()
  148. case let .disconnected(code, reason):
  149. closeCode = code
  150. closeReason = reason
  151. didDisconnect.fulfill()
  152. case let .completed(completion):
  153. receivedCompletion = completion
  154. didComplete.fulfill()
  155. }
  156. }
  157. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  158. timeout: timeout,
  159. enforceOrder: true)
  160. // Then
  161. XCTAssertEqual(connectedProtocol, `protocol`)
  162. XCTAssertNotNil(message)
  163. XCTAssertEqual(closeCode, .normalClosure)
  164. XCTAssertNil(closeReason)
  165. XCTAssertNil(receivedCompletion?.error)
  166. }
  167. func testThatWebSocketsCanReceiveMultipleMessages() {
  168. // Given
  169. let count = 5
  170. let didConnect = expectation(description: "didConnect")
  171. let didReceiveMessage = expectation(description: "didReceiveMessage")
  172. didReceiveMessage.expectedFulfillmentCount = count
  173. let didDisconnect = expectation(description: "didDisconnect")
  174. let didComplete = expectation(description: "didComplete")
  175. let session = stored(Session())
  176. var connectedProtocol: String?
  177. var messages: [URLSessionWebSocketTask.Message] = []
  178. var closeCode: URLSessionWebSocketTask.CloseCode?
  179. var closeReason: Data?
  180. var receivedCompletion: WebSocketRequest.Completion?
  181. // When
  182. session.websocketRequest(.websocketCount(count)).streamMessageEvents { event in
  183. switch event.kind {
  184. case let .connected(`protocol`):
  185. connectedProtocol = `protocol`
  186. didConnect.fulfill()
  187. case let .receivedMessage(receivedMessage):
  188. messages.append(receivedMessage)
  189. didReceiveMessage.fulfill()
  190. case let .disconnected(code, reason):
  191. closeCode = code
  192. closeReason = reason
  193. didDisconnect.fulfill()
  194. case let .completed(completion):
  195. receivedCompletion = completion
  196. didComplete.fulfill()
  197. }
  198. }
  199. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  200. // Then
  201. XCTAssertNil(connectedProtocol)
  202. XCTAssertEqual(messages.count, count)
  203. XCTAssertEqual(closeCode, .normalClosure)
  204. XCTAssertNil(closeReason)
  205. XCTAssertNil(receivedCompletion?.error)
  206. }
  207. func testThatWebSocketsCanSendAndReceiveMessages() {
  208. // Given
  209. let didConnect = expectation(description: "didConnect")
  210. let didSend = expectation(description: "didSend")
  211. let didReceiveMessage = expectation(description: "didReceiveMessage")
  212. let didDisconnect = expectation(description: "didDisconnect")
  213. let didComplete = expectation(description: "didComplete")
  214. let session = stored(Session())
  215. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  216. var connectedProtocol: String?
  217. var message: URLSessionWebSocketTask.Message?
  218. var closeCode: URLSessionWebSocketTask.CloseCode?
  219. var closeReason: Data?
  220. var receivedCompletion: WebSocketRequest.Completion?
  221. // When
  222. let request = session.websocketRequest(.websocketEcho)
  223. request.streamMessageEvents { event in
  224. switch event.kind {
  225. case let .connected(`protocol`):
  226. connectedProtocol = `protocol`
  227. didConnect.fulfill()
  228. request.send(sentMessage) { _ in didSend.fulfill() }
  229. case let .receivedMessage(receivedMessage):
  230. message = receivedMessage
  231. event.close(sending: .normalClosure)
  232. didReceiveMessage.fulfill()
  233. case let .disconnected(code, reason):
  234. closeCode = code
  235. closeReason = reason
  236. didDisconnect.fulfill()
  237. case let .completed(completion):
  238. receivedCompletion = completion
  239. didComplete.fulfill()
  240. }
  241. }
  242. wait(for: [didConnect, didSend, didReceiveMessage, didDisconnect, didComplete],
  243. timeout: timeout,
  244. enforceOrder: true)
  245. // Then
  246. XCTAssertNil(connectedProtocol)
  247. XCTAssertNotNil(message)
  248. XCTAssertEqual(sentMessage, message)
  249. XCTAssertEqual(closeCode, .normalClosure)
  250. XCTAssertNil(closeReason)
  251. XCTAssertNil(receivedCompletion?.error)
  252. }
  253. func testThatWebSocketsCanBeCancelled() {
  254. // Given
  255. let didConnect = expectation(description: "didConnect")
  256. let didComplete = expectation(description: "didComplete")
  257. let session = stored(Session())
  258. var connectedProtocol: String?
  259. var receivedCompletion: WebSocketRequest.Completion?
  260. // When
  261. let request = session.websocketRequest(.websocketEcho)
  262. request.streamMessageEvents { event in
  263. switch event.kind {
  264. case let .connected(`protocol`):
  265. connectedProtocol = `protocol`
  266. didConnect.fulfill()
  267. request.cancel()
  268. case let .receivedMessage(receivedMessage):
  269. XCTFail("cancelled socket received message: \(receivedMessage)")
  270. case .disconnected:
  271. XCTFail("cancelled socket shouldn't receive disconnected event")
  272. case let .completed(completion):
  273. receivedCompletion = completion
  274. didComplete.fulfill()
  275. }
  276. }
  277. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: true)
  278. // Then
  279. XCTAssertNil(connectedProtocol)
  280. XCTAssertTrue(receivedCompletion?.error?.isExplicitlyCancelledError == true)
  281. XCTAssertTrue(request.error?.isExplicitlyCancelledError == true)
  282. }
  283. func testOnePingOnly() {
  284. // Given
  285. let didConnect = expectation(description: "didConnect")
  286. let didSend = expectation(description: "didSend")
  287. let didReceiveMessage = expectation(description: "didReceiveMessage")
  288. let didReceivePong = expectation(description: "didReceivePong")
  289. didReceivePong.expectedFulfillmentCount = 100
  290. let didDisconnect = expectation(description: "didDisconnect")
  291. let didComplete = expectation(description: "didComplete")
  292. let session = stored(Session())
  293. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  294. var connectedProtocol: String?
  295. var message: URLSessionWebSocketTask.Message?
  296. var receivedPong: WebSocketRequest.PingResponse.Pong?
  297. var closeCode: URLSessionWebSocketTask.CloseCode?
  298. var closeReason: Data?
  299. var receivedCompletion: WebSocketRequest.Completion?
  300. // When
  301. let request = session.websocketRequest(.websocketEcho)
  302. request.streamMessageEvents { event in
  303. switch event.kind {
  304. case let .connected(`protocol`):
  305. connectedProtocol = `protocol`
  306. didConnect.fulfill()
  307. request.send(sentMessage) { _ in didSend.fulfill() }
  308. case let .receivedMessage(receivedMessage):
  309. message = receivedMessage
  310. didReceiveMessage.fulfill()
  311. for count in 0..<100 {
  312. request.sendPing { response in
  313. switch response {
  314. case let .pong(pong):
  315. receivedPong = pong
  316. default:
  317. break
  318. }
  319. didReceivePong.fulfill()
  320. if count == 99 {
  321. request.close(sending: .normalClosure)
  322. }
  323. }
  324. }
  325. case let .disconnected(code, reason):
  326. closeCode = code
  327. closeReason = reason
  328. didDisconnect.fulfill()
  329. case let .completed(completion):
  330. receivedCompletion = completion
  331. didComplete.fulfill()
  332. }
  333. }
  334. wait(for: [didConnect, didSend, didReceiveMessage, didReceivePong, didDisconnect, didComplete],
  335. timeout: timeout,
  336. enforceOrder: true)
  337. // Then
  338. XCTAssertNil(connectedProtocol)
  339. XCTAssertNotNil(message)
  340. XCTAssertEqual(sentMessage, message)
  341. XCTAssertEqual(closeCode, .normalClosure)
  342. XCTAssertNil(closeReason)
  343. XCTAssertNotNil(receivedCompletion)
  344. // XCTAssertNil(receivedCompletion?.error)
  345. XCTAssertNotNil(receivedPong)
  346. }
  347. func testThatTimePingsOccur() {
  348. // Given
  349. let didConnect = expectation(description: "didConnect")
  350. let didDisconnect = expectation(description: "didDisconnect")
  351. let didComplete = expectation(description: "didComplete")
  352. let session = stored(Session())
  353. var connectedProtocol: String?
  354. var closeCode: URLSessionWebSocketTask.CloseCode?
  355. var closeReason: Data?
  356. var receivedCompletion: WebSocketRequest.Completion?
  357. // When
  358. let request = session.websocketRequest(.websocketPings(), pingInterval: 0.01)
  359. request.streamMessageEvents { event in
  360. switch event.kind {
  361. case let .connected(`protocol`):
  362. connectedProtocol = `protocol`
  363. didConnect.fulfill()
  364. case .receivedMessage:
  365. break
  366. case let .disconnected(code, reason):
  367. closeCode = code
  368. closeReason = reason
  369. didDisconnect.fulfill()
  370. case let .completed(completion):
  371. receivedCompletion = completion
  372. didComplete.fulfill()
  373. }
  374. }
  375. wait(for: [didConnect, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  376. // Then
  377. XCTAssertNil(connectedProtocol)
  378. XCTAssertEqual(closeCode, .goingAway) // Default Vapor close() code.
  379. XCTAssertNil(closeReason)
  380. XCTAssertNotNil(receivedCompletion)
  381. XCTAssertNil(receivedCompletion?.error)
  382. }
  383. func testThatWebSocketFailsWithTooSmallMaximumMessageSize() {
  384. // Given
  385. let didConnect = expectation(description: "didConnect")
  386. let didComplete = expectation(description: "didComplete")
  387. let session = stored(Session())
  388. var connectedProtocol: String?
  389. var receivedCompletion: WebSocketRequest.Completion?
  390. // When
  391. session.websocketRequest(.websocket(), maximumMessageSize: 1).streamMessageEvents { event in
  392. switch event.kind {
  393. case let .connected(`protocol`):
  394. connectedProtocol = `protocol`
  395. didConnect.fulfill()
  396. case .receivedMessage, .disconnected:
  397. break
  398. case let .completed(completion):
  399. receivedCompletion = completion
  400. didComplete.fulfill()
  401. }
  402. }
  403. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: false)
  404. // Then
  405. XCTAssertNil(connectedProtocol)
  406. XCTAssertNotNil(receivedCompletion?.error)
  407. }
  408. func testThatWebSocketsFinishAfterNonNormalResponseCode() {
  409. // Given
  410. let didConnect = expectation(description: "didConnect")
  411. let didReceiveMessage = expectation(description: "didReceiveMessage")
  412. let didDisconnect = expectation(description: "didDisconnect")
  413. let didComplete = expectation(description: "didComplete")
  414. let session = stored(Session())
  415. var connectedProtocol: String?
  416. var message: URLSessionWebSocketTask.Message?
  417. var closeCode: URLSessionWebSocketTask.CloseCode?
  418. var closeReason: Data?
  419. var receivedCompletion: WebSocketRequest.Completion?
  420. // When
  421. session.websocketRequest(.websocket(closeCode: .goingAway)).streamMessageEvents { event in
  422. switch event.kind {
  423. case let .connected(`protocol`):
  424. connectedProtocol = `protocol`
  425. didConnect.fulfill()
  426. case let .receivedMessage(receivedMessage):
  427. message = receivedMessage
  428. didReceiveMessage.fulfill()
  429. case let .disconnected(code, reason):
  430. closeCode = code
  431. closeReason = reason
  432. didDisconnect.fulfill()
  433. case let .completed(completion):
  434. receivedCompletion = completion
  435. didComplete.fulfill()
  436. }
  437. }
  438. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  439. timeout: timeout,
  440. enforceOrder: false)
  441. // Then
  442. XCTAssertNil(connectedProtocol)
  443. XCTAssertNotNil(message)
  444. XCTAssertEqual(closeCode, .goingAway)
  445. XCTAssertNil(closeReason)
  446. XCTAssertNil(receivedCompletion?.error)
  447. }
  448. }
  449. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  450. extension URLSessionWebSocketTask.Message: Equatable {
  451. public static func ==(lhs: URLSessionWebSocketTask.Message, rhs: URLSessionWebSocketTask.Message) -> Bool {
  452. switch (lhs, rhs) {
  453. case let (.string(left), .string(right)):
  454. return left == right
  455. case let (.data(left), .data(right)):
  456. return left == right
  457. default:
  458. return false
  459. }
  460. }
  461. var string: String? {
  462. guard case let .string(string) = self else { return nil }
  463. return string
  464. }
  465. var data: Data? {
  466. guard case let .data(data) = self else { return nil }
  467. return data
  468. }
  469. }
  470. #endif