Skip to content

Commit 2b29c28

Browse files
committed
Support protobuf separately to GRPCPayload for the client
Motivation: To support payloads other than `SwiftProtobuf.Message` we required that all messages conform to `GRPCPayload`. For protobuf messages we added `GRPCProtobufPayload` which provides a default implemenation of `GRPCPayload` for protobuf messages. We generated this conformance for all protobuf messages we saw. This lead to a number issues and workarounds including: grpc#738, grpc#778, grpc#801, grpc#837, grpc#877, grpc#881. The intention is to continue to support `GRPCPayload` in addition to protobuf, however, support for protobuf will not be via the `GRPCProtobufPayload` protocol. This PR builds on grpc#886 by increasing the surface area of the client APIs so that they are not constrained to `GRPCPayload`. The surface API now has variants for `GRPCPayload` and `SwiftProtobuf.Message`. Internally the client deals with serializers and deserializers. Modifications: - `GRPCClientChannelHandler` and `GRPCClientStateMachine` are no longer generic over a request and response type, rather they deal with the serialzed version of requests and response (i.e. `ByteBuffer`s) and defer the (de/)serialization to a separate handler. - Added `GRCPClientCodecHandler` to handle (de/)serialization of messages - Clients are no longer constrained to having their request/response payloads conform to `GRPCPayload` - Conformance to `GRPCProtobufPayload` is no longer generated and the protocol is deprecated and has no requirements. - Drop the 'GenerateConformance' option from the codegen since it is no longer required - Reintroduce a filter to the codegen so that we only consider files which contain services, this avoids generating empty files - Regenerate code where necessary Result: - `GRPCProtobufPayload` is no longer required
1 parent b29b16b commit 2b29c28

31 files changed

+793
-392
lines changed

