WebSocketTests.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  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 testMany() {
  16. // for _ in 0..<5000 {
  17. // testThatWebSocketsCanReceiveMessageEvents()
  18. // }
  19. // }
  20. func testThatWebSocketsCanReceiveMessageEvents() {
  21. // Given
  22. let didConnect = expectation(description: "didConnect")
  23. let didReceiveMessage = expectation(description: "didReceiveMessage")
  24. let didDisconnect = expectation(description: "didDisconnect")
  25. let didComplete = expectation(description: "didComplete")
  26. let session = stored(Session())
  27. var connectedProtocol: String?
  28. var message: URLSessionWebSocketTask.Message?
  29. var closeCode: URLSessionWebSocketTask.CloseCode?
  30. var closeReason: Data?
  31. var receivedCompletion: WebSocketRequest.Completion?
  32. // When
  33. session.websocketRequest(.websocket()).streamMessageEvents { event in
  34. switch event.kind {
  35. case let .connected(`protocol`):
  36. connectedProtocol = `protocol`
  37. didConnect.fulfill()
  38. case let .receivedMessage(receivedMessage):
  39. message = receivedMessage
  40. didReceiveMessage.fulfill()
  41. case let .disconnected(code, reason):
  42. closeCode = code
  43. closeReason = reason
  44. didDisconnect.fulfill()
  45. case let .completed(completion):
  46. receivedCompletion = completion
  47. didComplete.fulfill()
  48. }
  49. }
  50. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  51. timeout: timeout,
  52. enforceOrder: true)
  53. // Then
  54. XCTAssertNil(connectedProtocol)
  55. XCTAssertNotNil(message)
  56. XCTAssertEqual(closeCode, .normalClosure)
  57. XCTAssertNil(closeReason)
  58. XCTAssertNil(receivedCompletion?.error)
  59. }
  60. func testThatWebSocketsCanReceiveAMessage() {
  61. // Given
  62. let didReceiveMessage = expectation(description: "didReceiveMessage")
  63. let didComplete = expectation(description: "didComplete")
  64. let session = stored(Session())
  65. var receivedMessage: URLSessionWebSocketTask.Message?
  66. // When
  67. session.websocketRequest(.websocket()).streamMessages { message in
  68. receivedMessage = message
  69. didReceiveMessage.fulfill()
  70. }
  71. .onCompletion {
  72. didComplete.fulfill()
  73. }
  74. waitForExpectations(timeout: timeout)
  75. // Then
  76. XCTAssertNotNil(receivedMessage)
  77. XCTAssertNotNil(receivedMessage?.data)
  78. }
  79. func testThatWebSocketsCanReceiveADecodableMessage() {
  80. // Given
  81. let didConnect = expectation(description: "didConnect")
  82. let didReceiveMessage = expectation(description: "didReceiveMessage")
  83. let didDisconnect = expectation(description: "didDisconnect")
  84. let didComplete = expectation(description: "didComplete")
  85. let session = stored(Session())
  86. var connectedProtocol: String?
  87. var message: TestResponse?
  88. var closeCode: URLSessionWebSocketTask.CloseCode?
  89. var closeReason: Data?
  90. var receivedCompletion: WebSocketRequest.Completion?
  91. // When
  92. session.websocketRequest(.websocketCount(1)).streamDecodableEvents(TestResponse.self) { event in
  93. switch event.kind {
  94. case let .connected(`protocol`):
  95. connectedProtocol = `protocol`
  96. didConnect.fulfill()
  97. case let .receivedMessage(receivedMessage):
  98. message = receivedMessage
  99. didReceiveMessage.fulfill()
  100. case let .serializerFailed(error):
  101. XCTFail("websocket message serialization failed with error: \(error)")
  102. case let .disconnected(code, reason):
  103. closeCode = code
  104. closeReason = reason
  105. didDisconnect.fulfill()
  106. case let .completed(completion):
  107. receivedCompletion = completion
  108. didComplete.fulfill()
  109. }
  110. }
  111. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  112. timeout: timeout,
  113. enforceOrder: true)
  114. // Then
  115. XCTAssertNil(connectedProtocol)
  116. XCTAssertNotNil(message)
  117. XCTAssertEqual(closeCode, .normalClosure)
  118. XCTAssertNil(closeReason)
  119. XCTAssertNil(receivedCompletion?.error)
  120. }
  121. func testThatWebSocketsCanReceiveADecodableValue() {
  122. // Given
  123. let didReceiveValue = expectation(description: "didReceiveMessage")
  124. let didComplete = expectation(description: "didComplete")
  125. let session = stored(Session())
  126. var receivedValue: TestResponse?
  127. // When
  128. session.websocketRequest(.websocket()).streamDecodable(TestResponse.self) { value in
  129. receivedValue = value
  130. didReceiveValue.fulfill()
  131. }
  132. .onCompletion {
  133. didComplete.fulfill()
  134. }
  135. waitForExpectations(timeout: timeout)
  136. // Then
  137. XCTAssertNotNil(receivedValue)
  138. }
  139. func testThatWebSocketsCanReceiveAMessageWithAProtocol() {
  140. // Given
  141. let didConnect = expectation(description: "didConnect")
  142. let didReceiveMessage = expectation(description: "didReceiveMessage")
  143. let didDisconnect = expectation(description: "didDisconnect")
  144. let didComplete = expectation(description: "didComplete")
  145. let session = stored(Session())
  146. let `protocol` = "protocol"
  147. var connectedProtocol: String?
  148. var message: URLSessionWebSocketTask.Message?
  149. var closeCode: URLSessionWebSocketTask.CloseCode?
  150. var closeReason: Data?
  151. var receivedCompletion: WebSocketRequest.Completion?
  152. // When
  153. session.websocketRequest(.websocket(), protocol: `protocol`).streamMessageEvents { event in
  154. switch event.kind {
  155. case let .connected(`protocol`):
  156. connectedProtocol = `protocol`
  157. didConnect.fulfill()
  158. case let .receivedMessage(receivedMessage):
  159. message = receivedMessage
  160. didReceiveMessage.fulfill()
  161. case let .disconnected(code, reason):
  162. closeCode = code
  163. closeReason = reason
  164. didDisconnect.fulfill()
  165. case let .completed(completion):
  166. receivedCompletion = completion
  167. didComplete.fulfill()
  168. }
  169. }
  170. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  171. timeout: timeout,
  172. enforceOrder: true)
  173. // Then
  174. XCTAssertEqual(connectedProtocol, `protocol`)
  175. XCTAssertNotNil(message)
  176. XCTAssertEqual(closeCode, .normalClosure)
  177. XCTAssertNil(closeReason)
  178. XCTAssertNil(receivedCompletion?.error)
  179. }
  180. func testThatWebSocketsCanReceiveMultipleMessages() {
  181. // Given
  182. let count = 5
  183. let didConnect = expectation(description: "didConnect")
  184. let didReceiveMessage = expectation(description: "didReceiveMessage")
  185. didReceiveMessage.expectedFulfillmentCount = count
  186. let didDisconnect = expectation(description: "didDisconnect")
  187. let didComplete = expectation(description: "didComplete")
  188. let session = stored(Session())
  189. var connectedProtocol: String?
  190. var messages: [URLSessionWebSocketTask.Message] = []
  191. var closeCode: URLSessionWebSocketTask.CloseCode?
  192. var closeReason: Data?
  193. var receivedCompletion: WebSocketRequest.Completion?
  194. // When
  195. session.websocketRequest(.websocketCount(count)).streamMessageEvents { event in
  196. switch event.kind {
  197. case let .connected(`protocol`):
  198. connectedProtocol = `protocol`
  199. didConnect.fulfill()
  200. case let .receivedMessage(receivedMessage):
  201. messages.append(receivedMessage)
  202. didReceiveMessage.fulfill()
  203. case let .disconnected(code, reason):
  204. closeCode = code
  205. closeReason = reason
  206. didDisconnect.fulfill()
  207. case let .completed(completion):
  208. receivedCompletion = completion
  209. didComplete.fulfill()
  210. }
  211. }
  212. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  213. // Then
  214. XCTAssertNil(connectedProtocol)
  215. XCTAssertEqual(messages.count, count)
  216. XCTAssertEqual(closeCode, .normalClosure)
  217. XCTAssertNil(closeReason)
  218. XCTAssertNil(receivedCompletion?.error)
  219. }
  220. func testThatWebSocketsCanSendAndReceiveMessages() {
  221. // Given
  222. let didConnect = expectation(description: "didConnect")
  223. let didSend = expectation(description: "didSend")
  224. let didReceiveMessage = expectation(description: "didReceiveMessage")
  225. let didDisconnect = expectation(description: "didDisconnect")
  226. let didComplete = expectation(description: "didComplete")
  227. let session = stored(Session())
  228. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  229. var connectedProtocol: String?
  230. var message: URLSessionWebSocketTask.Message?
  231. var closeCode: URLSessionWebSocketTask.CloseCode?
  232. var closeReason: Data?
  233. var receivedCompletion: WebSocketRequest.Completion?
  234. // When
  235. let request = session.websocketRequest(.websocketEcho)
  236. request.streamMessageEvents { event in
  237. switch event.kind {
  238. case let .connected(`protocol`):
  239. connectedProtocol = `protocol`
  240. didConnect.fulfill()
  241. request.send(sentMessage) { _ in didSend.fulfill() }
  242. case let .receivedMessage(receivedMessage):
  243. message = receivedMessage
  244. event.close(sending: .normalClosure)
  245. didReceiveMessage.fulfill()
  246. case let .disconnected(code, reason):
  247. closeCode = code
  248. closeReason = reason
  249. didDisconnect.fulfill()
  250. case let .completed(completion):
  251. receivedCompletion = completion
  252. didComplete.fulfill()
  253. }
  254. }
  255. wait(for: [didConnect, didSend, didReceiveMessage, didDisconnect, didComplete],
  256. timeout: timeout,
  257. enforceOrder: true)
  258. // Then
  259. XCTAssertNil(connectedProtocol)
  260. XCTAssertNotNil(message)
  261. XCTAssertEqual(sentMessage, message)
  262. XCTAssertEqual(closeCode, .normalClosure)
  263. XCTAssertNil(closeReason)
  264. XCTAssertNil(receivedCompletion?.error)
  265. }
  266. func testThatWebSocketsCanBeCancelled() {
  267. // Given
  268. let didConnect = expectation(description: "didConnect")
  269. let didComplete = expectation(description: "didComplete")
  270. let session = stored(Session())
  271. var connectedProtocol: String?
  272. var receivedCompletion: WebSocketRequest.Completion?
  273. // When
  274. let request = session.websocketRequest(.websocketEcho)
  275. request.streamMessageEvents { event in
  276. switch event.kind {
  277. case let .connected(`protocol`):
  278. connectedProtocol = `protocol`
  279. didConnect.fulfill()
  280. request.cancel()
  281. case let .receivedMessage(receivedMessage):
  282. XCTFail("cancelled socket received message: \(receivedMessage)")
  283. case .disconnected:
  284. XCTFail("cancelled socket shouldn't receive disconnected event")
  285. case let .completed(completion):
  286. receivedCompletion = completion
  287. didComplete.fulfill()
  288. }
  289. }
  290. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: true)
  291. // Then
  292. XCTAssertNil(connectedProtocol)
  293. XCTAssertTrue(receivedCompletion?.error?.isExplicitlyCancelledError == true)
  294. XCTAssertTrue(request.error?.isExplicitlyCancelledError == true)
  295. }
  296. func testOnePingOnly() {
  297. // Given
  298. let didConnect = expectation(description: "didConnect")
  299. let didSend = expectation(description: "didSend")
  300. let didReceiveMessage = expectation(description: "didReceiveMessage")
  301. let didReceivePong = expectation(description: "didReceivePong")
  302. didReceivePong.expectedFulfillmentCount = 100
  303. let didDisconnect = expectation(description: "didDisconnect")
  304. let didComplete = expectation(description: "didComplete")
  305. let session = stored(Session())
  306. let sentMessage = URLSessionWebSocketTask.Message.string("Echo")
  307. var connectedProtocol: String?
  308. var message: URLSessionWebSocketTask.Message?
  309. var receivedPong: WebSocketRequest.PingResponse.Pong?
  310. var closeCode: URLSessionWebSocketTask.CloseCode?
  311. var closeReason: Data?
  312. var receivedCompletion: WebSocketRequest.Completion?
  313. // When
  314. let request = session.websocketRequest(.websocketEcho)
  315. request.streamMessageEvents { event in
  316. switch event.kind {
  317. case let .connected(`protocol`):
  318. connectedProtocol = `protocol`
  319. didConnect.fulfill()
  320. request.send(sentMessage) { _ in didSend.fulfill() }
  321. case let .receivedMessage(receivedMessage):
  322. message = receivedMessage
  323. didReceiveMessage.fulfill()
  324. for count in 0..<100 {
  325. request.sendPing { response in
  326. switch response {
  327. case let .pong(pong):
  328. receivedPong = pong
  329. default:
  330. break
  331. }
  332. didReceivePong.fulfill()
  333. if count == 99 {
  334. request.close(sending: .normalClosure)
  335. }
  336. }
  337. }
  338. case let .disconnected(code, reason):
  339. closeCode = code
  340. closeReason = reason
  341. didDisconnect.fulfill()
  342. case let .completed(completion):
  343. receivedCompletion = completion
  344. didComplete.fulfill()
  345. }
  346. }
  347. wait(for: [didConnect, didSend, didReceiveMessage, didReceivePong, didDisconnect, didComplete],
  348. timeout: timeout,
  349. enforceOrder: true)
  350. // Then
  351. XCTAssertNil(connectedProtocol)
  352. XCTAssertNotNil(message)
  353. XCTAssertEqual(sentMessage, message)
  354. XCTAssertEqual(closeCode, .normalClosure)
  355. XCTAssertNil(closeReason)
  356. XCTAssertNotNil(receivedCompletion)
  357. XCTAssertNil(receivedCompletion?.error)
  358. XCTAssertNotNil(receivedPong)
  359. }
  360. func testThatTimePingsOccur() {
  361. // Given
  362. let didConnect = expectation(description: "didConnect")
  363. let didDisconnect = expectation(description: "didDisconnect")
  364. let didComplete = expectation(description: "didComplete")
  365. let session = stored(Session())
  366. var connectedProtocol: String?
  367. var closeCode: URLSessionWebSocketTask.CloseCode?
  368. var closeReason: Data?
  369. var receivedCompletion: WebSocketRequest.Completion?
  370. // When
  371. let request = session.websocketRequest(.websocketPings(), pingInterval: 0.01)
  372. request.streamMessageEvents { event in
  373. switch event.kind {
  374. case let .connected(`protocol`):
  375. connectedProtocol = `protocol`
  376. didConnect.fulfill()
  377. case .receivedMessage:
  378. break
  379. case let .disconnected(code, reason):
  380. closeCode = code
  381. closeReason = reason
  382. didDisconnect.fulfill()
  383. case let .completed(completion):
  384. receivedCompletion = completion
  385. didComplete.fulfill()
  386. }
  387. }
  388. wait(for: [didConnect, didDisconnect, didComplete], timeout: timeout, enforceOrder: true)
  389. // Then
  390. XCTAssertNil(connectedProtocol)
  391. XCTAssertEqual(closeCode, .goingAway) // Default Vapor close() code.
  392. XCTAssertNil(closeReason)
  393. XCTAssertNotNil(receivedCompletion)
  394. XCTAssertNil(receivedCompletion?.error)
  395. }
  396. func testThatWebSocketFailsWithTooSmallMaximumMessageSize() {
  397. // Given
  398. let didConnect = expectation(description: "didConnect")
  399. let didComplete = expectation(description: "didComplete")
  400. let session = stored(Session())
  401. var connectedProtocol: String?
  402. var receivedCompletion: WebSocketRequest.Completion?
  403. // When
  404. session.websocketRequest(.websocket(), maximumMessageSize: 1).streamMessageEvents { event in
  405. switch event.kind {
  406. case let .connected(`protocol`):
  407. connectedProtocol = `protocol`
  408. didConnect.fulfill()
  409. case .receivedMessage, .disconnected:
  410. break
  411. case let .completed(completion):
  412. receivedCompletion = completion
  413. didComplete.fulfill()
  414. }
  415. }
  416. wait(for: [didConnect, didComplete], timeout: timeout, enforceOrder: true)
  417. // Then
  418. XCTAssertNil(connectedProtocol)
  419. XCTAssertNotNil(receivedCompletion?.error)
  420. }
  421. func testThatWebSocketsFinishAfterNonNormalResponseCode() {
  422. // Given
  423. let didConnect = expectation(description: "didConnect")
  424. let didReceiveMessage = expectation(description: "didReceiveMessage")
  425. let didDisconnect = expectation(description: "didDisconnect")
  426. let didComplete = expectation(description: "didComplete")
  427. let session = stored(Session())
  428. var connectedProtocol: String?
  429. var message: URLSessionWebSocketTask.Message?
  430. var closeCode: URLSessionWebSocketTask.CloseCode?
  431. var closeReason: Data?
  432. var receivedCompletion: WebSocketRequest.Completion?
  433. // When
  434. session.websocketRequest(.websocket(closeCode: .goingAway)).streamMessageEvents { event in
  435. switch event.kind {
  436. case let .connected(`protocol`):
  437. connectedProtocol = `protocol`
  438. didConnect.fulfill()
  439. case let .receivedMessage(receivedMessage):
  440. message = receivedMessage
  441. didReceiveMessage.fulfill()
  442. case let .disconnected(code, reason):
  443. closeCode = code
  444. closeReason = reason
  445. didDisconnect.fulfill()
  446. case let .completed(completion):
  447. receivedCompletion = completion
  448. didComplete.fulfill()
  449. }
  450. }
  451. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  452. timeout: timeout,
  453. enforceOrder: true)
  454. // Then
  455. XCTAssertNil(connectedProtocol)
  456. XCTAssertNotNil(message)
  457. XCTAssertEqual(closeCode, .goingAway)
  458. XCTAssertNil(closeReason)
  459. XCTAssertNil(receivedCompletion?.error)
  460. }
  461. func testThatWebSocketsCanHaveMultipleHandlers() {
  462. // Given
  463. let didConnect = expectation(description: "didConnect")
  464. didConnect.expectedFulfillmentCount = 2
  465. let didReceiveMessage = expectation(description: "didReceiveMessage")
  466. didReceiveMessage.expectedFulfillmentCount = 2
  467. let didDisconnect = expectation(description: "didDisconnect")
  468. didDisconnect.expectedFulfillmentCount = 2
  469. let didComplete = expectation(description: "didComplete")
  470. didComplete.expectedFulfillmentCount = 2
  471. let session = stored(Session())
  472. var firstConnectedProtocol: String?
  473. var firstMessage: URLSessionWebSocketTask.Message?
  474. var firstCloseCode: URLSessionWebSocketTask.CloseCode?
  475. var firstCloseReason: Data?
  476. var firstReceivedCompletion: WebSocketRequest.Completion?
  477. var secondConnectedProtocol: String?
  478. var secondMessage: URLSessionWebSocketTask.Message?
  479. var secondCloseCode: URLSessionWebSocketTask.CloseCode?
  480. var secondCloseReason: Data?
  481. var secondReceivedCompletion: WebSocketRequest.Completion?
  482. // When
  483. session.websocketRequest(.websocket(closeCode: .goingAway)).streamMessageEvents { event in
  484. switch event.kind {
  485. case let .connected(`protocol`):
  486. firstConnectedProtocol = `protocol`
  487. didConnect.fulfill()
  488. case let .receivedMessage(receivedMessage):
  489. firstMessage = receivedMessage
  490. didReceiveMessage.fulfill()
  491. case let .disconnected(code, reason):
  492. firstCloseCode = code
  493. firstCloseReason = reason
  494. didDisconnect.fulfill()
  495. case let .completed(completion):
  496. firstReceivedCompletion = completion
  497. didComplete.fulfill()
  498. }
  499. }
  500. .streamMessageEvents { event in
  501. switch event.kind {
  502. case let .connected(`protocol`):
  503. secondConnectedProtocol = `protocol`
  504. didConnect.fulfill()
  505. case let .receivedMessage(receivedMessage):
  506. secondMessage = receivedMessage
  507. didReceiveMessage.fulfill()
  508. case let .disconnected(code, reason):
  509. secondCloseCode = code
  510. secondCloseReason = reason
  511. didDisconnect.fulfill()
  512. case let .completed(completion):
  513. secondReceivedCompletion = completion
  514. didComplete.fulfill()
  515. }
  516. }
  517. wait(for: [didConnect, didReceiveMessage, didDisconnect, didComplete],
  518. timeout: timeout,
  519. enforceOrder: true)
  520. // Then
  521. XCTAssertNil(firstConnectedProtocol)
  522. XCTAssertEqual(firstConnectedProtocol, secondConnectedProtocol)
  523. XCTAssertNotNil(firstMessage)
  524. XCTAssertEqual(firstMessage, secondMessage)
  525. XCTAssertEqual(firstCloseCode, .goingAway)
  526. XCTAssertEqual(firstCloseCode, secondCloseCode)
  527. XCTAssertNil(firstCloseReason)
  528. XCTAssertEqual(firstCloseReason, secondCloseReason)
  529. XCTAssertNil(firstReceivedCompletion?.error)
  530. XCTAssertNil(secondReceivedCompletion?.error)
  531. }
  532. }
  533. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  534. extension WebSocketRequest {
  535. @discardableResult
  536. func onCompletion(_ handler: @escaping () -> Void) -> Self {
  537. streamMessageEvents { event in
  538. guard case .completed = event.kind else { return }
  539. handler()
  540. }
  541. }
  542. }
  543. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  544. extension URLSessionWebSocketTask.Message: Equatable {
  545. public static func ==(lhs: URLSessionWebSocketTask.Message, rhs: URLSessionWebSocketTask.Message) -> Bool {
  546. switch (lhs, rhs) {
  547. case let (.string(left), .string(right)):
  548. return left == right
  549. case let (.data(left), .data(right)):
  550. return left == right
  551. default:
  552. return false
  553. }
  554. }
  555. var string: String? {
  556. guard case let .string(string) = self else { return nil }
  557. return string
  558. }
  559. var data: Data? {
  560. guard case let .data(data) = self else { return nil }
  561. return data
  562. }
  563. }
  564. #endif