Skip to content

Commit 50ccad2

Browse files
authored
Put the client connection behind a protocol (#727)
Motivation: Generated client stubs may only be initialized using a `ClientConnection` which makes the assumption that a connection will only ever be singular, and that all RPCs for that stub will run on a single event loop. However, there are scenarios where we may want to drive a stub and have multiple connections (load balancing), or no real connection at all (testing). Modifications: - Generated client stubs now depend on the implementation of a `GRPCChannel` - The first argument label for the `init` in generated clients has changed from `connection` to `client` - Added fixits for the above - Updated generated code - `GRPCChannel`s contract ensures that RPCs can be made but do not specify the transport. - `GRPCClient` now requires a `GRPCChannel` - `GRPCClient` provides higher level wrappers for the factory methods on `GRPCChannel` (i.e. provide defaults such as for call options) Result: - We have a better ability, in the future, to use already-generated stubs with different `GRPCChannel`s. - Cleaner abstraction between call and connection.
1 parent 9243527 commit 50ccad2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+580
-322
lines changed

Sources/Examples/Echo/Model/echo.grpc.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ public protocol Echo_EchoService {
3636
}
3737

3838
public final class Echo_EchoServiceClient: GRPCClient, Echo_EchoService {
39-
public let connection: ClientConnection
39+
public let channel: GRPCChannel
4040
public var defaultCallOptions: CallOptions
4141

4242
/// Creates a client for the echo.Echo service.
4343
///
4444
/// - Parameters:
45-
/// - connection: `ClientConnection` to the service host.
45+
/// - channel: `GRPCChannel` to the service host.
4646
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
47-
public init(connection: ClientConnection, defaultCallOptions: CallOptions = CallOptions()) {
48-
self.connection = connection
47+
public init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
48+
self.channel = channel
4949
self.defaultCallOptions = defaultCallOptions
5050
}
5151

@@ -153,3 +153,4 @@ extension Echo_EchoProvider {
153153
/// Provides conformance to `GRPCPayload` for the request and response messages
154154
extension Echo_EchoRequest: GRPCProtobufPayload {}
155155
extension Echo_EchoResponse: GRPCProtobufPayload {}
156+

Sources/Examples/Echo/Runtime/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ func makeClient(group: EventLoopGroup, host: String, port: Int, useTLS: Bool) ->
198198

199199
// Start the connection and create the client:
200200
let connection = ClientConnection(configuration: configuration)
201-
return Echo_EchoServiceClient(connection: connection)
201+
return Echo_EchoServiceClient(channel: connection)
202202
}
203203

204204
func callRPC(_ rpc: RPC, using client: Echo_EchoServiceClient, message: String) {

Sources/Examples/HelloWorld/Client/main.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func main(args: [String]) {
7676
let connection = ClientConnection(configuration: configuration)
7777

7878
// Provide the connection to the generated client.
79-
let greeter = Helloworld_GreeterServiceClient(connection: connection)
79+
let greeter = Helloworld_GreeterServiceClient(channel: connection)
8080

8181
// Do the greeting.
8282
greet(name: name, client: greeter)

Sources/Examples/HelloWorld/Model/helloworld.grpc.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,16 @@ public protocol Helloworld_GreeterService {
3333
}
3434

3535
public final class Helloworld_GreeterServiceClient: GRPCClient, Helloworld_GreeterService {
36-
public let connection: ClientConnection
36+
public let channel: GRPCChannel
3737
public var defaultCallOptions: CallOptions
3838

3939
/// Creates a client for the helloworld.Greeter service.
4040
///
4141
/// - Parameters:
42-
/// - connection: `ClientConnection` to the service host.
42+
/// - channel: `GRPCChannel` to the service host.
4343
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
44-
public init(connection: ClientConnection, defaultCallOptions: CallOptions = CallOptions()) {
45-
self.connection = connection
44+
public init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
45+
self.channel = channel
4646
self.defaultCallOptions = defaultCallOptions
4747
}
4848

@@ -88,3 +88,4 @@ extension Helloworld_GreeterProvider {
8888
/// Provides conformance to `GRPCPayload` for the request and response messages
8989
extension Helloworld_HelloRequest: GRPCProtobufPayload {}
9090
extension Helloworld_HelloReply: GRPCProtobufPayload {}
91+

Sources/Examples/RouteGuide/Client/main.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func makeClient(port: Int, group: EventLoopGroup) -> Routeguide_RouteGuideServic
3434
)
3535

3636
let connection = ClientConnection(configuration: config)
37-
return Routeguide_RouteGuideServiceClient(connection: connection)
37+
return Routeguide_RouteGuideServiceClient(channel: connection)
3838
}
3939

4040
/// Unary call example. Calls `getFeature` and prints the response.
@@ -214,7 +214,7 @@ func main(args: [String]) throws {
214214
// Make a client, make sure we close it when we're done.
215215
let routeGuide = makeClient(port: port, group: group)
216216
defer {
217-
try? routeGuide.connection.close().wait()
217+
try? routeGuide.channel.close().wait()
218218
}
219219

220220
// Look for a valid feature.

Sources/Examples/RouteGuide/Model/route_guide.grpc.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ public protocol Routeguide_RouteGuideService {
3636
}
3737

3838
public final class Routeguide_RouteGuideServiceClient: GRPCClient, Routeguide_RouteGuideService {
39-
public let connection: ClientConnection
39+
public let channel: GRPCChannel
4040
public var defaultCallOptions: CallOptions
4141

4242
/// Creates a client for the routeguide.RouteGuide service.
4343
///
4444
/// - Parameters:
45-
/// - connection: `ClientConnection` to the service host.
45+
/// - channel: `GRPCChannel` to the service host.
4646
/// - defaultCallOptions: Options to use for each service call if the user doesn't provide them.
47-
public init(connection: ClientConnection, defaultCallOptions: CallOptions = CallOptions()) {
48-
self.connection = connection
47+
public init(channel: GRPCChannel, defaultCallOptions: CallOptions = CallOptions()) {
48+
self.channel = channel
4949
self.defaultCallOptions = defaultCallOptions
5050
}
5151

@@ -156,3 +156,4 @@ extension Routeguide_Feature: GRPCProtobufPayload {}
156156
extension Routeguide_Rectangle: GRPCProtobufPayload {}
157157
extension Routeguide_RouteSummary: GRPCProtobufPayload {}
158158
extension Routeguide_RouteNote: GRPCProtobufPayload {}
159+

Sources/GRPC/ClientCalls/BidirectionalStreamingCall.swift

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import Foundation
17-
import SwiftProtobuf
1816
import NIO
17+
import NIOHTTP2
1918
import Logging
2019

2120
/// A bidirectional-streaming gRPC call. Each response is passed to the provided observer block.
@@ -32,42 +31,46 @@ public final class BidirectionalStreamingCall<RequestPayload: GRPCPayload, Respo
3231
StreamingRequestClientCall {
3332
private var messageQueue: EventLoopFuture<Void>
3433

35-
public init(
36-
connection: ClientConnection,
34+
init(
3735
path: String,
36+
scheme: String,
37+
authority: String,
3838
callOptions: CallOptions,
39+
eventLoop: EventLoop,
40+
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
3941
errorDelegate: ClientErrorDelegate?,
42+
logger: Logger,
4043
handler: @escaping (ResponsePayload) -> Void
4144
) {
42-
self.messageQueue = connection.channel.eventLoop.makeSucceededFuture(())
45+
self.messageQueue = eventLoop.makeSucceededFuture(())
4346
let requestID = callOptions.requestIDProvider.requestID()
44-
45-
let logger = Logger(subsystem: .clientChannelCall, metadata: [MetadataKey.requestID: "\(requestID)"])
47+
var logger = logger
48+
logger[metadataKey: MetadataKey.requestID] = "\(requestID)"
4649
logger.debug("starting rpc", metadata: ["path": "\(path)"])
4750

4851
let responseHandler = GRPCClientStreamingResponseChannelHandler(
49-
initialMetadataPromise: connection.channel.eventLoop.makePromise(),
50-
trailingMetadataPromise: connection.channel.eventLoop.makePromise(),
51-
statusPromise: connection.channel.eventLoop.makePromise(),
52+
initialMetadataPromise: eventLoop.makePromise(),
53+
trailingMetadataPromise: eventLoop.makePromise(),
54+
statusPromise: eventLoop.makePromise(),
5255
errorDelegate: errorDelegate,
5356
timeout: callOptions.timeout,
5457
logger: logger,
5558
responseHandler: handler
5659
)
5760

5861
let requestHead = _GRPCRequestHead(
59-
scheme: connection.configuration.httpProtocol.scheme,
62+
scheme: scheme,
6063
path: path,
61-
host: connection.configuration.target.host,
64+
host: authority,
6265
requestID: requestID,
6366
options: callOptions
6467
)
6568

6669
let requestHandler = _StreamingRequestChannelHandler<RequestPayload>(requestHead: requestHead)
6770

6871
super.init(
69-
eventLoop: connection.eventLoop,
70-
multiplexer: connection.multiplexer,
72+
eventLoop: eventLoop,
73+
multiplexer: multiplexer,
7174
callType: .bidirectionalStreaming,
7275
callOptions: callOptions,
7376
responseHandler: responseHandler,

Sources/GRPC/ClientCalls/ClientStreamingCall.swift

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import Foundation
17-
import SwiftProtobuf
1816
import NIO
17+
import NIOHTTP2
1918
import Logging
2019

2120
/// A client-streaming gRPC call.
@@ -35,43 +34,48 @@ public final class ClientStreamingCall<RequestPayload: GRPCPayload, ResponsePayl
3534
public let response: EventLoopFuture<ResponsePayload>
3635
private var messageQueue: EventLoopFuture<Void>
3736

38-
public init(
39-
connection: ClientConnection,
37+
init(
4038
path: String,
39+
scheme: String,
40+
authority: String,
4141
callOptions: CallOptions,
42-
errorDelegate: ClientErrorDelegate?
42+
eventLoop: EventLoop,
43+
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
44+
errorDelegate: ClientErrorDelegate?,
45+
logger: Logger
4346
) {
4447
let requestID = callOptions.requestIDProvider.requestID()
45-
let logger = Logger(subsystem: .clientChannelCall, metadata: [MetadataKey.requestID: "\(requestID)"])
48+
var logger = logger
49+
logger[metadataKey: MetadataKey.requestID] = "\(requestID)"
4650
logger.debug("starting rpc", metadata: ["path": "\(path)"])
4751

48-
self.messageQueue = connection.eventLoop.makeSucceededFuture(())
49-
let responsePromise = connection.eventLoop.makePromise(of: ResponsePayload.self)
52+
self.messageQueue = eventLoop.makeSucceededFuture(())
53+
let responsePromise = eventLoop.makePromise(of: ResponsePayload.self)
5054
self.response = responsePromise.futureResult
5155

5256
let responseHandler = GRPCClientUnaryResponseChannelHandler(
53-
initialMetadataPromise: connection.channel.eventLoop.makePromise(),
54-
trailingMetadataPromise: connection.channel.eventLoop.makePromise(),
57+
initialMetadataPromise: eventLoop.makePromise(),
58+
trailingMetadataPromise: eventLoop.makePromise(),
5559
responsePromise: responsePromise,
56-
statusPromise: connection.channel.eventLoop.makePromise(),
60+
statusPromise: eventLoop.makePromise(),
5761
errorDelegate: errorDelegate,
5862
timeout: callOptions.timeout,
5963
logger: logger
6064
)
6165

6266
let requestHead = _GRPCRequestHead(
63-
scheme: connection.configuration.httpProtocol.scheme,
67+
scheme: scheme,
6468
path: path,
65-
host: connection.configuration.target.host,
69+
host: authority,
6670
requestID: requestID,
6771
options: callOptions
6872
)
6973

7074
let requestHandler = _StreamingRequestChannelHandler<RequestPayload>(requestHead: requestHead)
7175

7276
super.init(
73-
eventLoop: connection.eventLoop,
74-
multiplexer: connection.multiplexer,
77+
eventLoop: eventLoop,
78+
multiplexer: multiplexer,
7579
callType: .clientStreaming,
7680
callOptions: callOptions,
7781
responseHandler: responseHandler,

Sources/GRPC/ClientCalls/ServerStreamingCall.swift

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import Foundation
17-
import SwiftProtobuf
1816
import NIO
17+
import NIOHTTP2
1918
import Logging
2019

2120
/// A server-streaming gRPC call. The request is sent on initialization, each response is passed to the provided observer block.
@@ -25,32 +24,38 @@ import Logging
2524
/// - `status`: the status of the gRPC call after it has ended,
2625
/// - `trailingMetadata`: any metadata returned from the server alongside the `status`.
2726
public final class ServerStreamingCall<RequestPayload: GRPCPayload, ResponsePayload: GRPCPayload>: BaseClientCall<RequestPayload, ResponsePayload> {
28-
public init(
29-
connection: ClientConnection,
27+
init(
3028
path: String,
31-
request: RequestPayload,
29+
scheme: String,
30+
authority: String,
3231
callOptions: CallOptions,
32+
eventLoop: EventLoop,
33+
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
3334
errorDelegate: ClientErrorDelegate?,
35+
logger: Logger,
36+
request: RequestPayload,
3437
handler: @escaping (ResponsePayload) -> Void
3538
) {
3639
let requestID = callOptions.requestIDProvider.requestID()
37-
let logger = Logger(subsystem: .clientChannelCall, metadata: [MetadataKey.requestID: "\(requestID)"])
40+
var logger = logger
41+
logger[metadataKey: MetadataKey.requestID] = "\(requestID)"
3842
logger.debug("starting rpc", metadata: ["path": "\(path)"])
3943

44+
4045
let responseHandler = GRPCClientStreamingResponseChannelHandler(
41-
initialMetadataPromise: connection.channel.eventLoop.makePromise(),
42-
trailingMetadataPromise: connection.channel.eventLoop.makePromise(),
43-
statusPromise: connection.channel.eventLoop.makePromise(),
46+
initialMetadataPromise: eventLoop.makePromise(),
47+
trailingMetadataPromise: eventLoop.makePromise(),
48+
statusPromise: eventLoop.makePromise(),
4449
errorDelegate: errorDelegate,
4550
timeout: callOptions.timeout,
4651
logger: logger,
4752
responseHandler: handler
4853
)
4954

5055
let requestHead = _GRPCRequestHead(
51-
scheme: connection.configuration.httpProtocol.scheme,
56+
scheme: scheme,
5257
path: path,
53-
host: connection.configuration.target.host,
58+
host: authority,
5459
requestID: requestID,
5560
options: callOptions
5661
)
@@ -61,8 +66,8 @@ public final class ServerStreamingCall<RequestPayload: GRPCPayload, ResponsePayl
6166
)
6267

6368
super.init(
64-
eventLoop: connection.eventLoop,
65-
multiplexer: connection.multiplexer,
69+
eventLoop: eventLoop,
70+
multiplexer: multiplexer,
6671
callType: .serverStreaming,
6772
callOptions: callOptions,
6873
responseHandler: responseHandler,

Sources/GRPC/ClientCalls/UnaryCall.swift

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -32,34 +32,39 @@ public final class UnaryCall<RequestPayload: GRPCPayload, ResponsePayload: GRPCP
3232
UnaryResponseClientCall {
3333
public let response: EventLoopFuture<ResponsePayload>
3434

35-
public init(
36-
connection: ClientConnection,
35+
init(
3736
path: String,
38-
request: RequestPayload,
37+
scheme: String,
38+
authority: String,
3939
callOptions: CallOptions,
40-
errorDelegate: ClientErrorDelegate?
40+
eventLoop: EventLoop,
41+
multiplexer: EventLoopFuture<HTTP2StreamMultiplexer>,
42+
errorDelegate: ClientErrorDelegate?,
43+
logger: Logger,
44+
request: RequestPayload
4145
) {
4246
let requestID = callOptions.requestIDProvider.requestID()
43-
let logger = Logger(subsystem: .clientChannelCall, metadata: [MetadataKey.requestID: "\(requestID)"])
47+
var logger = logger
48+
logger[metadataKey: MetadataKey.requestID] = "\(requestID)"
4449
logger.debug("starting rpc", metadata: ["path": "\(path)"])
4550

46-
let responsePromise = connection.channel.eventLoop.makePromise(of: ResponsePayload.self)
51+
let responsePromise = eventLoop.makePromise(of: ResponsePayload.self)
4752
self.response = responsePromise.futureResult
4853

4954
let responseHandler = GRPCClientUnaryResponseChannelHandler<ResponsePayload>(
50-
initialMetadataPromise: connection.channel.eventLoop.makePromise(),
51-
trailingMetadataPromise: connection.channel.eventLoop.makePromise(),
55+
initialMetadataPromise: eventLoop.makePromise(),
56+
trailingMetadataPromise: eventLoop.makePromise(),
5257
responsePromise: responsePromise,
53-
statusPromise: connection.channel.eventLoop.makePromise(),
58+
statusPromise: eventLoop.makePromise(),
5459
errorDelegate: errorDelegate,
5560
timeout: callOptions.timeout,
5661
logger: logger
5762
)
5863

5964
let requestHead = _GRPCRequestHead(
60-
scheme: connection.configuration.httpProtocol.scheme,
65+
scheme: scheme,
6166
path: path,
62-
host: connection.configuration.target.host,
67+
host: authority,
6368
requestID: requestID,
6469
options: callOptions
6570
)
@@ -70,8 +75,8 @@ public final class UnaryCall<RequestPayload: GRPCPayload, ResponsePayload: GRPCP
7075
)
7176

7277
super.init(
73-
eventLoop: connection.channel.eventLoop,
74-
multiplexer: connection.multiplexer,
78+
eventLoop: eventLoop,
79+
multiplexer: multiplexer,
7580
callType: .unary,
7681
callOptions: callOptions,
7782
responseHandler: responseHandler,

0 commit comments

Comments
 (0)