ClientResponse.swift 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. /*
  2. * Copyright 2023, 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. /// A namespace for response message types used by clients.
  17. public enum ClientResponse {}
  18. extension ClientResponse {
  19. /// A response for a single message received by a client.
  20. ///
  21. /// Single responses are used for unary and client-streaming RPCs. For streaming responses
  22. /// see ``ClientResponse/Stream``.
  23. ///
  24. /// A single response captures every part of the response stream and distinguishes successful
  25. /// and unsuccessful responses via the ``accepted`` property. The value for the `success` case
  26. /// contains the initial metadata, response message, and the trailing metadata and implicitly
  27. /// has an ``Status/Code-swift.struct/ok`` status code.
  28. ///
  29. /// The `failure` case indicates that the server chose not to process the RPC, or the processing
  30. /// of the RPC failed, or the client failed to execute the request. The failure case contains
  31. /// an ``RPCError`` describing why the RPC failed, including an error code, error message and any
  32. /// metadata sent by the server.
  33. ///
  34. /// ### Using ``Single`` responses
  35. ///
  36. /// Each response has a ``accepted`` property which contains all RPC information. You can create
  37. /// one by calling ``init(accepted:)`` or one of the two convenience initializers:
  38. /// - ``init(message:metadata:trailingMetadata:)`` to create a successful response, or
  39. /// - ``init(of:error:)`` to create a failed response.
  40. ///
  41. /// You can interrogate a response by inspecting the ``accepted`` property directly or by using
  42. /// its convenience properties:
  43. /// - ``metadata`` extracts the initial metadata,
  44. /// - ``message`` extracts the message, or throws if the response failed, and
  45. /// - ``trailingMetadata`` extracts the trailing metadata.
  46. ///
  47. /// The following example demonstrates how you can use the API:
  48. ///
  49. /// ```swift
  50. /// // Create a successful response
  51. /// let response = ClientResponse.Single<String>(
  52. /// message: "Hello, World!",
  53. /// metadata: ["hello": "initial metadata"],
  54. /// trailingMetadata: ["goodbye": "trailing metadata"]
  55. /// )
  56. ///
  57. /// // The explicit API:
  58. /// switch response {
  59. /// case .success(let contents):
  60. /// print("Received response with message '\(contents.message)'")
  61. /// case .failure(let error):
  62. /// print("RPC failed with code '\(error.code)'")
  63. /// }
  64. ///
  65. /// // The convenience API:
  66. /// do {
  67. /// print("Received response with message '\(try response.message)'")
  68. /// } catch let error as RPCError {
  69. /// print("RPC failed with code '\(error.code)'")
  70. /// }
  71. /// ```
  72. public struct Single<Message: Sendable>: Sendable {
  73. /// The contents of an accepted response with a single message.
  74. public struct Contents: Sendable {
  75. /// Metadata received from the server at the beginning of the response.
  76. ///
  77. /// The metadata may contain transport-specific information in addition to any application
  78. /// level metadata provided by the service.
  79. public var metadata: Metadata
  80. /// The response message received from the server.
  81. public var message: Message
  82. /// Metadata received from the server at the end of the response.
  83. ///
  84. /// The metadata may contain transport-specific information in addition to any application
  85. /// level metadata provided by the service.
  86. public var trailingMetadata: Metadata
  87. /// Creates a `Contents`.
  88. ///
  89. /// - Parameters:
  90. /// - metadata: Metadata received from the server at the beginning of the response.
  91. /// - message: The response message received from the server.
  92. /// - trailingMetadata: Metadata received from the server at the end of the response.
  93. public init(
  94. metadata: Metadata,
  95. message: Message,
  96. trailingMetadata: Metadata
  97. ) {
  98. self.metadata = metadata
  99. self.message = message
  100. self.trailingMetadata = trailingMetadata
  101. }
  102. }
  103. /// Whether the RPC was accepted or rejected.
  104. ///
  105. /// The `success` case indicates the RPC completed successfully with an
  106. /// ``Status/Code-swift.struct/ok`` status code. The `failure` case indicates that the RPC was
  107. /// rejected by the server and wasn't processed or couldn't be processed successfully.
  108. public var accepted: Result<Contents, RPCError>
  109. /// Creates a new response.
  110. ///
  111. /// - Parameter accepted: The result of the RPC.
  112. public init(accepted: Result<Contents, RPCError>) {
  113. self.accepted = accepted
  114. }
  115. }
  116. }
  117. extension ClientResponse {
  118. /// A response for a stream of messages received by a client.
  119. ///
  120. /// Stream responses are used for server-streaming and bidirectional-streaming RPCs. For single
  121. /// responses see ``ClientResponse/Single``.
  122. ///
  123. /// A stream response captures every part of the response stream over time and distinguishes
  124. /// accepted and rejected requests via the ``accepted`` property. An "accepted" request is one
  125. /// where the the server responds with initial metadata and attempts to process the request. A
  126. /// "rejected" request is one where the server responds with a status as the first and only
  127. /// response part and doesn't process the request body.
  128. ///
  129. /// The value for the `success` case contains the initial metadata and a ``RPCAsyncSequence`` of
  130. /// message parts (messages followed by a single status). If the sequence completes without
  131. /// throwing then the response implicitly has an ``Status/Code-swift.struct/ok`` status code.
  132. /// However, the response sequence may also throw an ``RPCError`` if the server fails to complete
  133. /// processing the request.
  134. ///
  135. /// The `failure` case indicates that the server chose not to process the RPC or the client failed
  136. /// to execute the request. The failure case contains an ``RPCError`` describing why the RPC
  137. /// failed, including an error code, error message and any metadata sent by the server.
  138. ///
  139. /// ### Using ``Stream`` responses
  140. ///
  141. /// Each response has a ``accepted`` property which contains RPC information. You can create
  142. /// one by calling ``init(accepted:)`` or one of the two convenience initializers:
  143. /// - ``init(of:metadata:bodyParts:)`` to create an accepted response, or
  144. /// - ``init(of:error:)`` to create a failed response.
  145. ///
  146. /// You can interrogate a response by inspecting the ``accepted`` property directly or by using
  147. /// its convenience properties:
  148. /// - ``metadata`` extracts the initial metadata,
  149. /// - ``messages`` extracts the sequence of response message, or throws if the response failed.
  150. ///
  151. /// The following example demonstrates how you can use the API:
  152. ///
  153. /// ```swift
  154. /// // Create a failed response
  155. /// let response = ClientResponse.Stream(
  156. /// of: String.self,
  157. /// error: RPCError(code: .notFound, message: "The requested resource couldn't be located")
  158. /// )
  159. ///
  160. /// // The explicit API:
  161. /// switch response {
  162. /// case .success(let contents):
  163. /// for try await part in contents.bodyParts {
  164. /// switch part {
  165. /// case .message(let message):
  166. /// print("Received message '\(message)'")
  167. /// case .trailingMetadata(let metadata):
  168. /// print("Received trailing metadata '\(metadata)'")
  169. /// }
  170. /// }
  171. /// case .failure(let error):
  172. /// print("RPC failed with code '\(error.code)'")
  173. /// }
  174. ///
  175. /// // The convenience API:
  176. /// do {
  177. /// for try await message in response.messages {
  178. /// print("Received message '\(message)'")
  179. /// }
  180. /// } catch let error as RPCError {
  181. /// print("RPC failed with code '\(error.code)'")
  182. /// }
  183. /// ```
  184. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  185. public struct Stream<Message: Sendable>: Sendable {
  186. public struct Contents: Sendable {
  187. /// Metadata received from the server at the beginning of the response.
  188. ///
  189. /// The metadata may contain transport-specific information in addition to any application
  190. /// level metadata provided by the service.
  191. public var metadata: Metadata
  192. /// A sequence of stream parts received from the server ending with metadata if the RPC
  193. /// succeeded.
  194. ///
  195. /// If the RPC fails then the sequence will throw an ``RPCError``.
  196. ///
  197. /// The sequence may only be iterated once.
  198. public var bodyParts: RPCAsyncSequence<BodyPart>
  199. /// Parts received from the server.
  200. public enum BodyPart: Sendable {
  201. /// A response message.
  202. case message(Message)
  203. /// Metadata. Must be the final value of the sequence unless the stream throws an error.
  204. case trailingMetadata(Metadata)
  205. }
  206. /// Creates a ``Contents``.
  207. ///
  208. /// - Parameters:
  209. /// - metadata: Metadata received from the server at the beginning of the response.
  210. /// - bodyParts: An `AsyncSequence` of parts received from the server.
  211. public init(
  212. metadata: Metadata,
  213. bodyParts: RPCAsyncSequence<BodyPart>
  214. ) {
  215. self.metadata = metadata
  216. self.bodyParts = bodyParts
  217. }
  218. }
  219. /// Whether the RPC was accepted or rejected.
  220. ///
  221. /// The `success` case indicates the RPC was accepted by the server for
  222. /// processing, however, the RPC may still fail by throwing an error from its
  223. /// `messages` sequence. The `failure` case indicates that the RPC was
  224. /// rejected by the server.
  225. public var accepted: Result<Contents, RPCError>
  226. /// Creates a new response.
  227. ///
  228. /// - Parameter accepted: The result of the RPC.
  229. public init(accepted: Result<Contents, RPCError>) {
  230. self.accepted = accepted
  231. }
  232. }
  233. }
  234. // MARK: - Convenience API
  235. extension ClientResponse.Single {
  236. /// Creates a new accepted response.
  237. ///
  238. /// - Parameters:
  239. /// - metadata: Metadata received from the server at the beginning of the response.
  240. /// - message: The response message received from the server.
  241. /// - trailingMetadata: Metadata received from the server at the end of the response.
  242. public init(message: Message, metadata: Metadata = [:], trailingMetadata: Metadata = [:]) {
  243. let contents = Contents(
  244. metadata: metadata,
  245. message: message,
  246. trailingMetadata: trailingMetadata
  247. )
  248. self.accepted = .success(contents)
  249. }
  250. /// Creates a new failed response.
  251. ///
  252. /// - Parameters:
  253. /// - messageType: The type of message.
  254. /// - error: An error describing why the RPC failed.
  255. public init(of messageType: Message.Type = Message.self, error: RPCError) {
  256. self.accepted = .failure(error)
  257. }
  258. /// Returns metadata received from the server at the start of the response.
  259. ///
  260. /// For rejected RPCs (in other words, where ``accepted`` is `failure`) the metadata is empty.
  261. public var metadata: Metadata {
  262. switch self.accepted {
  263. case let .success(contents):
  264. return contents.metadata
  265. case .failure:
  266. return [:]
  267. }
  268. }
  269. /// Returns the message received from the server.
  270. ///
  271. /// - Throws: ``RPCError`` if the request failed.
  272. public var message: Message {
  273. get throws {
  274. try self.accepted.map { $0.message }.get()
  275. }
  276. }
  277. /// Returns metadata received from the server at the end of the response.
  278. ///
  279. /// Unlike ``metadata``, for rejected RPCs the metadata returned may contain values.
  280. public var trailingMetadata: Metadata {
  281. switch self.accepted {
  282. case let .success(contents):
  283. return contents.trailingMetadata
  284. case let .failure(error):
  285. return error.metadata
  286. }
  287. }
  288. }
  289. @available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
  290. extension ClientResponse.Stream {
  291. /// Creates a new accepted response.
  292. ///
  293. /// - Parameters:
  294. /// - messageType: The type of message.
  295. /// - metadata: Metadata received from the server at the beginning of the response.
  296. /// - bodyParts: An ``RPCAsyncSequence`` of response parts received from the server.
  297. public init(
  298. of messageType: Message.Type = Message.self,
  299. metadata: Metadata,
  300. bodyParts: RPCAsyncSequence<Contents.BodyPart>
  301. ) {
  302. let contents = Contents(metadata: metadata, bodyParts: bodyParts)
  303. self.accepted = .success(contents)
  304. }
  305. /// Creates a new failed response.
  306. ///
  307. /// - Parameters:
  308. /// - messageType: The type of message.
  309. /// - error: An error describing why the RPC failed.
  310. public init(of messageType: Message.Type = Message.self, error: RPCError) {
  311. self.accepted = .failure(error)
  312. }
  313. /// Returns metadata received from the server at the start of the response.
  314. ///
  315. /// For rejected RPCs (in other words, where ``accepted`` is `failure`) the metadata is empty.
  316. public var metadata: Metadata {
  317. switch self.accepted {
  318. case let .success(contents):
  319. return contents.metadata
  320. case .failure:
  321. return [:]
  322. }
  323. }
  324. /// Returns the messages received from the server.
  325. ///
  326. /// For rejected RPCs the `RPCAsyncSequence` throws a `RPCError``.
  327. public var messages: RPCAsyncSequence<Message> {
  328. switch self.accepted {
  329. case let .success(contents):
  330. let filtered = contents.bodyParts.compactMap {
  331. switch $0 {
  332. case let .message(message):
  333. return message
  334. case .trailingMetadata:
  335. return nil
  336. }
  337. }
  338. return RPCAsyncSequence(wrapping: filtered)
  339. case let .failure(error):
  340. return RPCAsyncSequence.throwing(error)
  341. }
  342. }
  343. }