@@ -40,9 +40,6 @@ import Atomics
4040/// // Create a configuration object for the client.
4141/// var configuration = GRPCClient.Configuration()
4242///
43- /// // Create and add an interceptor.
44- /// configuration.interceptors.add(StatsRecordingClientInterceptor())
45- ///
4643/// // Override the timeout for the 'Get' method on the 'echo.Echo' service. This configuration
4744/// // takes precedence over any set by the transport.
4845/// let echoGet = MethodDescriptor(service: "echo.Echo", method: "Get")
@@ -56,10 +53,15 @@ import Atomics
5653/// let defaultMethodConfiguration = MethodConfiguration(executionPolicy: nil, timeout: seconds(10))
5754/// configuration.method.defaults.setDefaultConfiguration(defaultMethodConfiguration)
5855///
59- /// // Finally create a transport and instantiate the client.
56+ /// // Finally create a transport and instantiate the client, adding an interceptor .
6057/// let inProcessServerTransport = InProcessServerTransport()
6158/// let inProcessClientTransport = InProcessClientTransport(serverTransport: inProcessServerTransport)
62- /// let client = GRPCClient(transport: inProcessClientTransport, configuration: configuration)
59+ ///
60+ /// let client = GRPCClient(
61+ /// transport: inProcessClientTransport,
62+ /// interceptors: [StatsRecordingClientInterceptor()],
63+ /// configuration: configuration
64+ /// )
6365/// ```
6466///
6567/// ## Starting and stopping the client
@@ -101,6 +103,14 @@ public struct GRPCClient: Sendable {
101103 /// The transport which provides a bidirectional communication channel with the server.
102104 private let transport : any ClientTransport
103105
106+ /// A collection of interceptors providing cross-cutting functionality to each accepted RPC.
107+ ///
108+ /// The order in which interceptors are added reflects the order in which they are called. The
109+ /// first interceptor added will be the first interceptor to intercept each request. The last
110+ /// interceptor added will be the final interceptor to intercept each request before calling
111+ /// the appropriate handler.
112+ private let interceptors : [ any ClientInterceptor ]
113+
104114 /// The configuration used by the client.
105115 public let configuration : Configuration
106116
@@ -121,13 +131,23 @@ public struct GRPCClient: Sendable {
121131 case stopped
122132 }
123133
124- /// Creates a new client with the given transport and configuration.
134+ /// Creates a new client with the given transport, interceptors and configuration.
125135 ///
126136 /// - Parameters:
127137 /// - transport: The transport used to establish a communication channel with a server.
138+ /// - interceptors: A collection of interceptors providing cross-cutting functionality to each
139+ /// accepted RPC. The order in which interceptors are added reflects the order in which they
140+ /// are called. The first interceptor added will be the first interceptor to intercept each
141+ /// request. The last interceptor added will be the final interceptor to intercept each
142+ /// request before calling the appropriate handler.
128143 /// - configuration: Configuration for the client.
129- public init ( transport: some ClientTransport , configuration: Configuration = Configuration ( ) ) {
144+ public init (
145+ transport: some ClientTransport ,
146+ interceptors: [ any ClientInterceptor ] = [ ] ,
147+ configuration: Configuration = Configuration ( )
148+ ) {
130149 self . transport = transport
150+ self . interceptors = interceptors
131151 self . configuration = configuration
132152 self . state = ManagedAtomic ( . notStarted)
133153 }
@@ -250,7 +270,7 @@ public struct GRPCClient: Sendable {
250270 deserializer: some MessageDeserializer < Response > ,
251271 handler: @Sendable @escaping ( ClientResponse . Single < Response > ) async throws -> ReturnValue
252272 ) async throws -> ReturnValue {
253- try await bidirectionalStreaming (
273+ try await self . bidirectionalStreaming (
254274 request: ClientRequest . Stream ( single: request) ,
255275 descriptor: descriptor,
256276 serializer: serializer,
@@ -278,7 +298,7 @@ public struct GRPCClient: Sendable {
278298 deserializer: some MessageDeserializer < Response > ,
279299 handler: @Sendable @escaping ( ClientResponse . Single < Response > ) async throws -> ReturnValue
280300 ) async throws -> ReturnValue {
281- try await bidirectionalStreaming (
301+ try await self . bidirectionalStreaming (
282302 request: request,
283303 descriptor: descriptor,
284304 serializer: serializer,
@@ -306,7 +326,7 @@ public struct GRPCClient: Sendable {
306326 deserializer: some MessageDeserializer < Response > ,
307327 handler: @Sendable @escaping ( ClientResponse . Stream < Response > ) async throws -> ReturnValue
308328 ) async throws -> ReturnValue {
309- try await bidirectionalStreaming (
329+ try await self . bidirectionalStreaming (
310330 request: ClientRequest . Stream ( single: request) ,
311331 descriptor: descriptor,
312332 serializer: serializer,
@@ -336,13 +356,10 @@ public struct GRPCClient: Sendable {
336356 handler: @Sendable @escaping ( ClientResponse . Stream < Response > ) async throws -> ReturnValue
337357 ) async throws -> ReturnValue {
338358 switch self . state. load ( ordering: . sequentiallyConsistent) {
339- case . running:
359+ case . notStarted, . running:
360+ // Allow .notStarted as making a request can race with 'run()'. Transports should tolerate
361+ // queuing the request if not yet started.
340362 ( )
341- case . notStarted:
342- throw ClientError (
343- code: . clientIsNotRunning,
344- message: " Client must be running to make an RPC: call run() first. "
345- )
346363 case . stopping, . stopped:
347364 throw ClientError (
348365 code: . clientIsStopped,
@@ -357,7 +374,7 @@ public struct GRPCClient: Sendable {
357374 serializer: serializer,
358375 deserializer: deserializer,
359376 transport: self . transport,
360- interceptors: self . configuration . interceptors. values ,
377+ interceptors: self . interceptors,
361378 handler: handler
362379 )
363380 }
@@ -383,14 +400,6 @@ public struct GRPCClient: Sendable {
383400@available ( macOS 13 . 0 , iOS 16 . 0 , watchOS 9 . 0 , tvOS 16 . 0 , * )
384401extension GRPCClient {
385402 public struct Configuration : Sendable {
386- /// A collection of interceptors providing cross-cutting functionality to each accepted RPC.
387- ///
388- /// The order in which interceptors are added reflects the order in which they are called. The
389- /// first interceptor added will be the first interceptor to intercept each request. The last
390- /// interceptor added will be the final interceptor to intercept each request before calling
391- /// the appropriate handler.
392- public var interceptors : Interceptors
393-
394403 /// Configuration for how methods are executed.
395404 ///
396405 /// Method configuration determines how each RPC is executed by the client. Some services and
@@ -401,48 +410,13 @@ extension GRPCClient {
401410
402411 /// Creates a new default configuration.
403412 public init ( ) {
404- self . interceptors = Interceptors ( )
405413 self . method = Method ( )
406414 }
407415 }
408416}
409417
410418@available ( macOS 13 . 0 , iOS 16 . 0 , watchOS 9 . 0 , tvOS 16 . 0 , * )
411419extension GRPCClient . Configuration {
412- /// A collection of ``ClientInterceptor`` implementations which are applied to all accepted
413- /// RPCs.
414- ///
415- /// RPCs are intercepted in the order that interceptors are added. That is, a request sent from the client to
416- /// the server will first be intercepted by the first added interceptor followed by the second, and so on.
417- /// For responses from the server, they'll be applied in the opposite order.
418- public struct Interceptors : Sendable {
419- private( set) var values : [ any ClientInterceptor ] = [ ]
420-
421- /// Add an interceptor to the client.
422- ///
423- /// The order in which interceptors are added reflects the order in which they are called. The
424- /// first interceptor added will be the first interceptor to intercept each request. The last
425- /// interceptor added will be the final interceptor to intercept each request before calling
426- /// the appropriate handler.
427- ///
428- /// - Parameter interceptor: The interceptor to add.
429- public mutating func add( _ interceptor: some ClientInterceptor ) {
430- self . values. append ( interceptor)
431- }
432-
433- /// Adds a sequence of interceptor to the client.
434- ///
435- /// The order in which interceptors are added reflects the order in which they are called. The
436- /// first interceptor added will be the first interceptor to intercept each request. The last
437- /// interceptor added will be the final interceptor to intercept each request before calling
438- /// the appropriate handler.
439- ///
440- /// - Parameter interceptors: The interceptors to add.
441- public mutating func add( contentsOf interceptors: some Sequence < ClientInterceptor > ) {
442- self . values. append ( contentsOf: interceptors)
443- }
444- }
445-
446420 /// Configuration for how methods should be executed.
447421 ///
448422 /// In most cases the client should defer to the configuration provided by the transport as this
@@ -464,10 +438,3 @@ extension GRPCClient.Configuration {
464438 }
465439 }
466440}
467-
468- @available ( macOS 13 . 0 , iOS 16 . 0 , watchOS 9 . 0 , tvOS 16 . 0 , * )
469- extension GRPCClient . Configuration . Interceptors : CustomStringConvertible {
470- public var description : String {
471- return String ( describing: self . values. map { String ( describing: type ( of: $0) ) } )
472- }
473- }
0 commit comments