Coverage Report

Created: 2026-02-11 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/grpc-swift/Sources/GRPC/ClientCalls/ClientCall.swift
Line
Count
Source
1
/*
2
 * Copyright 2019, 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
import Foundation
17
import NIOCore
18
import NIOHPACK
19
import NIOHTTP1
20
import NIOHTTP2
21
import SwiftProtobuf
22
23
/// Base protocol for a client call to a gRPC service.
24
public protocol ClientCall {
25
  /// The type of the request message for the call.
26
  associatedtype RequestPayload
27
  /// The type of the response message for the call.
28
  associatedtype ResponsePayload
29
30
  /// The event loop this call is running on.
31
  var eventLoop: EventLoop { get }
32
33
  /// The options used to make the RPC.
34
  var options: CallOptions { get }
35
36
  /// HTTP/2 stream that requests and responses are sent and received on.
37
  var subchannel: EventLoopFuture<Channel> { get }
38
39
  /// Initial response metadata.
40
  var initialMetadata: EventLoopFuture<HPACKHeaders> { get }
41
42
  /// Status of this call which may be populated by the server or client.
43
  ///
44
  /// The client may populate the status if, for example, it was not possible to connect to the service.
45
  ///
46
  /// Note: despite ``GRPCStatus`` conforming to `Error`, the value will be __always__ delivered as a __success__
47
  /// result even if the status represents a __negative__ outcome. This future will __never__ be fulfilled
48
  /// with an error.
49
  var status: EventLoopFuture<GRPCStatus> { get }
50
51
  /// Trailing response metadata.
52
  var trailingMetadata: EventLoopFuture<HPACKHeaders> { get }
53
54
  /// Cancel the current call.
55
  ///
56
  /// Closes the HTTP/2 stream once it becomes available. Additional writes to the channel will be ignored.
57
  /// Any unfulfilled promises will be failed with a cancelled status (excepting ``status`` which will be
58
  /// succeeded, if not already succeeded).
59
  func cancel(promise: EventLoopPromise<Void>?)
60
}
61
62
extension ClientCall {
63
0
  func cancel() -> EventLoopFuture<Void> {
64
0
    let promise = self.eventLoop.makePromise(of: Void.self)
65
0
    self.cancel(promise: promise)
66
0
    return promise.futureResult
67
0
  }
68
}
69
70
/// A ``ClientCall`` with request streaming; i.e. client-streaming and bidirectional-streaming.
71
public protocol StreamingRequestClientCall: ClientCall {
72
  /// Sends a message to the service.
73
  ///
74
  /// - Important: Callers must terminate the stream of messages by calling <doc:/documentation/GRPC/StreamingRequestClientCall/sendEnd()-7zuxl>
75
  /// or ``sendEnd(promise:)``.
76
  ///
77
  /// - Parameters:
78
  ///   - message: The message to send.
79
  ///   - compression: Whether compression should be used for this message. Ignored if compression
80
  ///     was not enabled for the RPC.
81
  /// - Returns: A future which will be fullfilled when the message has been sent.
82
  func sendMessage(_ message: RequestPayload, compression: Compression) -> EventLoopFuture<Void>
83
84
  /// Sends a message to the service.
85
  ///
86
  /// - Important: Callers must terminate the stream of messages by calling ``sendEnd()-7bhdp`` or ``sendEnd(promise:)``.
87
  ///
88
  /// - Parameters:
89
  ///   - message: The message to send.
90
  ///   - compression: Whether compression should be used for this message. Ignored if compression
91
  ///     was not enabled for the RPC.
92
  ///   - promise: A promise to be fulfilled when the message has been sent.
93
  func sendMessage(
94
    _ message: RequestPayload,
95
    compression: Compression,
96
    promise: EventLoopPromise<Void>?
97
  )
98
99
  /// Sends a sequence of messages to the service.
100
  ///
101
  /// - Important: Callers must terminate the stream of messages by calling <doc:/documentation/GRPC/StreamingRequestClientCall/sendEnd()-7bhdp>
102
  /// or ``sendEnd(promise:)``.
103
  ///
104
  /// - Parameters:
105
  ///   - messages: The sequence of messages to send.
106
  ///   - compression: Whether compression should be used for this message. Ignored if compression
107
  ///     was not enabled for the RPC.
108
  func sendMessages<S: Sequence>(_ messages: S, compression: Compression) -> EventLoopFuture<Void>
109
  where S.Element == RequestPayload
110
111
  /// Sends a sequence of messages to the service.
112
  ///
113
  /// - Important: Callers must terminate the stream of messages by calling ``sendEnd()-7bhdp`` or ``sendEnd(promise:)``.
114
  ///
115
  /// - Parameters:
116
  ///   - messages: The sequence of messages to send.
117
  ///   - compression: Whether compression should be used for this message. Ignored if compression
118
  ///     was not enabled for the RPC.
119
  ///   - promise: A promise to be fulfilled when all messages have been sent successfully.
120
  func sendMessages<S: Sequence>(
121
    _ messages: S,
122
    compression: Compression,
123
    promise: EventLoopPromise<Void>?
124
  ) where S.Element == RequestPayload
125
126
  /// Terminates a stream of messages sent to the service.
127
  ///
128
  /// - Important: This should only ever be called once.
129
  /// - Returns: A future which will be fulfilled when the end has been sent.
130
  func sendEnd() -> EventLoopFuture<Void>
131
132
  /// Terminates a stream of messages sent to the service.
133
  ///
134
  /// - Important: This should only ever be called once.
135
  /// - Parameter promise: A promise to be fulfilled when the end has been sent.
136
  func sendEnd(promise: EventLoopPromise<Void>?)
137
}
138
139
extension StreamingRequestClientCall {
140
  public func sendMessage(
141
    _ message: RequestPayload,
142
    compression: Compression = .deferToCallDefault
143
0
  ) -> EventLoopFuture<Void> {
144
0
    let promise = self.eventLoop.makePromise(of: Void.self)
145
0
    self.sendMessage(message, compression: compression, promise: promise)
146
0
    return promise.futureResult
147
0
  }
148
149
  public func sendMessages<S: Sequence>(
150
    _ messages: S,
151
    compression: Compression = .deferToCallDefault
152
  ) -> EventLoopFuture<Void>
153
0
  where S.Element == RequestPayload {
154
0
    let promise = self.eventLoop.makePromise(of: Void.self)
155
0
    self.sendMessages(messages, compression: compression, promise: promise)
156
0
    return promise.futureResult
157
0
  }
158
159
0
  public func sendEnd() -> EventLoopFuture<Void> {
160
0
    let promise = self.eventLoop.makePromise(of: Void.self)
161
0
    self.sendEnd(promise: promise)
162
0
    return promise.futureResult
163
0
  }
164
}
165
166
/// A ``ClientCall`` with a unary response; i.e. unary and client-streaming.
167
public protocol UnaryResponseClientCall: ClientCall {
168
  /// The response message returned from the service if the call is successful. This may be failed
169
  /// if the call encounters an error.
170
  ///
171
  /// Callers should rely on the `status` of the call for the canonical outcome.
172
  var response: EventLoopFuture<ResponsePayload> { get }
173
}