WebSocketTests.swift 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  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 testThatWebSocketsCanReceiveAMessage() {
  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()).responseMessage { 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 testThatWebSocketsCanReceiveAMessageWithAProtocol() {
  56. // Given
  57. let didConnect = expectation(description: "didConnect")
  58. let didReceiveMessage = expectation(description: "didReceiveMessage")
  59. let didDisconnect = expectation(description: "didDisconnect")
  60. let didComplete = expectation(description: "didComplete")
  61. let session = stored(Session())
  62. let `protocol` = "protocol"
  63. var connectedProtocol: String?
  64. var message: URLSessionWebSocketTask.Message?
  65. var closeCode: URLSessionWebSocketTask.CloseCode?
  66. var closeReason: Data?
  67. var receivedCompletion: WebSocketRequest.Completion?
  68. // When
  69. session.websocketRequest(.websocket(), protocol: `protocol`).responseMessage { event in
  70. switch event.kind {
  71. case let .connected(`protocol`):
  72. connectedProtocol = `protocol`
  73. didConnect.fulfill()
  74. case let .receivedMessage(receivedMessage):
  75. message = receivedMessage
  76. didReceiveMessage.fulfill()
  77. case let .disconnected(code, reason):
  78. closeCode = code
  79. closeReason = reason
  80. didDisconnect.fulfill()
  81. case let .completed(completion):
  82. receivedCompletion = completion
  83. didComplete.fulfill()
  84. }
  85. }
  86. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  87. timeout: timeout,
  88. enforceOrder: true)
  89. // Then
  90. XCTAssertEqual(connectedProtocol, `protocol`)
  91. XCTAssertNotNil(message)
  92. XCTAssertEqual(closeCode, .normalClosure)
  93. XCTAssertNil(closeReason)
  94. XCTAssertNil(receivedCompletion?.error)
  95. }
  96. func testThatWebSocketsCanReceiveMultipleMessages() {
  97. // Given
  98. let count = 5
  99. let didConnect = expectation(description: "didConnect")
  100. let didReceiveMessage = expectation(description: "didReceiveMessage")
  101. didReceiveMessage.expectedFulfillmentCount = count
  102. let didDisconnect = expectation(description: "didDisconnect")
  103. let didComplete = expectation(description: "didComplete")
  104. let session = stored(Session())
  105. var connectedProtocol: String?
  106. var messages: [URLSessionWebSocketTask.Message] = []
  107. var closeCode: URLSessionWebSocketTask.CloseCode?
  108. var closeReason: Data?
  109. var receivedCompletion: WebSocketRequest.Completion?
  110. // When
  111. session.websocketRequest(.websocketCount(count)).responseMessage { event in
  112. switch event.kind {
  113. case let .connected(`protocol`):
  114. connectedProtocol = `protocol`
  115. didConnect.fulfill()
  116. case let .receivedMessage(receivedMessage):
  117. messages.append(receivedMessage)
  118. didReceiveMessage.fulfill()
  119. case let .disconnected(code, reason):
  120. closeCode = code
  121. closeReason = reason
  122. didDisconnect.fulfill()
  123. case let .completed(completion):
  124. receivedCompletion = completion
  125. didComplete.fulfill()
  126. }
  127. }
  128. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  129. // Then
  130. XCTAssertNil(connectedProtocol)
  131. XCTAssertEqual(messages.count, count)
  132. XCTAssertEqual(closeCode, .normalClosure)
  133. XCTAssertNil(closeReason)
  134. XCTAssertNil(receivedCompletion?.error)
  135. }
  136. func testThatWebSocketsCanSendAndReceiveMessages() {
  137. // Given
  138. let didConnect = expectation(description: "didConnect")
  139. let didSend = expectation(description: "didSend")
  140. let didReceiveMessage = expectation(description: "didReceiveMessage")
  141. let didDisconnect = expectation(description: "didDisconnect")
  142. let didComplete = expectation(description: "didComplete")
  143. let session = stored(Session())
  144. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  145. var connectedProtocol: String?
  146. var message: URLSessionWebSocketTask.Message?
  147. var closeCode: URLSessionWebSocketTask.CloseCode?
  148. var closeReason: Data?
  149. var receivedCompletion: WebSocketRequest.Completion?
  150. // When
  151. let request = session.websocketRequest(.websocketEcho)
  152. request.responseMessage { event in
  153. switch event.kind {
  154. case let .connected(`protocol`):
  155. connectedProtocol = `protocol`
  156. didConnect.fulfill()
  157. request.send(sentMessage) { _ in didSend.fulfill() }
  158. case let .receivedMessage(receivedMessage):
  159. message = receivedMessage
  160. event.cancel(with: .normalClosure, reason: nil)
  161. didReceiveMessage.fulfill()
  162. case let .disconnected(code, reason):
  163. closeCode = code
  164. closeReason = reason
  165. didDisconnect.fulfill()
  166. case let .completed(completion):
  167. receivedCompletion = completion
  168. didComplete.fulfill()
  169. }
  170. }
  171. wait(for: [didConnect, didSend, didReceiveMessage, didDisconnect, didComplete],
  172. timeout: timeout,
  173. enforceOrder: true)
  174. // Then
  175. XCTAssertNil(connectedProtocol)
  176. XCTAssertNotNil(message)
  177. XCTAssertEqual(sentMessage, message)
  178. XCTAssertEqual(closeCode, .normalClosure)
  179. XCTAssertNil(closeReason)
  180. // XCTAssertNil(receivedCompletion?.error)
  181. }
  182. func testOnePingOnly() {
  183. // Given
  184. let didConnect = expectation(description: "didConnect")
  185. let didSend = expectation(description: "didSend")
  186. let didReceiveMessage = expectation(description: "didReceiveMessage")
  187. let didReceivePong = expectation(description: "didReceivePong")
  188. didReceivePong.expectedFulfillmentCount = 100
  189. let didDisconnect = expectation(description: "didDisconnect")
  190. let didComplete = expectation(description: "didComplete")
  191. let session = stored(Session())
  192. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  193. var connectedProtocol: String?
  194. var message: URLSessionWebSocketTask.Message?
  195. var receivedPong: WebSocketRequest.PingResponse.Pong?
  196. var closeCode: URLSessionWebSocketTask.CloseCode?
  197. var closeReason: Data?
  198. var receivedCompletion: WebSocketRequest.Completion?
  199. // When
  200. let request = session.websocketRequest(.websocketEcho)
  201. request.responseMessage { event in
  202. switch event.kind {
  203. case let .connected(`protocol`):
  204. connectedProtocol = `protocol`
  205. didConnect.fulfill()
  206. request.send(sentMessage) { _ in didSend.fulfill() }
  207. case let .receivedMessage(receivedMessage):
  208. message = receivedMessage
  209. didReceiveMessage.fulfill()
  210. for count in 0..<100 {
  211. request.sendPing { response in
  212. switch response {
  213. case let .pong(pong):
  214. receivedPong = pong
  215. default:
  216. break
  217. }
  218. didReceivePong.fulfill()
  219. if count == 99 {
  220. request.cancel(with: .normalClosure, reason: nil)
  221. }
  222. }
  223. }
  224. case let .disconnected(code, reason):
  225. closeCode = code
  226. closeReason = reason
  227. didDisconnect.fulfill()
  228. case let .completed(completion):
  229. receivedCompletion = completion
  230. didComplete.fulfill()
  231. }
  232. }
  233. wait(for: [didConnect, didSend, didReceiveMessage, didReceivePong, didDisconnect, didComplete],
  234. timeout: timeout,
  235. enforceOrder: true)
  236. // Then
  237. XCTAssertNil(connectedProtocol)
  238. XCTAssertNotNil(message)
  239. XCTAssertEqual(sentMessage, message)
  240. XCTAssertEqual(closeCode, .normalClosure)
  241. XCTAssertNil(closeReason)
  242. XCTAssertNotNil(receivedCompletion)
  243. // XCTAssertNil(receivedCompletion?.error)
  244. XCTAssertNotNil(receivedPong)
  245. }
  246. func testThatTimePingsOccur() {
  247. // Given
  248. let didConnect = expectation(description: "didConnect")
  249. let didDisconnect = expectation(description: "didDisconnect")
  250. let didComplete = expectation(description: "didComplete")
  251. let session = stored(Session())
  252. var connectedProtocol: String?
  253. var closeCode: URLSessionWebSocketTask.CloseCode?
  254. var closeReason: Data?
  255. var receivedCompletion: WebSocketRequest.Completion?
  256. // When
  257. let request = session.websocketRequest(.websocketPings(), pingInterval: 0.01)
  258. request.responseMessage { event in
  259. switch event.kind {
  260. case let .connected(`protocol`):
  261. connectedProtocol = `protocol`
  262. didConnect.fulfill()
  263. case .receivedMessage:
  264. break
  265. case let .disconnected(code, reason):
  266. closeCode = code
  267. closeReason = reason
  268. didDisconnect.fulfill()
  269. case let .completed(completion):
  270. receivedCompletion = completion
  271. didComplete.fulfill()
  272. }
  273. }
  274. wait(for: [didConnect, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  275. // Then
  276. XCTAssertNil(connectedProtocol)
  277. XCTAssertEqual(closeCode, .goingAway) // Default Vapor close() code.
  278. XCTAssertNil(closeReason)
  279. XCTAssertNotNil(receivedCompletion)
  280. XCTAssertNil(receivedCompletion?.error)
  281. }
  282. func testThatWebSocketFailsWithTooSmallMaximumMessageSize() {
  283. // Given
  284. let didConnect = expectation(description: "didConnect")
  285. let didComplete = expectation(description: "didComplete")
  286. let session = stored(Session())
  287. var connectedProtocol: String?
  288. var receivedCompletion: WebSocketRequest.Completion?
  289. // When
  290. session.websocketRequest(.websocket(), maximumMessageSize: 1).responseMessage { event in
  291. switch event.kind {
  292. case let .connected(`protocol`):
  293. connectedProtocol = `protocol`
  294. didConnect.fulfill()
  295. case .receivedMessage, .disconnected:
  296. break
  297. case let .completed(completion):
  298. receivedCompletion = completion
  299. didComplete.fulfill()
  300. }
  301. }
  302. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: false)
  303. // Then
  304. XCTAssertNil(connectedProtocol)
  305. XCTAssertNotNil(receivedCompletion?.error)
  306. }
  307. func testThatWebSocketsFinishAfterNonNormalResponseCode() {
  308. // Given
  309. let didConnect = expectation(description: "didConnect")
  310. let didReceiveMessage = expectation(description: "didReceiveMessage")
  311. let didDisconnect = expectation(description: "didDisconnect")
  312. let didComplete = expectation(description: "didComplete")
  313. let session = stored(Session())
  314. var connectedProtocol: String?
  315. var message: URLSessionWebSocketTask.Message?
  316. var closeCode: URLSessionWebSocketTask.CloseCode?
  317. var closeReason: Data?
  318. var receivedCompletion: WebSocketRequest.Completion?
  319. // When
  320. session.websocketRequest(.websocket(closeCode: .goingAway)).responseMessage { event in
  321. switch event.kind {
  322. case let .connected(`protocol`):
  323. connectedProtocol = `protocol`
  324. didConnect.fulfill()
  325. case let .receivedMessage(receivedMessage):
  326. message = receivedMessage
  327. didReceiveMessage.fulfill()
  328. case let .disconnected(code, reason):
  329. closeCode = code
  330. closeReason = reason
  331. didDisconnect.fulfill()
  332. case let .completed(completion):
  333. receivedCompletion = completion
  334. didComplete.fulfill()
  335. }
  336. }
  337. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  338. timeout: timeout,
  339. enforceOrder: false)
  340. // Then
  341. XCTAssertNil(connectedProtocol)
  342. XCTAssertNotNil(message)
  343. XCTAssertEqual(closeCode, .goingAway)
  344. XCTAssertNil(closeReason)
  345. XCTAssertNil(receivedCompletion?.error)
  346. }
  347. }
  348. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  349. extension URLSessionWebSocketTask.Message: Equatable {
  350. public static func ==(lhs: URLSessionWebSocketTask.Message, rhs: URLSessionWebSocketTask.Message) -> Bool {
  351. switch (lhs, rhs) {
  352. case let (.string(left), .string(right)):
  353. return left == right
  354. case let (.data(left), .data(right)):
  355. return left == right
  356. default:
  357. return false
  358. }
  359. }
  360. var string: String? {
  361. guard case let .string(string) = self else { return nil }
  362. return string
  363. }
  364. var data: Data? {
  365. guard case let .data(data) = self else { return nil }
  366. return data
  367. }
  368. }
  369. #endif