ClientResponse.swift 15 KB

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