|
15 | 15 | */
|
16 | 16 | #if compiler(>=5.5)
|
17 | 17 |
|
18 |
| -import _NIOConcurrency |
19 | 18 | import NIOHPACK
|
20 | 19 |
|
21 | 20 | /// Async-await variant of BidirectionalStreamingCall.
|
22 | 21 | @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
|
23 | 22 | public struct GRPCAsyncBidirectionalStreamingCall<Request, Response> {
|
24 | 23 | private let call: Call<Request, Response>
|
25 | 24 | private let responseParts: StreamingResponseParts<Response>
|
| 25 | + private let responseSource: PassthroughMessageSource<Response, Error> |
| 26 | + |
| 27 | + /// A request stream writer for sending messages to the server. |
| 28 | + public let requestStream: GRPCAsyncRequestStreamWriter<Request> |
26 | 29 |
|
27 | 30 | /// The stream of responses from the server.
|
28 | 31 | public let responses: GRPCAsyncResponseStream<Response>
|
@@ -70,93 +73,43 @@ public struct GRPCAsyncBidirectionalStreamingCall<Request, Response> {
|
70 | 73 |
|
71 | 74 | private init(call: Call<Request, Response>) {
|
72 | 75 | self.call = call
|
73 |
| - // Initialise `responseParts` with an empty response handler because we |
74 |
| - // provide the responses as an AsyncSequence in `responseStream`. |
75 | 76 | self.responseParts = StreamingResponseParts(on: call.eventLoop) { _ in }
|
76 |
| - |
77 |
| - // Call and StreamingResponseParts are reference types so we grab a |
78 |
| - // referecence to them here to avoid capturing mutable self in the closure |
79 |
| - // passed to the AsyncThrowingStream initializer. |
80 |
| - // |
81 |
| - // The alternative would be to declare the responseStream as: |
82 |
| - // ``` |
83 |
| - // public private(set) var responseStream: AsyncThrowingStream<ResponsePayload>! |
84 |
| - // ``` |
85 |
| - // |
86 |
| - // UPDATE: Additionally we expect to replace this soon with an AsyncSequence |
87 |
| - // implementation that supports yielding values from outside the closure. |
88 |
| - let call = self.call |
89 |
| - let responseParts = self.responseParts |
90 |
| - let responseStream = AsyncThrowingStream(Response.self) { continuation in |
91 |
| - call.invokeStreamingRequests { error in |
92 |
| - responseParts.handleError(error) |
93 |
| - continuation.finish(throwing: error) |
94 |
| - } onResponsePart: { responsePart in |
95 |
| - responseParts.handle(responsePart) |
96 |
| - switch responsePart { |
97 |
| - case let .message(response): |
98 |
| - continuation.yield(response) |
99 |
| - case .metadata: |
100 |
| - break |
101 |
| - case .end: |
102 |
| - continuation.finish() |
103 |
| - } |
104 |
| - } |
105 |
| - } |
106 |
| - self.responses = .init(responseStream) |
| 77 | + self.responseSource = PassthroughMessageSource<Response, Error>() |
| 78 | + self.responses = .init(PassthroughMessageSequence(consuming: self.responseSource)) |
| 79 | + self.requestStream = call.makeRequestStreamWriter() |
107 | 80 | }
|
108 | 81 |
|
109 | 82 | /// We expose this as the only non-private initializer so that the caller
|
110 | 83 | /// knows that invocation is part of initialisation.
|
111 | 84 | internal static func makeAndInvoke(call: Call<Request, Response>) -> Self {
|
112 |
| - Self(call: call) |
113 |
| - } |
114 |
| - |
115 |
| - // MARK: - Requests |
116 |
| - |
117 |
| - /// Sends a message to the service. |
118 |
| - /// |
119 |
| - /// - Important: Callers must terminate the stream of messages by calling `sendEnd()`. |
120 |
| - /// |
121 |
| - /// - Parameters: |
122 |
| - /// - message: The message to send. |
123 |
| - /// - compression: Whether compression should be used for this message. Ignored if compression |
124 |
| - /// was not enabled for the RPC. |
125 |
| - public func sendMessage( |
126 |
| - _ message: Request, |
127 |
| - compression: Compression = .deferToCallDefault |
128 |
| - ) async throws { |
129 |
| - let compress = self.call.compress(compression) |
130 |
| - let promise = self.call.eventLoop.makePromise(of: Void.self) |
131 |
| - self.call.send(.message(message, .init(compress: compress, flush: true)), promise: promise) |
132 |
| - // TODO: This waits for the message to be written to the socket. We should probably just wait for it to be written to the channel? |
133 |
| - try await promise.futureResult.get() |
134 |
| - } |
135 |
| - |
136 |
| - /// Sends a sequence of messages to the service. |
137 |
| - /// |
138 |
| - /// - Important: Callers must terminate the stream of messages by calling `sendEnd()`. |
139 |
| - /// |
140 |
| - /// - Parameters: |
141 |
| - /// - messages: The sequence of messages to send. |
142 |
| - /// - compression: Whether compression should be used for this message. Ignored if compression |
143 |
| - /// was not enabled for the RPC. |
144 |
| - public func sendMessages<S>( |
145 |
| - _ messages: S, |
146 |
| - compression: Compression = .deferToCallDefault |
147 |
| - ) async throws where S: Sequence, S.Element == Request { |
148 |
| - let promise = self.call.eventLoop.makePromise(of: Void.self) |
149 |
| - self.call.sendMessages(messages, compression: compression, promise: promise) |
150 |
| - try await promise.futureResult.get() |
151 |
| - } |
| 85 | + let asyncCall = Self(call: call) |
| 86 | + |
| 87 | + asyncCall.call.invokeStreamingRequests { error in |
| 88 | + asyncCall.responseParts.handleError(error) |
| 89 | + asyncCall.responseSource.finish(throwing: error) |
| 90 | + } onResponsePart: { responsePart in |
| 91 | + // Handle the metadata, trailers and status. |
| 92 | + asyncCall.responseParts.handle(responsePart) |
| 93 | + |
| 94 | + // Handle the response messages and status. |
| 95 | + switch responsePart { |
| 96 | + case .metadata: |
| 97 | + () |
| 98 | + |
| 99 | + case let .message(response): |
| 100 | + // TODO: when we support backpressure we will need to stop ignoring the return value. |
| 101 | + _ = asyncCall.responseSource.yield(response) |
| 102 | + |
| 103 | + case let .end(status, _): |
| 104 | + if status.isOk { |
| 105 | + asyncCall.responseSource.finish() |
| 106 | + } else { |
| 107 | + asyncCall.responseSource.finish(throwing: status) |
| 108 | + } |
| 109 | + } |
| 110 | + } |
152 | 111 |
|
153 |
| - /// Terminates a stream of messages sent to the service. |
154 |
| - /// |
155 |
| - /// - Important: This should only ever be called once. |
156 |
| - public func sendEnd() async throws { |
157 |
| - let promise = self.call.eventLoop.makePromise(of: Void.self) |
158 |
| - self.call.send(.end, promise: promise) |
159 |
| - try await promise.futureResult.get() |
| 112 | + return asyncCall |
160 | 113 | }
|
161 | 114 | }
|
162 | 115 |
|
|
0 commit comments