Sources/GRPC/ClientCalls/BidirectionalStreamingCall.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ import Logging
2222
///
2323
/// Messages should be sent via the `sendMessage` and `sendMessages` methods; the stream of messages
2424
/// must be terminated by calling `sendEnd` to indicate the final message has been sent.
25-
public final class BidirectionalStreamingCall<
26-
RequestPayload: GRPCPayload,
27-
ResponsePayload: GRPCPayload
28-
>: StreamingRequestClientCall {
25+
public final class BidirectionalStreamingCall<RequestPayload, ResponsePayload>: StreamingRequestClientCall {
2926
private let transport: ChannelTransport<RequestPayload, ResponsePayload>
3027

3128
/// The options used to make the RPC.
@@ -147,16 +144,20 @@ public final class BidirectionalStreamingCall<
147144
}
148145

149146
extension BidirectionalStreamingCall {
150-
internal static func makeOnHTTP2Stream(
147+
internal static func makeOnHTTP2Stream<S: MessageSerializer, D: MessageDeserializer>(
151148
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
149+
serializer: S,
150+
deserializer: D,
152151
callOptions: CallOptions,
153152
errorDelegate: ClientErrorDelegate?,
154153
logger: Logger,
155154
responseHandler: @escaping (ResponsePayload) -> Void
156-
) -> BidirectionalStreamingCall<RequestPayload, ResponsePayload> {
155+
) -> BidirectionalStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
157156
let eventLoop = multiplexer.eventLoop
158157
let transport = ChannelTransport<RequestPayload, ResponsePayload>(
159158
multiplexer: multiplexer,
159+
serializer: serializer,
160+
deserializer: deserializer,
160161
responseContainer: .init(eventLoop: eventLoop, streamingResponseHandler: responseHandler),
161162
callType: .bidirectionalStreaming,
162163
timeLimit: callOptions.timeLimit,
@@ -167,12 +168,14 @@ extension BidirectionalStreamingCall {
167168
return BidirectionalStreamingCall(transport: transport, options: callOptions)
168169
}
169170

170-
internal static func make(
171+
internal static func make<S: MessageSerializer, D: MessageDeserializer>(
172+
serializer: S,
173+
deserializer: D,
171174
fakeResponse: FakeStreamingResponse<RequestPayload, ResponsePayload>?,
172175
callOptions: CallOptions,
173176
logger: Logger,
174177
responseHandler: @escaping (ResponsePayload) -> Void
175-
) -> BidirectionalStreamingCall<RequestPayload, ResponsePayload> {
178+
) -> BidirectionalStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
176179
let eventLoop = fakeResponse?.channel.eventLoop ?? EmbeddedEventLoop()
177180
let responseContainer = ResponsePartContainer(eventLoop: eventLoop, streamingResponseHandler: responseHandler)
178181

Sources/GRPC/ClientCalls/ClientCall.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ import SwiftProtobuf
2323
/// Base protocol for a client call to a gRPC service.
2424
public protocol ClientCall {
2525
/// The type of the request message for the call.
26-
associatedtype RequestPayload: GRPCPayload
26+
associatedtype RequestPayload
2727
/// The type of the response message for the call.
28-
associatedtype ResponsePayload: GRPCPayload
28+
associatedtype ResponsePayload
2929

3030
/// The event loop this call is running on.
3131
var eventLoop: EventLoop { get }
@@ -159,7 +159,7 @@ public protocol UnaryResponseClientCall: ClientCall {
159159
// a NIO HTTP/2 stream channel.
160160

161161
internal protocol ClientCallInbound {
162-
associatedtype Response: GRPCPayload
162+
associatedtype Response
163163
typealias ResponsePart = _GRPCClientResponsePart<Response>
164164

165165
/// Receive an error.
@@ -170,7 +170,7 @@ internal protocol ClientCallInbound {
170170
}
171171

172172
internal protocol ClientCallOutbound {
173-
associatedtype Request: GRPCPayload
173+
associatedtype Request
174174
typealias RequestPart = _GRPCClientRequestPart<Request>
175175

176176
/// Send a single request part and complete the promise once the part has been sent.

Sources/GRPC/ClientCalls/ClientCallTransport.swift

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import Logging
4444
///```
4545
///
4646
/// Note: the "main" pipeline provided by the channel in `ClientConnection`.
47-
internal class ChannelTransport<Request: GRPCPayload, Response: GRPCPayload> {
47+
internal class ChannelTransport<Request, Response> {
4848
internal typealias RequestPart = _GRPCClientRequestPart<Request>
4949
internal typealias ResponsePart = _GRPCClientResponsePart<Response>
5050

@@ -148,14 +148,16 @@ internal class ChannelTransport<Request: GRPCPayload, Response: GRPCPayload> {
148148
channelProvider(self, channelPromise)
149149
}
150150

151-
internal convenience init(
151+
internal convenience init<S: MessageSerializer, D: MessageDeserializer>(
152152
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
153+
serializer: S,
154+
deserializer: D,
153155
responseContainer: ResponsePartContainer<Response>,
154156
callType: GRPCCallType,
155157
timeLimit: TimeLimit,
156158
errorDelegate: ClientErrorDelegate?,
157159
logger: Logger
158-
) {
160+
) where S.Input == Request, D.Output == Response {
159161
self.init(
160162
eventLoop: multiplexer.eventLoop,
161163
responseContainer: responseContainer,
@@ -168,7 +170,8 @@ internal class ChannelTransport<Request: GRPCPayload, Response: GRPCPayload> {
168170
case .success(let mux):
169171
mux.createStreamChannel(promise: streamPromise) { stream, streamID in
170172
stream.pipeline.addHandlers([
171-
_GRPCClientChannelHandler<Request, Response>(streamID: streamID, callType: callType, logger: logger),
173+
_GRPCClientChannelHandler(streamID: streamID, callType: callType, logger: logger),
174+
GRPCClientCodecHandler(serializer: serializer, deserializer: deserializer),
172175
GRPCClientCallHandler(call: call)
173176
])
174177
}

Sources/GRPC/ClientCalls/ClientStreamingCall.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ import Logging
2222
///
2323
/// Messages should be sent via the `sendMessage` and `sendMessages` methods; the stream of messages
2424
/// must be terminated by calling `sendEnd` to indicate the final message has been sent.
25-
public final class ClientStreamingCall<
26-
RequestPayload: GRPCPayload,
27-
ResponsePayload: GRPCPayload
28-
> : StreamingRequestClientCall, UnaryResponseClientCall {
25+
public final class ClientStreamingCall<RequestPayload, ResponsePayload>: StreamingRequestClientCall, UnaryResponseClientCall {
2926
private let transport: ChannelTransport<RequestPayload, ResponsePayload>
3027

3128
/// The options used to make the RPC.
@@ -152,16 +149,20 @@ public final class ClientStreamingCall<
152149
}
153150

154151
extension ClientStreamingCall {
155-
internal static func makeOnHTTP2Stream(
152+
internal static func makeOnHTTP2Stream<S: MessageSerializer, D: MessageDeserializer>(
156153
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
154+
serializer: S,
155+
deserializer: D,
157156
callOptions: CallOptions,
158157
errorDelegate: ClientErrorDelegate?,
159158
logger: Logger
160-
) -> ClientStreamingCall<RequestPayload, ResponsePayload> {
159+
) -> ClientStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
161160
let eventLoop = multiplexer.eventLoop
162161
let responsePromise: EventLoopPromise<ResponsePayload> = eventLoop.makePromise()
163162
let transport = ChannelTransport<RequestPayload, ResponsePayload>(
164163
multiplexer: multiplexer,
164+
serializer: serializer,
165+
deserializer: deserializer,
165166
responseContainer: .init(eventLoop: eventLoop, unaryResponsePromise: responsePromise),
166167
callType: .clientStreaming,
167168
timeLimit: callOptions.timeLimit,
@@ -171,11 +172,13 @@ extension ClientStreamingCall {
171172
return ClientStreamingCall(response: responsePromise.futureResult, transport: transport, options: callOptions)
172173
}
173174

174-
internal static func make(
175+
internal static func make<S: MessageSerializer, D: MessageDeserializer>(
176+
serializer: S,
177+
deserializer: D,
175178
fakeResponse: FakeUnaryResponse<RequestPayload, ResponsePayload>?,
176179
callOptions: CallOptions,
177180
logger: Logger
178-
) -> ClientStreamingCall<RequestPayload, ResponsePayload> {
181+
) -> ClientStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
179182
let eventLoop = fakeResponse?.channel.eventLoop ?? EmbeddedEventLoop()
180183
let responsePromise: EventLoopPromise<ResponsePayload> = eventLoop.makePromise()
181184
let responseContainer = ResponsePartContainer(eventLoop: eventLoop, unaryResponsePromise: responsePromise)

Sources/GRPC/ClientCalls/GRPCClientCallHandler.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import NIO
1717

1818
/// An inbound channel handler which forwards events and messages to a client call.
19-
internal class GRPCClientCallHandler<Request: GRPCPayload, Response: GRPCPayload>: ChannelInboundHandler {
19+
internal class GRPCClientCallHandler<Request, Response>: ChannelInboundHandler {
2020
typealias InboundIn = _GRPCClientResponsePart<Response>
2121
private var call: ChannelTransport<Request, Response>
2222

Sources/GRPC/ClientCalls/ResponsePartContainer.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import NIO
1717
import NIOHPACK
1818

1919
/// A container for RPC response parts.
20-
internal struct ResponsePartContainer<Response: GRPCPayload> {
20+
internal struct ResponsePartContainer<Response> {
2121
/// The type of handler for response message part.
2222
enum ResponseHandler {
2323
case unary(EventLoopPromise<Response>)

Sources/GRPC/ClientCalls/ServerStreamingCall.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,7 @@ import Logging
2020

2121
/// A server-streaming gRPC call. The request is sent on initialization, each response is passed to
2222
/// the provided observer block.
23-
public final class ServerStreamingCall<
24-
RequestPayload: GRPCPayload,
25-
ResponsePayload: GRPCPayload
26-
>: ClientCall {
23+
public final class ServerStreamingCall<RequestPayload, ResponsePayload>: ClientCall {
2724
private let transport: ChannelTransport<RequestPayload, ResponsePayload>
2825

2926
/// The options used to make the RPC.
@@ -93,16 +90,20 @@ public final class ServerStreamingCall<
9390
}
9491

9592
extension ServerStreamingCall {
96-
internal static func makeOnHTTP2Stream(
93+
internal static func makeOnHTTP2Stream<S: MessageSerializer, D: MessageDeserializer>(
9794
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
95+
serializer: S,
96+
deserializer: D,
9897
callOptions: CallOptions,
9998
errorDelegate: ClientErrorDelegate?,
10099
logger: Logger,
101100
responseHandler: @escaping (ResponsePayload) -> Void
102-
) -> ServerStreamingCall<RequestPayload, ResponsePayload> {
101+
) -> ServerStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
103102
let eventLoop = multiplexer.eventLoop
104103
let transport = ChannelTransport<RequestPayload, ResponsePayload>(
105104
multiplexer: multiplexer,
105+
serializer: serializer,
106+
deserializer: deserializer,
106107
responseContainer: .init(eventLoop: eventLoop, streamingResponseHandler: responseHandler),
107108
callType: .serverStreaming,
108109
timeLimit: callOptions.timeLimit,
@@ -113,12 +114,14 @@ extension ServerStreamingCall {
113114
return ServerStreamingCall(transport: transport, options: callOptions)
114115
}
115116

116-
internal static func make(
117+
internal static func make<S: MessageSerializer, D: MessageDeserializer>(
118+
serializer: S,
119+
deserializer: D,
117120
fakeResponse: FakeStreamingResponse<RequestPayload, ResponsePayload>?,
118121
callOptions: CallOptions,
119122
logger: Logger,
120123
responseHandler: @escaping (ResponsePayload) -> Void
121-
) -> ServerStreamingCall<RequestPayload, ResponsePayload> {
124+
) -> ServerStreamingCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
122125
let eventLoop = fakeResponse?.channel.eventLoop ?? EmbeddedEventLoop()
123126
let responseContainer = ResponsePartContainer(eventLoop: eventLoop, streamingResponseHandler: responseHandler)
124127

Sources/GRPC/ClientCalls/UnaryCall.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,7 @@ import NIOHPACK
2222
import Logging
2323

2424
/// A unary gRPC call. The request is sent on initialization.
25-
public final class UnaryCall<
26-
RequestPayload: GRPCPayload,
27-
ResponsePayload: GRPCPayload
28-
>: UnaryResponseClientCall {
25+
public final class UnaryCall<RequestPayload, ResponsePayload>: UnaryResponseClientCall {
2926
private let transport: ChannelTransport<RequestPayload, ResponsePayload>
3027

3128
/// The options used to make the RPC.
@@ -100,16 +97,20 @@ public final class UnaryCall<
10097
}
10198

10299
extension UnaryCall {
103-
internal static func makeOnHTTP2Stream(
100+
internal static func makeOnHTTP2Stream<S: MessageSerializer, D: MessageDeserializer>(
104101
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
102+
serializer: S,
103+
deserializer: D,
105104
callOptions: CallOptions,
106105
errorDelegate: ClientErrorDelegate?,
107106
logger: Logger
108-
) -> UnaryCall<RequestPayload, ResponsePayload> {
107+
) -> UnaryCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
109108
let eventLoop = multiplexer.eventLoop
110109
let responsePromise: EventLoopPromise<ResponsePayload> = eventLoop.makePromise()
111110
let transport = ChannelTransport<RequestPayload, ResponsePayload>(
112111
multiplexer: multiplexer,
112+
serializer: serializer,
113+
deserializer: deserializer,
113114
responseContainer: .init(eventLoop: eventLoop, unaryResponsePromise: responsePromise),
114115
callType: .unary,
115116
timeLimit: callOptions.timeLimit,
@@ -119,11 +120,13 @@ extension UnaryCall {
119120
return UnaryCall(response: responsePromise.futureResult, transport: transport, options: callOptions)
120121
}
121122

122-
internal static func make(
123+
internal static func make<S: MessageSerializer, D: MessageDeserializer>(
124+
serializer: S,
125+
deserializer: D,
123126
fakeResponse: FakeUnaryResponse<RequestPayload, ResponsePayload>?,
124127
callOptions: CallOptions,
125128
logger: Logger
126-
) -> UnaryCall<RequestPayload, ResponsePayload> {
129+
) -> UnaryCall<RequestPayload, ResponsePayload> where S.Input == RequestPayload, D.Output == ResponsePayload {
127130
let eventLoop = fakeResponse?.channel.eventLoop ?? EmbeddedEventLoop()
128131
let responsePromise: EventLoopPromise<ResponsePayload> = eventLoop.makePromise()
129132
let responseContainer = ResponsePartContainer(eventLoop: eventLoop, unaryResponsePromise: responsePromise)

0 commit comments

Comments
 (0)