From 5152e8f447b6924e5fee744867792dcceefe1b54 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 4 Dec 2024 15:10:19 +0000 Subject: [PATCH] Separate out the security config from the rest of the transport config (#37) Motivation: Users must explicitly state whether they want TLS or plaintext. This config for this is currently nested within the rest of the config for the transport. In many cases this results in users writing `.defaults(transportSecurity: .tls(.defaults(...)))` where `.defaults(...)` often includes various TLS config. This is verbose and a little bit finicky to use. Modifications: - Seprate out the transport security from the rest of the config and have the client and server transport take a config _and_ a transport security config. - This allows for APIs which compose better while also allowing for the transport config to just use a set of defaults. Result: Easier to use API. --- .../InteroperabilityTestsExecutable.swift | 6 +- .../Sources/BenchmarkClient.swift | 6 +- .../grpc_testing_benchmark_service.grpc.swift | 1273 ++++++++++++----- .../grpc_testing_worker_service.grpc.swift | 465 ++++-- .../Sources/PerformanceWorker.swift | 2 +- .../Sources/WorkerService.swift | 5 +- .../Config+TLS.swift | 84 +- .../HTTP2ClientTransport+Posix.swift | 45 +- .../HTTP2ServerTransport+Posix.swift | 38 +- .../NIOSSL+GRPC.swift | 4 +- .../Config+TLS.swift | 77 +- ...TP2ClientTransport+TransportServices.swift | 46 +- ...TP2ServerTransport+TransportServices.swift | 33 +- .../ClientConnectionHandlerTests.swift | 4 + .../ControlClient.swift | 12 +- .../HTTP2TransportNIOPosixTests.swift | 36 +- ...P2TransportNIOTransportServicesTests.swift | 22 +- .../HTTP2TransportTLSEnabledTests.swift | 132 +- .../HTTP2TransportTests.swift | 17 +- 19 files changed, 1702 insertions(+), 605 deletions(-) diff --git a/IntegrationTests/grpc-interop-tests/Sources/InteroperabilityTestsExecutable.swift b/IntegrationTests/grpc-interop-tests/Sources/InteroperabilityTestsExecutable.swift index 9311e79..f984c42 100644 --- a/IntegrationTests/grpc-interop-tests/Sources/InteroperabilityTestsExecutable.swift +++ b/IntegrationTests/grpc-interop-tests/Sources/InteroperabilityTestsExecutable.swift @@ -41,7 +41,8 @@ struct InteroperabilityTestsExecutable: AsyncParsableCommand { let server = GRPCServer( transport: .http2NIOPosix( address: .ipv4(host: self.host, port: self.port), - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.enabledAlgorithms = .all } ), @@ -115,7 +116,8 @@ struct InteroperabilityTestsExecutable: AsyncParsableCommand { return GRPCClient( transport: try .http2NIOPosix( target: .ipv4(host: host, port: port), - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.enabledAlgorithms = .all }, serviceConfig: serviceConfig diff --git a/IntegrationTests/grpc-performance-tests/Sources/BenchmarkClient.swift b/IntegrationTests/grpc-performance-tests/Sources/BenchmarkClient.swift index f5f4c45..402dde1 100644 --- a/IntegrationTests/grpc-performance-tests/Sources/BenchmarkClient.swift +++ b/IntegrationTests/grpc-performance-tests/Sources/BenchmarkClient.swift @@ -92,7 +92,7 @@ final class BenchmarkClient: Sendable { } internal func run() async throws { - let benchmarkClient = Grpc_Testing_BenchmarkService_Client(wrapping: self.client) + let benchmarkClient = Grpc_Testing_BenchmarkService.Client(wrapping: self.client) return try await withThrowingTaskGroup(of: Void.self) { clientGroup in // Start the client. clientGroup.addTask { @@ -148,7 +148,7 @@ final class BenchmarkClient: Sendable { return (result, nanoseconds: Double(endTime - startTime)) } - private func unary(benchmark: Grpc_Testing_BenchmarkService_Client) async { + private func unary(benchmark: Grpc_Testing_BenchmarkService.Client) async { let (errorCode, nanoseconds): (RPCError.Code?, Double) = await self.timeIt { do { try await benchmark.unaryCall(request: ClientRequest(message: self.message)) { @@ -165,7 +165,7 @@ final class BenchmarkClient: Sendable { self.record(latencyNanos: nanoseconds, errorCode: errorCode) } - private func streaming(benchmark: Grpc_Testing_BenchmarkService_Client) async { + private func streaming(benchmark: Grpc_Testing_BenchmarkService.Client) async { // Streaming RPCs ping-pong messages back and forth. To achieve this the response message // stream is sent to the request closure, and the request closure indicates the outcome back // to the response handler to keep the RPC alive for the appropriate amount of time. diff --git a/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_benchmark_service.grpc.swift b/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_benchmark_service.grpc.swift index 933d1ec..8e54201 100644 --- a/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_benchmark_service.grpc.swift +++ b/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_benchmark_service.grpc.swift @@ -26,50 +26,77 @@ import GRPCCore import GRPCProtobuf +import SwiftProtobuf +// MARK: - grpc.testing.BenchmarkService + +/// Namespace containing generated types for the "grpc.testing.BenchmarkService" service. internal enum Grpc_Testing_BenchmarkService { - internal static let descriptor = GRPCCore.ServiceDescriptor.grpc_testing_BenchmarkService + /// Service descriptor for the "grpc.testing.BenchmarkService" service. + internal static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService") + /// Namespace for method metadata. internal enum Method { + /// Namespace for "UnaryCall" metadata. internal enum UnaryCall { + /// Request type for "UnaryCall". internal typealias Input = Grpc_Testing_SimpleRequest + /// Response type for "UnaryCall". internal typealias Output = Grpc_Testing_SimpleResponse + /// Descriptor for "UnaryCall". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_BenchmarkService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService"), method: "UnaryCall" ) } + /// Namespace for "StreamingCall" metadata. internal enum StreamingCall { + /// Request type for "StreamingCall". internal typealias Input = Grpc_Testing_SimpleRequest + /// Response type for "StreamingCall". internal typealias Output = Grpc_Testing_SimpleResponse + /// Descriptor for "StreamingCall". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_BenchmarkService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService"), method: "StreamingCall" ) } + /// Namespace for "StreamingFromClient" metadata. internal enum StreamingFromClient { + /// Request type for "StreamingFromClient". internal typealias Input = Grpc_Testing_SimpleRequest + /// Response type for "StreamingFromClient". internal typealias Output = Grpc_Testing_SimpleResponse + /// Descriptor for "StreamingFromClient". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_BenchmarkService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService"), method: "StreamingFromClient" ) } + /// Namespace for "StreamingFromServer" metadata. internal enum StreamingFromServer { + /// Request type for "StreamingFromServer". internal typealias Input = Grpc_Testing_SimpleRequest + /// Response type for "StreamingFromServer". internal typealias Output = Grpc_Testing_SimpleResponse + /// Descriptor for "StreamingFromServer". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_BenchmarkService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService"), method: "StreamingFromServer" ) } + /// Namespace for "StreamingBothWays" metadata. internal enum StreamingBothWays { + /// Request type for "StreamingBothWays". internal typealias Input = Grpc_Testing_SimpleRequest + /// Response type for "StreamingBothWays". internal typealias Output = Grpc_Testing_SimpleResponse + /// Descriptor for "StreamingBothWays". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_BenchmarkService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService"), method: "StreamingBothWays" ) } + /// Descriptors for all methods in the "grpc.testing.BenchmarkService" service. internal static let descriptors: [GRPCCore.MethodDescriptor] = [ UnaryCall.descriptor, StreamingCall.descriptor, @@ -78,66 +105,338 @@ internal enum Grpc_Testing_BenchmarkService { StreamingBothWays.descriptor ] } - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias StreamingServiceProtocol = Grpc_Testing_BenchmarkService_StreamingServiceProtocol - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias ServiceProtocol = Grpc_Testing_BenchmarkService_ServiceProtocol - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias ClientProtocol = Grpc_Testing_BenchmarkService_ClientProtocol - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias Client = Grpc_Testing_BenchmarkService_Client } extension GRPCCore.ServiceDescriptor { - internal static let grpc_testing_BenchmarkService = Self( - package: "grpc.testing", - service: "BenchmarkService" - ) + /// Service descriptor for the "grpc.testing.BenchmarkService" service. + internal static let grpc_testing_BenchmarkService = GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.BenchmarkService") } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal protocol Grpc_Testing_BenchmarkService_StreamingServiceProtocol: GRPCCore.RegistrableRPCService { - /// One request followed by one response. - /// The server returns the client payload as-is. - func unaryCall( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Repeated sequence of one request followed by one response. - /// Should be called streaming ping-pong - /// The server returns the client payload as-is on each response - func streamingCall( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Single-sided unbounded streaming from client to server - /// The server returns the client payload as-is once the client does WritesDone - func streamingFromClient( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Single-sided unbounded streaming from server to client - /// The server repeatedly returns the client payload as-is - func streamingFromServer( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Two-sided unbounded streaming between server to client - /// Both sides send the content of their own choice to the other - func streamingBothWays( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse +// MARK: grpc.testing.BenchmarkService (server) + +extension Grpc_Testing_BenchmarkService { + /// Streaming variant of the service protocol for the "grpc.testing.BenchmarkService" service. + /// + /// This protocol is the lowest-level of the service protocols generated for this service + /// giving you the most flexibility over the implementation of your service. This comes at + /// the cost of more verbose and less strict APIs. Each RPC requires you to implement it in + /// terms of a request stream and response stream. Where only a single request or response + /// message is expected, you are responsible for enforcing this invariant is maintained. + /// + /// Where possible, prefer using the stricter, less-verbose ``ServiceProtocol`` + /// or ``SimpleServiceProtocol`` instead. + internal protocol StreamingServiceProtocol: GRPCCore.RegistrableRPCService { + /// Handle the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func unaryCall( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingCall( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingFromClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingFromServer( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingBothWays( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Service protocol for the "grpc.testing.BenchmarkService" service. + /// + /// This protocol is higher level than ``StreamingServiceProtocol`` but lower level than + /// the ``SimpleServiceProtocol``, it provides access to request and response metadata and + /// trailing response metadata. If you don't need these then consider using + /// the ``SimpleServiceProtocol``. If you need fine grained control over your RPCs then + /// use ``StreamingServiceProtocol``. + internal protocol ServiceProtocol: Grpc_Testing_BenchmarkService.StreamingServiceProtocol { + /// Handle the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A response containing a single `Grpc_Testing_SimpleResponse` message. + func unaryCall( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + + /// Handle the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingCall( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A response containing a single `Grpc_Testing_SimpleResponse` message. + func streamingFromClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + + /// Handle the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingFromServer( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_SimpleResponse` messages. + func streamingBothWays( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Simple service protocol for the "grpc.testing.BenchmarkService" service. + /// + /// This is the highest level protocol for the service. The API is the easiest to use but + /// doesn't provide access to request or response metadata. If you need access to these + /// then use ``ServiceProtocol`` instead. + internal protocol SimpleServiceProtocol: Grpc_Testing_BenchmarkService.ServiceProtocol { + /// Handle the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A `Grpc_Testing_SimpleRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A `Grpc_Testing_SimpleResponse` to respond with. + func unaryCall( + request: Grpc_Testing_SimpleRequest, + context: GRPCCore.ServerContext + ) async throws -> Grpc_Testing_SimpleResponse + + /// Handle the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A stream of `Grpc_Testing_SimpleRequest` messages. + /// - response: A response stream of `Grpc_Testing_SimpleResponse` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + func streamingCall( + request: GRPCCore.RPCAsyncSequence, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + + /// Handle the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A stream of `Grpc_Testing_SimpleRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A `Grpc_Testing_SimpleResponse` to respond with. + func streamingFromClient( + request: GRPCCore.RPCAsyncSequence, + context: GRPCCore.ServerContext + ) async throws -> Grpc_Testing_SimpleResponse + + /// Handle the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A `Grpc_Testing_SimpleRequest` message. + /// - response: A response stream of `Grpc_Testing_SimpleResponse` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + func streamingFromServer( + request: Grpc_Testing_SimpleRequest, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + + /// Handle the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A stream of `Grpc_Testing_SimpleRequest` messages. + /// - response: A response stream of `Grpc_Testing_SimpleResponse` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + func streamingBothWays( + request: GRPCCore.RPCAsyncSequence, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + } } -/// Conformance to `GRPCCore.RegistrableRPCService`. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Default implementation of 'registerMethods(with:)'. extension Grpc_Testing_BenchmarkService.StreamingServiceProtocol { - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) internal func registerMethods(with router: inout GRPCCore.RPCRouter) { router.registerHandler( forMethod: Grpc_Testing_BenchmarkService.Method.UnaryCall.descriptor, @@ -197,47 +496,7 @@ extension Grpc_Testing_BenchmarkService.StreamingServiceProtocol { } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal protocol Grpc_Testing_BenchmarkService_ServiceProtocol: Grpc_Testing_BenchmarkService.StreamingServiceProtocol { - /// One request followed by one response. - /// The server returns the client payload as-is. - func unaryCall( - request: GRPCCore.ServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.ServerResponse - - /// Repeated sequence of one request followed by one response. - /// Should be called streaming ping-pong - /// The server returns the client payload as-is on each response - func streamingCall( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Single-sided unbounded streaming from client to server - /// The server returns the client payload as-is once the client does WritesDone - func streamingFromClient( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.ServerResponse - - /// Single-sided unbounded streaming from server to client - /// The server repeatedly returns the client payload as-is - func streamingFromServer( - request: GRPCCore.ServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Two-sided unbounded streaming between server to client - /// Both sides send the content of their own choice to the other - func streamingBothWays( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse -} - -/// Partial conformance to `Grpc_Testing_BenchmarkService_StreamingServiceProtocol`. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Default implementation of streaming methods from 'StreamingServiceProtocol'. extension Grpc_Testing_BenchmarkService.ServiceProtocol { internal func unaryCall( request: GRPCCore.StreamingServerRequest, @@ -249,7 +508,7 @@ extension Grpc_Testing_BenchmarkService.ServiceProtocol { ) return GRPCCore.StreamingServerResponse(single: response) } - + internal func streamingFromClient( request: GRPCCore.StreamingServerRequest, context: GRPCCore.ServerContext @@ -260,7 +519,7 @@ extension Grpc_Testing_BenchmarkService.ServiceProtocol { ) return GRPCCore.StreamingServerResponse(single: response) } - + internal func streamingFromServer( request: GRPCCore.StreamingServerRequest, context: GRPCCore.ServerContext @@ -273,147 +532,575 @@ extension Grpc_Testing_BenchmarkService.ServiceProtocol { } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal protocol Grpc_Testing_BenchmarkService_ClientProtocol: Sendable { - /// One request followed by one response. - /// The server returns the client payload as-is. - func unaryCall( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable - - /// Repeated sequence of one request followed by one response. - /// Should be called streaming ping-pong - /// The server returns the client payload as-is on each response - func streamingCall( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable - - /// Single-sided unbounded streaming from client to server - /// The server returns the client payload as-is once the client does WritesDone - func streamingFromClient( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R - ) async throws -> R where R: Sendable - - /// Single-sided unbounded streaming from server to client - /// The server repeatedly returns the client payload as-is - func streamingFromServer( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable - - /// Two-sided unbounded streaming between server to client - /// Both sides send the content of their own choice to the other - func streamingBothWays( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable +// Default implementation of methods from 'ServiceProtocol'. +extension Grpc_Testing_BenchmarkService.SimpleServiceProtocol { + internal func unaryCall( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.unaryCall( + request: request.message, + context: context + ), + metadata: [:] + ) + } + + internal func streamingCall( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.streamingCall( + request: request.messages, + response: writer, + context: context + ) + return [:] + } + ) + } + + internal func streamingFromClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.streamingFromClient( + request: request.messages, + context: context + ), + metadata: [:] + ) + } + + internal func streamingFromServer( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.streamingFromServer( + request: request.message, + response: writer, + context: context + ) + return [:] + } + ) + } + + internal func streamingBothWays( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.streamingBothWays( + request: request.messages, + response: writer, + context: context + ) + return [:] + } + ) + } +} + +// MARK: grpc.testing.BenchmarkService (client) + +extension Grpc_Testing_BenchmarkService { + /// Generated client protocol for the "grpc.testing.BenchmarkService" service. + /// + /// You don't need to implement this protocol directly, use the generated + /// implementation, ``Client``. + internal protocol ClientProtocol: Sendable { + /// Call the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func unaryCall( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + + /// Call the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func streamingCall( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + + /// Call the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func streamingFromClient( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + + /// Call the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func streamingFromServer( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + + /// Call the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + func streamingBothWays( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable + } + + /// Generated client for the "grpc.testing.BenchmarkService" service. + /// + /// The ``Client`` provides an implementation of ``ClientProtocol`` which wraps + /// a `GRPCCore.GRPCCClient`. The underlying `GRPCClient` provides the long-lived + /// means of communication with the remote peer. + internal struct Client: ClientProtocol { + private let client: GRPCCore.GRPCClient + + /// Creates a new client wrapping the provided `GRPCCore.GRPCClient`. + /// + /// - Parameters: + /// - client: A `GRPCCore.GRPCClient` providing a communication channel to the service. + internal init(wrapping client: GRPCCore.GRPCClient) { + self.client = client + } + + /// Call the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func unaryCall( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + try await self.client.unary( + request: request, + descriptor: Grpc_Testing_BenchmarkService.Method.UnaryCall.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingCall( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + try await self.client.bidirectionalStreaming( + request: request, + descriptor: Grpc_Testing_BenchmarkService.Method.StreamingCall.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingFromClient( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + try await self.client.clientStreaming( + request: request, + descriptor: Grpc_Testing_BenchmarkService.Method.StreamingFromClient.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingFromServer( + request: GRPCCore.ClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + try await self.client.serverStreaming( + request: request, + descriptor: Grpc_Testing_BenchmarkService.Method.StreamingFromServer.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - serializer: A serializer for `Grpc_Testing_SimpleRequest` messages. + /// - deserializer: A deserializer for `Grpc_Testing_SimpleResponse` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingBothWays( + request: GRPCCore.StreamingClientRequest, + serializer: some GRPCCore.MessageSerializer, + deserializer: some GRPCCore.MessageDeserializer, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + try await self.client.bidirectionalStreaming( + request: request, + descriptor: Grpc_Testing_BenchmarkService.Method.StreamingBothWays.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Helpers providing default arguments to 'ClientProtocol' methods. extension Grpc_Testing_BenchmarkService.ClientProtocol { - internal func unaryCall( + /// Call the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func unaryCall( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.unaryCall( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } - - internal func streamingCall( + + /// Call the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingCall( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.streamingCall( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } - - internal func streamingFromClient( + + /// Call the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingFromClient( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } - ) async throws -> R where R: Sendable { + ) async throws -> Result where Result: Sendable { try await self.streamingFromClient( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } - - internal func streamingFromServer( + + /// Call the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_SimpleRequest` message. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingFromServer( request: GRPCCore.ClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.streamingFromServer( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } - - internal func streamingBothWays( + + /// Call the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - request: A streaming request producing `Grpc_Testing_SimpleRequest` messages. + /// - options: Options to apply to this RPC. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. + internal func streamingBothWays( request: GRPCCore.StreamingClientRequest, options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { try await self.streamingBothWays( request: request, serializer: GRPCProtobuf.ProtobufSerializer(), deserializer: GRPCProtobuf.ProtobufDeserializer(), options: options, - body + onResponse: handleResponse ) } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Helpers providing sugared APIs for 'ClientProtocol' methods. extension Grpc_Testing_BenchmarkService.ClientProtocol { - /// One request followed by one response. - /// The server returns the client payload as-is. + /// Call the "UnaryCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > One request followed by one response. + /// > The server returns the client payload as-is. + /// + /// - Parameters: + /// - message: request message to send. + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. internal func unaryCall( _ message: Grpc_Testing_SimpleRequest, metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.ClientRequest( @@ -423,53 +1110,94 @@ extension Grpc_Testing_BenchmarkService.ClientProtocol { return try await self.unaryCall( request: request, options: options, - handleResponse + onResponse: handleResponse ) } - - /// Repeated sequence of one request followed by one response. - /// Should be called streaming ping-pong - /// The server returns the client payload as-is on each response + + /// Call the "StreamingCall" method. + /// + /// > Source IDL Documentation: + /// > + /// > Repeated sequence of one request followed by one response. + /// > Should be called streaming ping-pong + /// > The server returns the client payload as-is on each response + /// + /// - Parameters: + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - producer: A closure producing request messages to send to the server. The request + /// stream is closed when the closure returns. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. internal func streamingCall( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.streamingCall( request: request, options: options, - handleResponse + onResponse: handleResponse ) } - - /// Single-sided unbounded streaming from client to server - /// The server returns the client payload as-is once the client does WritesDone + + /// Call the "StreamingFromClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from client to server + /// > The server returns the client payload as-is once the client does WritesDone + /// + /// - Parameters: + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - producer: A closure producing request messages to send to the server. The request + /// stream is closed when the closure returns. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. internal func streamingFromClient( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, - onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { - try $0.message + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message } ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.streamingFromClient( request: request, options: options, - handleResponse + onResponse: handleResponse ) } - - /// Single-sided unbounded streaming from server to client - /// The server repeatedly returns the client payload as-is + + /// Call the "StreamingFromServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Single-sided unbounded streaming from server to client + /// > The server repeatedly returns the client payload as-is + /// + /// - Parameters: + /// - message: request message to send. + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. internal func streamingFromServer( _ message: Grpc_Testing_SimpleRequest, metadata: GRPCCore.Metadata = [:], @@ -483,135 +1211,40 @@ extension Grpc_Testing_BenchmarkService.ClientProtocol { return try await self.streamingFromServer( request: request, options: options, - handleResponse + onResponse: handleResponse ) } - - /// Two-sided unbounded streaming between server to client - /// Both sides send the content of their own choice to the other + + /// Call the "StreamingBothWays" method. + /// + /// > Source IDL Documentation: + /// > + /// > Two-sided unbounded streaming between server to client + /// > Both sides send the content of their own choice to the other + /// + /// - Parameters: + /// - metadata: Additional metadata to send, defaults to empty. + /// - options: Options to apply to this RPC, defaults to `.defaults`. + /// - producer: A closure producing request messages to send to the server. The request + /// stream is closed when the closure returns. + /// - handleResponse: A closure which handles the response, the result of which is + /// returned to the caller. Returning from the closure will cancel the RPC if it + /// hasn't already finished. + /// - Returns: The result of `handleResponse`. internal func streamingBothWays( metadata: GRPCCore.Metadata = [:], options: GRPCCore.CallOptions = .defaults, - requestProducer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, + requestProducer producer: @Sendable @escaping (GRPCCore.RPCWriter) async throws -> Void, onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result ) async throws -> Result where Result: Sendable { let request = GRPCCore.StreamingClientRequest( metadata: metadata, - producer: requestProducer + producer: producer ) return try await self.streamingBothWays( request: request, options: options, - handleResponse - ) - } -} - -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal struct Grpc_Testing_BenchmarkService_Client: Grpc_Testing_BenchmarkService.ClientProtocol { - private let client: GRPCCore.GRPCClient - - internal init(wrapping client: GRPCCore.GRPCClient) { - self.client = client - } - - /// One request followed by one response. - /// The server returns the client payload as-is. - internal func unaryCall( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message - } - ) async throws -> R where R: Sendable { - try await self.client.unary( - request: request, - descriptor: Grpc_Testing_BenchmarkService.Method.UnaryCall.descriptor, - serializer: serializer, - deserializer: deserializer, - options: options, - handler: body - ) - } - - /// Repeated sequence of one request followed by one response. - /// Should be called streaming ping-pong - /// The server returns the client payload as-is on each response - internal func streamingCall( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { - try await self.client.bidirectionalStreaming( - request: request, - descriptor: Grpc_Testing_BenchmarkService.Method.StreamingCall.descriptor, - serializer: serializer, - deserializer: deserializer, - options: options, - handler: body - ) - } - - /// Single-sided unbounded streaming from client to server - /// The server returns the client payload as-is once the client does WritesDone - internal func streamingFromClient( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> R = { - try $0.message - } - ) async throws -> R where R: Sendable { - try await self.client.clientStreaming( - request: request, - descriptor: Grpc_Testing_BenchmarkService.Method.StreamingFromClient.descriptor, - serializer: serializer, - deserializer: deserializer, - options: options, - handler: body - ) - } - - /// Single-sided unbounded streaming from server to client - /// The server repeatedly returns the client payload as-is - internal func streamingFromServer( - request: GRPCCore.ClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { - try await self.client.serverStreaming( - request: request, - descriptor: Grpc_Testing_BenchmarkService.Method.StreamingFromServer.descriptor, - serializer: serializer, - deserializer: deserializer, - options: options, - handler: body - ) - } - - /// Two-sided unbounded streaming between server to client - /// Both sides send the content of their own choice to the other - internal func streamingBothWays( - request: GRPCCore.StreamingClientRequest, - serializer: some GRPCCore.MessageSerializer, - deserializer: some GRPCCore.MessageDeserializer, - options: GRPCCore.CallOptions = .defaults, - _ body: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> R - ) async throws -> R where R: Sendable { - try await self.client.bidirectionalStreaming( - request: request, - descriptor: Grpc_Testing_BenchmarkService.Method.StreamingBothWays.descriptor, - serializer: serializer, - deserializer: deserializer, - options: options, - handler: body + onResponse: handleResponse ) } } \ No newline at end of file diff --git a/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_worker_service.grpc.swift b/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_worker_service.grpc.swift index a3df96f..df08581 100644 --- a/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_worker_service.grpc.swift +++ b/IntegrationTests/grpc-performance-tests/Sources/Generated/grpc_testing_worker_service.grpc.swift @@ -26,42 +26,65 @@ import GRPCCore import GRPCProtobuf +import SwiftProtobuf +// MARK: - grpc.testing.WorkerService + +/// Namespace containing generated types for the "grpc.testing.WorkerService" service. internal enum Grpc_Testing_WorkerService { - internal static let descriptor = GRPCCore.ServiceDescriptor.grpc_testing_WorkerService + /// Service descriptor for the "grpc.testing.WorkerService" service. + internal static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService") + /// Namespace for method metadata. internal enum Method { + /// Namespace for "RunServer" metadata. internal enum RunServer { + /// Request type for "RunServer". internal typealias Input = Grpc_Testing_ServerArgs + /// Response type for "RunServer". internal typealias Output = Grpc_Testing_ServerStatus + /// Descriptor for "RunServer". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_WorkerService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService"), method: "RunServer" ) } + /// Namespace for "RunClient" metadata. internal enum RunClient { + /// Request type for "RunClient". internal typealias Input = Grpc_Testing_ClientArgs + /// Response type for "RunClient". internal typealias Output = Grpc_Testing_ClientStatus + /// Descriptor for "RunClient". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_WorkerService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService"), method: "RunClient" ) } + /// Namespace for "CoreCount" metadata. internal enum CoreCount { + /// Request type for "CoreCount". internal typealias Input = Grpc_Testing_CoreRequest + /// Response type for "CoreCount". internal typealias Output = Grpc_Testing_CoreResponse + /// Descriptor for "CoreCount". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_WorkerService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService"), method: "CoreCount" ) } + /// Namespace for "QuitWorker" metadata. internal enum QuitWorker { + /// Request type for "QuitWorker". internal typealias Input = Grpc_Testing_Void + /// Response type for "QuitWorker". internal typealias Output = Grpc_Testing_Void + /// Descriptor for "QuitWorker". internal static let descriptor = GRPCCore.MethodDescriptor( - service: Grpc_Testing_WorkerService.descriptor.fullyQualifiedService, + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService"), method: "QuitWorker" ) } + /// Descriptors for all methods in the "grpc.testing.WorkerService" service. internal static let descriptors: [GRPCCore.MethodDescriptor] = [ RunServer.descriptor, RunClient.descriptor, @@ -69,60 +92,295 @@ internal enum Grpc_Testing_WorkerService { QuitWorker.descriptor ] } - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias StreamingServiceProtocol = Grpc_Testing_WorkerService_StreamingServiceProtocol - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) - internal typealias ServiceProtocol = Grpc_Testing_WorkerService_ServiceProtocol } extension GRPCCore.ServiceDescriptor { - internal static let grpc_testing_WorkerService = Self( - package: "grpc.testing", - service: "WorkerService" - ) + /// Service descriptor for the "grpc.testing.WorkerService" service. + internal static let grpc_testing_WorkerService = GRPCCore.ServiceDescriptor(fullyQualifiedService: "grpc.testing.WorkerService") } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal protocol Grpc_Testing_WorkerService_StreamingServiceProtocol: GRPCCore.RegistrableRPCService { - /// Start server with specified workload. - /// First request sent specifies the ServerConfig followed by ServerStatus - /// response. After that, a "Mark" can be sent anytime to request the latest - /// stats. Closing the stream will initiate shutdown of the test server - /// and once the shutdown has finished, the OK status is sent to terminate - /// this RPC. - func runServer( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Start client with specified workload. - /// First request sent specifies the ClientConfig followed by ClientStatus - /// response. After that, a "Mark" can be sent anytime to request the latest - /// stats. Closing the stream will initiate shutdown of the test client - /// and once the shutdown has finished, the OK status is sent to terminate - /// this RPC. - func runClient( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Just return the core count - unary call - func coreCount( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Quit this worker - func quitWorker( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse +// MARK: grpc.testing.WorkerService (server) + +extension Grpc_Testing_WorkerService { + /// Streaming variant of the service protocol for the "grpc.testing.WorkerService" service. + /// + /// This protocol is the lowest-level of the service protocols generated for this service + /// giving you the most flexibility over the implementation of your service. This comes at + /// the cost of more verbose and less strict APIs. Each RPC requires you to implement it in + /// terms of a request stream and response stream. Where only a single request or response + /// message is expected, you are responsible for enforcing this invariant is maintained. + /// + /// Where possible, prefer using the stricter, less-verbose ``ServiceProtocol`` + /// or ``SimpleServiceProtocol`` instead. + internal protocol StreamingServiceProtocol: GRPCCore.RegistrableRPCService { + /// Handle the "RunServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start server with specified workload. + /// > First request sent specifies the ServerConfig followed by ServerStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test server + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_ServerArgs` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_ServerStatus` messages. + func runServer( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "RunClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start client with specified workload. + /// > First request sent specifies the ClientConfig followed by ClientStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test client + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_ClientArgs` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_ClientStatus` messages. + func runClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "CoreCount" method. + /// + /// > Source IDL Documentation: + /// > + /// > Just return the core count - unary call + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_CoreRequest` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_CoreResponse` messages. + func coreCount( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "QuitWorker" method. + /// + /// > Source IDL Documentation: + /// > + /// > Quit this worker + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_Void` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_Void` messages. + func quitWorker( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Service protocol for the "grpc.testing.WorkerService" service. + /// + /// This protocol is higher level than ``StreamingServiceProtocol`` but lower level than + /// the ``SimpleServiceProtocol``, it provides access to request and response metadata and + /// trailing response metadata. If you don't need these then consider using + /// the ``SimpleServiceProtocol``. If you need fine grained control over your RPCs then + /// use ``StreamingServiceProtocol``. + internal protocol ServiceProtocol: Grpc_Testing_WorkerService.StreamingServiceProtocol { + /// Handle the "RunServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start server with specified workload. + /// > First request sent specifies the ServerConfig followed by ServerStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test server + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_ServerArgs` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_ServerStatus` messages. + func runServer( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "RunClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start client with specified workload. + /// > First request sent specifies the ClientConfig followed by ClientStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test client + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A streaming request of `Grpc_Testing_ClientArgs` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A streaming response of `Grpc_Testing_ClientStatus` messages. + func runClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "CoreCount" method. + /// + /// > Source IDL Documentation: + /// > + /// > Just return the core count - unary call + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_CoreRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A response containing a single `Grpc_Testing_CoreResponse` message. + func coreCount( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + + /// Handle the "QuitWorker" method. + /// + /// > Source IDL Documentation: + /// > + /// > Quit this worker + /// + /// - Parameters: + /// - request: A request containing a single `Grpc_Testing_Void` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A response containing a single `Grpc_Testing_Void` message. + func quitWorker( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + } + + /// Simple service protocol for the "grpc.testing.WorkerService" service. + /// + /// This is the highest level protocol for the service. The API is the easiest to use but + /// doesn't provide access to request or response metadata. If you need access to these + /// then use ``ServiceProtocol`` instead. + internal protocol SimpleServiceProtocol: Grpc_Testing_WorkerService.ServiceProtocol { + /// Handle the "RunServer" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start server with specified workload. + /// > First request sent specifies the ServerConfig followed by ServerStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test server + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A stream of `Grpc_Testing_ServerArgs` messages. + /// - response: A response stream of `Grpc_Testing_ServerStatus` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + func runServer( + request: GRPCCore.RPCAsyncSequence, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + + /// Handle the "RunClient" method. + /// + /// > Source IDL Documentation: + /// > + /// > Start client with specified workload. + /// > First request sent specifies the ClientConfig followed by ClientStatus + /// > response. After that, a "Mark" can be sent anytime to request the latest + /// > stats. Closing the stream will initiate shutdown of the test client + /// > and once the shutdown has finished, the OK status is sent to terminate + /// > this RPC. + /// + /// - Parameters: + /// - request: A stream of `Grpc_Testing_ClientArgs` messages. + /// - response: A response stream of `Grpc_Testing_ClientStatus` messages. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + func runClient( + request: GRPCCore.RPCAsyncSequence, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + + /// Handle the "CoreCount" method. + /// + /// > Source IDL Documentation: + /// > + /// > Just return the core count - unary call + /// + /// - Parameters: + /// - request: A `Grpc_Testing_CoreRequest` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A `Grpc_Testing_CoreResponse` to respond with. + func coreCount( + request: Grpc_Testing_CoreRequest, + context: GRPCCore.ServerContext + ) async throws -> Grpc_Testing_CoreResponse + + /// Handle the "QuitWorker" method. + /// + /// > Source IDL Documentation: + /// > + /// > Quit this worker + /// + /// - Parameters: + /// - request: A `Grpc_Testing_Void` message. + /// - context: Context providing information about the RPC. + /// - Throws: Any error which occurred during the processing of the request. Thrown errors + /// of type `RPCError` are mapped to appropriate statuses. All other errors are converted + /// to an internal error. + /// - Returns: A `Grpc_Testing_Void` to respond with. + func quitWorker( + request: Grpc_Testing_Void, + context: GRPCCore.ServerContext + ) async throws -> Grpc_Testing_Void + } } -/// Conformance to `GRPCCore.RegistrableRPCService`. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Default implementation of 'registerMethods(with:)'. extension Grpc_Testing_WorkerService.StreamingServiceProtocol { - @available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) internal func registerMethods(with router: inout GRPCCore.RPCRouter) { router.registerHandler( forMethod: Grpc_Testing_WorkerService.Method.RunServer.descriptor, @@ -171,45 +429,7 @@ extension Grpc_Testing_WorkerService.StreamingServiceProtocol { } } -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) -internal protocol Grpc_Testing_WorkerService_ServiceProtocol: Grpc_Testing_WorkerService.StreamingServiceProtocol { - /// Start server with specified workload. - /// First request sent specifies the ServerConfig followed by ServerStatus - /// response. After that, a "Mark" can be sent anytime to request the latest - /// stats. Closing the stream will initiate shutdown of the test server - /// and once the shutdown has finished, the OK status is sent to terminate - /// this RPC. - func runServer( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Start client with specified workload. - /// First request sent specifies the ClientConfig followed by ClientStatus - /// response. After that, a "Mark" can be sent anytime to request the latest - /// stats. Closing the stream will initiate shutdown of the test client - /// and once the shutdown has finished, the OK status is sent to terminate - /// this RPC. - func runClient( - request: GRPCCore.StreamingServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.StreamingServerResponse - - /// Just return the core count - unary call - func coreCount( - request: GRPCCore.ServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.ServerResponse - - /// Quit this worker - func quitWorker( - request: GRPCCore.ServerRequest, - context: GRPCCore.ServerContext - ) async throws -> GRPCCore.ServerResponse -} - -/// Partial conformance to `Grpc_Testing_WorkerService_StreamingServiceProtocol`. -@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *) +// Default implementation of streaming methods from 'StreamingServiceProtocol'. extension Grpc_Testing_WorkerService.ServiceProtocol { internal func coreCount( request: GRPCCore.StreamingServerRequest, @@ -221,7 +441,7 @@ extension Grpc_Testing_WorkerService.ServiceProtocol { ) return GRPCCore.StreamingServerResponse(single: response) } - + internal func quitWorker( request: GRPCCore.StreamingServerRequest, context: GRPCCore.ServerContext @@ -232,4 +452,67 @@ extension Grpc_Testing_WorkerService.ServiceProtocol { ) return GRPCCore.StreamingServerResponse(single: response) } +} + +// Default implementation of methods from 'ServiceProtocol'. +extension Grpc_Testing_WorkerService.SimpleServiceProtocol { + internal func runServer( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.runServer( + request: request.messages, + response: writer, + context: context + ) + return [:] + } + ) + } + + internal func runClient( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.runClient( + request: request.messages, + response: writer, + context: context + ) + return [:] + } + ) + } + + internal func coreCount( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.coreCount( + request: request.message, + context: context + ), + metadata: [:] + ) + } + + internal func quitWorker( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.quitWorker( + request: request.message, + context: context + ), + metadata: [:] + ) + } } \ No newline at end of file diff --git a/IntegrationTests/grpc-performance-tests/Sources/PerformanceWorker.swift b/IntegrationTests/grpc-performance-tests/Sources/PerformanceWorker.swift index 2f528aa..dc99c1d 100644 --- a/IntegrationTests/grpc-performance-tests/Sources/PerformanceWorker.swift +++ b/IntegrationTests/grpc-performance-tests/Sources/PerformanceWorker.swift @@ -51,7 +51,7 @@ struct PerformanceWorker: AsyncParsableCommand { let server = GRPCServer( transport: .http2NIOPosix( address: .ipv4(host: "127.0.0.1", port: self.driverPort), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ), services: [WorkerService()] ) diff --git a/IntegrationTests/grpc-performance-tests/Sources/WorkerService.swift b/IntegrationTests/grpc-performance-tests/Sources/WorkerService.swift index dc0bed9..ecafef5 100644 --- a/IntegrationTests/grpc-performance-tests/Sources/WorkerService.swift +++ b/IntegrationTests/grpc-performance-tests/Sources/WorkerService.swift @@ -388,11 +388,12 @@ extension WorkerService { let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: numberOfThreads) // Don't restrict the max payload size, the client is always trusted. - var config = HTTP2ServerTransport.Posix.Config.defaults(transportSecurity: .plaintext) + var config = HTTP2ServerTransport.Posix.Config.defaults config.rpc.maxRequestPayloadSize = .max let transport = HTTP2ServerTransport.Posix( address: .ipv4(host: "127.0.0.1", port: Int(serverConfig.port)), + transportSecurity: .plaintext, config: config, eventLoopGroup: eventLoopGroup ) @@ -457,7 +458,7 @@ extension WorkerService { client: GRPCClient( transport: try .http2NIOPosix( target: target, - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) ), concurrentRPCs: Int(config.outstandingRpcsPerChannel), diff --git a/Sources/GRPCNIOTransportHTTP2Posix/Config+TLS.swift b/Sources/GRPCNIOTransportHTTP2Posix/Config+TLS.swift index aaf1de2..95de2fb 100644 --- a/Sources/GRPCNIOTransportHTTP2Posix/Config+TLS.swift +++ b/Sources/GRPCNIOTransportHTTP2Posix/Config+TLS.swift @@ -14,7 +14,7 @@ * limitations under the License. */ -extension HTTP2ServerTransport.Posix.Config { +extension HTTP2ServerTransport.Posix { /// The security configuration for this connection. public struct TransportSecurity: Sendable { package enum Wrapped: Sendable { @@ -27,12 +27,52 @@ extension HTTP2ServerTransport.Posix.Config { /// This connection is plaintext: no encryption will take place. public static let plaintext = Self(wrapped: .plaintext) - /// This connection will use TLS. + /// Secure connections with the given TLS configuration. public static func tls(_ tls: TLS) -> Self { Self(wrapped: .tls(tls)) } + + /// Secure connections with TLS. + /// + /// - Parameters: + /// - certificateChain: The certificates the server will offer during negotiation. + /// - privateKey: The private key associated with the leaf certificate. + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func tls( + certificateChain: [TLSConfig.CertificateSource], + privateKey: TLSConfig.PrivateKeySource, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .defaults( + certificateChain: certificateChain, + privateKey: privateKey, + configure: configure + ) + return .tls(tlsConfig) + } + + /// Secure the connection with mutual TLS. + /// + /// - Parameters: + /// - certificateChain: The certificates the client will offer during negotiation. + /// - privateKey: The private key associated with the leaf certificate. + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func mTLS( + certificateChain: [TLSConfig.CertificateSource], + privateKey: TLSConfig.PrivateKeySource, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .mTLS( + certificateChain: certificateChain, + privateKey: privateKey, + configure: configure + ) + return .tls(tlsConfig) + } } +} +extension HTTP2ServerTransport.Posix.TransportSecurity { public struct TLS: Sendable { /// The certificates the server will offer during negotiation. public var certificateChain: [TLSConfig.CertificateSource] @@ -127,7 +167,7 @@ extension HTTP2ServerTransport.Posix.Config { } } -extension HTTP2ClientTransport.Posix.Config { +extension HTTP2ClientTransport.Posix { /// The security configuration for this connection. public struct TransportSecurity: Sendable { package enum Wrapped: Sendable { @@ -140,12 +180,48 @@ extension HTTP2ClientTransport.Posix.Config { /// This connection is plaintext: no encryption will take place. public static let plaintext = Self(wrapped: .plaintext) - /// This connection will use TLS. + /// Secure the connection with the given TLS configuration. public static func tls(_ tls: TLS) -> Self { Self(wrapped: .tls(tls)) } + + /// Secure the connection with TLS using the default configuration. + /// + /// - Parameters: + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func tls( + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + Self.tls(.defaults(configure: configure)) + } + + /// Secure the connection with TLS using the default configuration. + public static var tls: Self { + Self.tls(.defaults()) + } + + /// Secure the connection with mutual TLS. + /// + /// - Parameters: + /// - certificateChain: The certificates the client will offer during negotiation. + /// - privateKey: The private key associated with the leaf certificate. + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func mTLS( + certificateChain: [TLSConfig.CertificateSource], + privateKey: TLSConfig.PrivateKeySource, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .mTLS( + certificateChain: certificateChain, + privateKey: privateKey, + configure: configure + ) + return .tls(tlsConfig) + } } +} +extension HTTP2ClientTransport.Posix.TransportSecurity { public struct TLS: Sendable { /// The certificates the client will offer during negotiation. public var certificateChain: [TLSConfig.CertificateSource] diff --git a/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ClientTransport+Posix.swift b/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ClientTransport+Posix.swift index 00eed8e..0326f56 100644 --- a/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ClientTransport+Posix.swift +++ b/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ClientTransport+Posix.swift @@ -45,7 +45,7 @@ extension HTTP2ClientTransport { /// try await withThrowingDiscardingTaskGroup { group in /// let transport = try HTTP2ClientTransport.Posix( /// target: .ipv4(host: "example.com"), - /// config: .defaults(transportSecurity: .plaintext) + /// transportSecurity: .plaintext /// ) /// let client = GRPCClient(transport: transport) /// group.addTask { @@ -62,6 +62,7 @@ extension HTTP2ClientTransport { /// /// - Parameters: /// - target: A target to resolve. + /// - transportSecurity: The configuration for securing network traffic. /// - config: Configuration for the transport. /// - resolverRegistry: A registry of resolver factories. /// - serviceConfig: Service config controlling how the transport should establish and @@ -72,7 +73,8 @@ extension HTTP2ClientTransport { /// - Throws: When no suitable resolver could be found for the `target`. public init( target: any ResolvableTarget, - config: Config, + transportSecurity: TransportSecurity, + config: Config = .defaults, resolverRegistry: NameResolverRegistry = .defaults, serviceConfig: ServiceConfig = ServiceConfig(), eventLoopGroup: any EventLoopGroup = .singletonMultiThreadedEventLoopGroup @@ -89,7 +91,11 @@ extension HTTP2ClientTransport { self.channel = GRPCChannel( resolver: resolver, - connector: try Connector(eventLoopGroup: eventLoopGroup, config: config), + connector: try Connector( + eventLoopGroup: eventLoopGroup, + config: config, + transportSecurity: transportSecurity + ), config: GRPCChannel.Config(posix: config), defaultServiceConfig: serviceConfig ) @@ -129,11 +135,15 @@ extension HTTP2ClientTransport.Posix { private let sslContext: NIOSSLContext? private let isPlainText: Bool - init(eventLoopGroup: any EventLoopGroup, config: HTTP2ClientTransport.Posix.Config) throws { + init( + eventLoopGroup: any EventLoopGroup, + config: HTTP2ClientTransport.Posix.Config, + transportSecurity: HTTP2ClientTransport.Posix.TransportSecurity + ) throws { self.eventLoopGroup = eventLoopGroup self.config = config - switch self.config.transportSecurity.wrapped { + switch transportSecurity.wrapped { case .plaintext: self.sslContext = nil self.isPlainText = true @@ -198,9 +208,6 @@ extension HTTP2ClientTransport.Posix { /// Compression configuration. public var compression: HTTP2ClientTransport.Config.Compression - /// The transport's security. - public var transportSecurity: TransportSecurity - /// Creates a new connection configuration. /// /// - Parameters: @@ -208,38 +215,37 @@ extension HTTP2ClientTransport.Posix { /// - backoff: Backoff configuration. /// - connection: Connection configuration. /// - compression: Compression configuration. - /// - transportSecurity: The transport's security configuration. /// - /// - SeeAlso: ``defaults(transportSecurity:configure:)`` + /// - SeeAlso: ``defaults(configure:)`` and ``defaults``. public init( http2: HTTP2ClientTransport.Config.HTTP2, backoff: HTTP2ClientTransport.Config.Backoff, connection: HTTP2ClientTransport.Config.Connection, - compression: HTTP2ClientTransport.Config.Compression, - transportSecurity: TransportSecurity + compression: HTTP2ClientTransport.Config.Compression ) { self.http2 = http2 self.connection = connection self.backoff = backoff self.compression = compression - self.transportSecurity = transportSecurity + } + + /// Default configuration. + public static var defaults: Self { + Self.defaults() } /// Default values. /// /// - Parameters: - /// - transportSecurity: The security settings applied to the transport. /// - configure: A closure which allows you to modify the defaults before returning them. public static func defaults( - transportSecurity: TransportSecurity, configure: (_ config: inout Self) -> Void = { _ in } ) -> Self { var config = Self( http2: .defaults, backoff: .defaults, connection: .defaults, - compression: .defaults, - transportSecurity: transportSecurity + compression: .defaults ) configure(&config) return config @@ -263,6 +269,7 @@ extension ClientTransport where Self == HTTP2ClientTransport.Posix { /// /// - Parameters: /// - target: A target to resolve. + /// - transportSecurity: The configuration for securing network traffic. /// - config: Configuration for the transport. /// - resolverRegistry: A registry of resolver factories. /// - serviceConfig: Service config controlling how the transport should establish and @@ -273,13 +280,15 @@ extension ClientTransport where Self == HTTP2ClientTransport.Posix { /// - Throws: When no suitable resolver could be found for the `target`. public static func http2NIOPosix( target: any ResolvableTarget, - config: HTTP2ClientTransport.Posix.Config, + transportSecurity: HTTP2ClientTransport.Posix.TransportSecurity, + config: HTTP2ClientTransport.Posix.Config = .defaults, resolverRegistry: NameResolverRegistry = .defaults, serviceConfig: ServiceConfig = ServiceConfig(), eventLoopGroup: any EventLoopGroup = .singletonMultiThreadedEventLoopGroup ) throws -> Self { return try HTTP2ClientTransport.Posix( target: target, + transportSecurity: transportSecurity, config: config, resolverRegistry: resolverRegistry, serviceConfig: serviceConfig, diff --git a/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift b/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift index a94ac85..75e23f7 100644 --- a/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift +++ b/Sources/GRPCNIOTransportHTTP2Posix/HTTP2ServerTransport+Posix.swift @@ -41,7 +41,7 @@ extension HTTP2ServerTransport { /// try await withThrowingDiscardingTaskGroup { group in /// let transport = HTTP2ServerTransport.Posix( /// address: .ipv4(host: "127.0.0.1", port: 0), - /// config: .defaults(transportSecurity: .plaintext) + /// transportSecurity: .plaintext /// ) /// let server = GRPCServer(transport: transport, services: someServices) /// group.addTask { @@ -54,6 +54,7 @@ extension HTTP2ServerTransport { public struct Posix: ServerTransport, ListeningServerTransport { private struct ListenerFactory: HTTP2ListenerFactory { let config: Config + let transportSecurity: TransportSecurity func makeListeningChannel( eventLoopGroup: any EventLoopGroup, @@ -62,7 +63,7 @@ extension HTTP2ServerTransport { ) async throws -> NIOAsyncChannel { let sslContext: NIOSSLContext? - switch self.config.transportSecurity.wrapped { + switch self.transportSecurity.wrapped { case .plaintext: sslContext = nil case .tls(let tlsConfig): @@ -97,7 +98,7 @@ extension HTTP2ServerTransport { let requireALPN: Bool let scheme: Scheme - switch self.config.transportSecurity.wrapped { + switch self.transportSecurity.wrapped { case .plaintext: requireALPN = false scheme = .http @@ -141,14 +142,16 @@ extension HTTP2ServerTransport { /// /// - Parameters: /// - address: The address to which the server should be bound. + /// - transportSecurity: The configuration for securing network traffic. /// - config: The transport configuration. /// - eventLoopGroup: The ELG from which to get ELs to run this transport. public init( address: GRPCNIOTransportCore.SocketAddress, - config: Config, + transportSecurity: TransportSecurity, + config: Config = .defaults, eventLoopGroup: MultiThreadedEventLoopGroup = .singletonMultiThreadedEventLoopGroup ) { - let factory = ListenerFactory(config: config) + let factory = ListenerFactory(config: config, transportSecurity: transportSecurity) let helper = ServerQuiescingHelper(group: eventLoopGroup) self.underlyingTransport = CommonHTTP2ServerTransport( address: address, @@ -188,9 +191,6 @@ extension HTTP2ServerTransport.Posix { /// RPC configuration. public var rpc: HTTP2ServerTransport.Config.RPC - /// The transport's security. - public var transportSecurity: TransportSecurity - /// Construct a new `Config`. /// /// - Parameters: @@ -198,38 +198,37 @@ extension HTTP2ServerTransport.Posix { /// - rpc: RPC configuration. /// - connection: Connection configuration. /// - compression: Compression configuration. - /// - transportSecurity: The transport's security configuration. /// - /// - SeeAlso: ``defaults(transportSecurity:configure:)`` + /// - SeeAlso: ``defaults(configure:)`` and ``defaults``. public init( http2: HTTP2ServerTransport.Config.HTTP2, rpc: HTTP2ServerTransport.Config.RPC, connection: HTTP2ServerTransport.Config.Connection, - compression: HTTP2ServerTransport.Config.Compression, - transportSecurity: TransportSecurity + compression: HTTP2ServerTransport.Config.Compression ) { self.compression = compression self.connection = connection self.http2 = http2 self.rpc = rpc - self.transportSecurity = transportSecurity + } + + /// Default configuration. + public static var defaults: Self { + Self.defaults() } /// Default values for the different configurations. /// /// - Parameters: - /// - transportSecurity: The security settings applied to the transport. /// - configure: A closure which allows you to modify the defaults before returning them. public static func defaults( - transportSecurity: TransportSecurity, configure: (_ config: inout Self) -> Void = { _ in } ) -> Self { var config = Self( http2: .defaults, rpc: .defaults, connection: .defaults, - compression: .defaults, - transportSecurity: transportSecurity + compression: .defaults ) configure(&config) return config @@ -267,17 +266,20 @@ extension ServerTransport where Self == HTTP2ServerTransport.Posix { /// /// - Parameters: /// - address: The address to which the server should be bound. + /// - transportSecurity: The configuration for securing network traffic. /// - config: The transport configuration. /// - eventLoopGroup: The underlying NIO `EventLoopGroup` to the server on. This must /// be a `MultiThreadedEventLoopGroup` or an `EventLoop` from /// a `MultiThreadedEventLoopGroup`. public static func http2NIOPosix( address: GRPCNIOTransportCore.SocketAddress, - config: HTTP2ServerTransport.Posix.Config, + transportSecurity: HTTP2ServerTransport.Posix.TransportSecurity, + config: HTTP2ServerTransport.Posix.Config = .defaults, eventLoopGroup: MultiThreadedEventLoopGroup = .singletonMultiThreadedEventLoopGroup ) -> Self { return HTTP2ServerTransport.Posix( address: address, + transportSecurity: transportSecurity, config: config, eventLoopGroup: eventLoopGroup ) diff --git a/Sources/GRPCNIOTransportHTTP2Posix/NIOSSL+GRPC.swift b/Sources/GRPCNIOTransportHTTP2Posix/NIOSSL+GRPC.swift index 3bc8cf0..25a5d6b 100644 --- a/Sources/GRPCNIOTransportHTTP2Posix/NIOSSL+GRPC.swift +++ b/Sources/GRPCNIOTransportHTTP2Posix/NIOSSL+GRPC.swift @@ -126,7 +126,7 @@ extension CertificateVerification { } extension TLSConfiguration { - package init(_ tlsConfig: HTTP2ServerTransport.Posix.Config.TLS) throws { + package init(_ tlsConfig: HTTP2ServerTransport.Posix.TransportSecurity.TLS) throws { let certificateChain = try tlsConfig.certificateChain.sslCertificateSources() let privateKey = try NIOSSLPrivateKey(privateKey: tlsConfig.privateKey) @@ -142,7 +142,7 @@ extension TLSConfiguration { self.applicationProtocols = ["grpc-exp", "h2"] } - package init(_ tlsConfig: HTTP2ClientTransport.Posix.Config.TLS) throws { + package init(_ tlsConfig: HTTP2ClientTransport.Posix.TransportSecurity.TLS) throws { self = TLSConfiguration.makeClientConfiguration() self.certificateChain = try tlsConfig.certificateChain.sslCertificateSources() diff --git a/Sources/GRPCNIOTransportHTTP2TransportServices/Config+TLS.swift b/Sources/GRPCNIOTransportHTTP2TransportServices/Config+TLS.swift index 118b524..7e0683b 100644 --- a/Sources/GRPCNIOTransportHTTP2TransportServices/Config+TLS.swift +++ b/Sources/GRPCNIOTransportHTTP2TransportServices/Config+TLS.swift @@ -21,7 +21,7 @@ public import Network private import struct Foundation.Data private import struct Foundation.URL -extension HTTP2ServerTransport.TransportServices.Config { +extension HTTP2ServerTransport.TransportServices { /// The security configuration for this connection. public struct TransportSecurity: Sendable { package enum Wrapped: Sendable { @@ -34,12 +34,46 @@ extension HTTP2ServerTransport.TransportServices.Config { /// This connection is plaintext: no encryption will take place. public static let plaintext = Self(wrapped: .plaintext) - /// This connection will use TLS. + /// Secures connections with the given TLS configuration. public static func tls(_ tls: TLS) -> Self { Self(wrapped: .tls(tls)) } + + /// Secures connections with the TLS. + /// + /// - Parameters: + /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS. + /// - configure: A closure allowing you to modify the configuration before returning it. + public static func tls( + identityProvider: @Sendable @escaping () throws -> SecIdentity, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .defaults( + identityProvider: identityProvider, + configure: configure + ) + return .tls(tlsConfig) + } + + /// Secures connections with the mutual TLS. + /// + /// - Parameters: + /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS. + /// - configure: A closure allowing you to modify the configuration before returning it. + public static func mTLS( + identityProvider: @Sendable @escaping () throws -> SecIdentity, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .mTLS( + identityProvider: identityProvider, + configure: configure + ) + return .tls(tlsConfig) + } } +} +extension HTTP2ServerTransport.TransportServices { public struct TLS: Sendable { /// How to verify the client certificate, if one is presented. public var clientCertificateVerification: TLSConfig.CertificateVerification @@ -80,6 +114,7 @@ extension HTTP2ServerTransport.TransportServices.Config { /// /// - Parameters: /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS. + /// - configure: A closure which allows you to modify the defaults before returning them. /// - Returns: A new HTTP2 NIO Transport Services transport TLS config. public static func defaults( identityProvider: @Sendable @escaping () throws -> SecIdentity, @@ -121,7 +156,7 @@ extension HTTP2ServerTransport.TransportServices.Config { } } -extension HTTP2ClientTransport.TransportServices.Config { +extension HTTP2ClientTransport.TransportServices { /// The security configuration for this connection. public struct TransportSecurity: Sendable { package enum Wrapped: Sendable { @@ -134,12 +169,46 @@ extension HTTP2ClientTransport.TransportServices.Config { /// This connection is plaintext: no encryption will take place. public static let plaintext = Self(wrapped: .plaintext) - /// This connection will use TLS. + /// Secure connections with the given TLS configuration. public static func tls(_ tls: TLS) -> Self { Self(wrapped: .tls(tls)) } + + /// Secure connections with TLS. + /// + /// - Parameters: + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func tls( + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .defaults(configure: configure) + return .tls(tlsConfig) + } + + /// Secure connections with TLS. + public static var tls: Self { + return .tls() + } + + /// Secure connections with mutual TLS. + /// + /// - Parameters: + /// - identityProvider: A provider for the `SecIdentity` to be used when setting up TLS. + /// - configure: A closure which allows you to modify the defaults before returning them. + public static func mTLS( + identityProvider: @Sendable @escaping () throws -> SecIdentity, + configure: (_ config: inout TLS) -> Void = { _ in } + ) -> Self { + let tlsConfig: TLS = .mTLS( + identityProvider: identityProvider, + configure: configure + ) + return .tls(tlsConfig) + } } +} +extension HTTP2ClientTransport.TransportServices { public struct TLS: Sendable { /// How to verify the server certificate, if one is presented. public var serverCertificateVerification: TLSConfig.CertificateVerification diff --git a/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ClientTransport+TransportServices.swift b/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ClientTransport+TransportServices.swift index 77c2495..e8baced 100644 --- a/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ClientTransport+TransportServices.swift +++ b/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ClientTransport+TransportServices.swift @@ -47,7 +47,7 @@ extension HTTP2ClientTransport { /// try await withThrowingDiscardingTaskGroup { group in /// let transport = try HTTP2ClientTransport.TransportServices( /// target: .ipv4(host: "example.com"), - /// config: .defaults(transportSecurity: .plaintext) + /// transportSecurity: .plaintext /// ) /// let client = GRPCClient(transport: transport) /// group.addTask { @@ -68,6 +68,7 @@ extension HTTP2ClientTransport { /// /// - Parameters: /// - target: A target to resolve. + /// - transportSecurity: The configuration for securing network traffic. /// - config: Configuration for the transport. /// - resolverRegistry: A registry of resolver factories. /// - serviceConfig: Service config controlling how the transport should establish and @@ -78,7 +79,8 @@ extension HTTP2ClientTransport { /// - Throws: When no suitable resolver could be found for the `target`. public init( target: any ResolvableTarget, - config: Config, + transportSecurity: TransportSecurity, + config: Config = .defaults, resolverRegistry: NameResolverRegistry = .defaults, serviceConfig: ServiceConfig = ServiceConfig(), eventLoopGroup: any EventLoopGroup = .singletonNIOTSEventLoopGroup @@ -95,7 +97,11 @@ extension HTTP2ClientTransport { self.channel = GRPCChannel( resolver: resolver, - connector: Connector(eventLoopGroup: eventLoopGroup, config: config), + connector: Connector( + eventLoopGroup: eventLoopGroup, + config: config, + transportSecurity: transportSecurity + ), config: GRPCChannel.Config(transportServices: config), defaultServiceConfig: serviceConfig ) @@ -126,14 +132,17 @@ extension HTTP2ClientTransport { extension HTTP2ClientTransport.TransportServices { struct Connector: HTTP2Connector { private let config: HTTP2ClientTransport.TransportServices.Config + private let transportSecurity: HTTP2ClientTransport.TransportServices.TransportSecurity private let eventLoopGroup: any EventLoopGroup init( eventLoopGroup: any EventLoopGroup, - config: HTTP2ClientTransport.TransportServices.Config + config: HTTP2ClientTransport.TransportServices.Config, + transportSecurity: HTTP2ClientTransport.TransportServices.TransportSecurity ) { self.eventLoopGroup = eventLoopGroup self.config = config + self.transportSecurity = transportSecurity } func establishConnection( @@ -142,7 +151,7 @@ extension HTTP2ClientTransport.TransportServices { ) async throws -> HTTP2Connection { let bootstrap: NIOTSConnectionBootstrap let isPlainText: Bool - switch self.config.transportSecurity.wrapped { + switch self.transportSecurity.wrapped { case .plaintext: isPlainText = true bootstrap = NIOTSConnectionBootstrap(group: self.eventLoopGroup) @@ -197,9 +206,6 @@ extension HTTP2ClientTransport.TransportServices { /// Compression configuration. public var compression: HTTP2ClientTransport.Config.Compression - /// The transport's security. - public var transportSecurity: TransportSecurity - /// Creates a new connection configuration. /// /// - Parameters: @@ -207,38 +213,37 @@ extension HTTP2ClientTransport.TransportServices { /// - backoff: Backoff configuration. /// - connection: Connection configuration. /// - compression: Compression configuration. - /// - transportSecurity: The transport's security configuration. /// - /// - SeeAlso: ``defaults(transportSecurity:configure:)`` + /// - SeeAlso: ``defaults(configure:)`` and ``defaults``. public init( http2: HTTP2ClientTransport.Config.HTTP2, backoff: HTTP2ClientTransport.Config.Backoff, connection: HTTP2ClientTransport.Config.Connection, - compression: HTTP2ClientTransport.Config.Compression, - transportSecurity: TransportSecurity + compression: HTTP2ClientTransport.Config.Compression ) { self.http2 = http2 self.connection = connection self.backoff = backoff self.compression = compression - self.transportSecurity = transportSecurity + } + + /// Default configuration. + public static var defaults: Self { + Self.defaults() } /// Default values. /// /// - Parameters: - /// - transportSecurity: The security settings applied to the transport. /// - configure: A closure which allows you to modify the defaults before returning them. public static func defaults( - transportSecurity: TransportSecurity, configure: (_ config: inout Self) -> Void = { _ in } ) -> Self { var config = Self( http2: .defaults, backoff: .defaults, connection: .defaults, - compression: .defaults, - transportSecurity: transportSecurity + compression: .defaults ) configure(&config) return config @@ -284,6 +289,7 @@ extension ClientTransport where Self == HTTP2ClientTransport.TransportServices { /// /// - Parameters: /// - target: A target to resolve. + /// - transportSecurity: The security settings applied to the transport. /// - config: Configuration for the transport. /// - resolverRegistry: A registry of resolver factories. /// - serviceConfig: Service config controlling how the transport should establish and @@ -294,13 +300,15 @@ extension ClientTransport where Self == HTTP2ClientTransport.TransportServices { /// - Throws: When no suitable resolver could be found for the `target`. public static func http2NIOTS( target: any ResolvableTarget, - config: HTTP2ClientTransport.TransportServices.Config, + transportSecurity: HTTP2ClientTransport.TransportServices.TransportSecurity, + config: HTTP2ClientTransport.TransportServices.Config = .defaults, resolverRegistry: NameResolverRegistry = .defaults, serviceConfig: ServiceConfig = ServiceConfig(), eventLoopGroup: any EventLoopGroup = .singletonNIOTSEventLoopGroup ) throws -> Self { try HTTP2ClientTransport.TransportServices( target: target, + transportSecurity: transportSecurity, config: config, resolverRegistry: resolverRegistry, serviceConfig: serviceConfig, @@ -311,7 +319,7 @@ extension ClientTransport where Self == HTTP2ClientTransport.TransportServices { extension NWProtocolTLS.Options { convenience init( - _ tlsConfig: HTTP2ClientTransport.TransportServices.Config.TLS, + _ tlsConfig: HTTP2ClientTransport.TransportServices.TLS, authority: String? ) throws { self.init() diff --git a/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ServerTransport+TransportServices.swift b/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ServerTransport+TransportServices.swift index 37caf36..2d219ce 100644 --- a/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ServerTransport+TransportServices.swift +++ b/Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ServerTransport+TransportServices.swift @@ -31,6 +31,7 @@ extension HTTP2ServerTransport { public struct TransportServices: ServerTransport, ListeningServerTransport { private struct ListenerFactory: HTTP2ListenerFactory { let config: Config + let transportSecurity: TransportSecurity func makeListeningChannel( eventLoopGroup: any EventLoopGroup, @@ -41,7 +42,7 @@ extension HTTP2ServerTransport { let requireALPN: Bool let scheme: Scheme - switch self.config.transportSecurity.wrapped { + switch self.transportSecurity.wrapped { case .plaintext: requireALPN = false scheme = .http @@ -102,14 +103,16 @@ extension HTTP2ServerTransport { /// /// - Parameters: /// - address: The address to which the server should be bound. + /// - transportSecurity: The security settings applied to the transport. /// - config: The transport configuration. /// - eventLoopGroup: The ELG from which to get ELs to run this transport. public init( address: GRPCNIOTransportCore.SocketAddress, - config: Config, + transportSecurity: TransportSecurity, + config: Config = .defaults, eventLoopGroup: NIOTSEventLoopGroup = .singletonNIOTSEventLoopGroup ) { - let factory = ListenerFactory(config: config) + let factory = ListenerFactory(config: config, transportSecurity: transportSecurity) let helper = ServerQuiescingHelper(group: eventLoopGroup) self.underlyingTransport = CommonHTTP2ServerTransport( address: address, @@ -149,45 +152,40 @@ extension HTTP2ServerTransport.TransportServices { /// RPC configuration. public var rpc: HTTP2ServerTransport.Config.RPC - /// The transport's security. - public var transportSecurity: TransportSecurity - /// Construct a new `Config`. /// - Parameters: /// - compression: Compression configuration. /// - connection: Connection configuration. /// - http2: HTTP2 configuration. /// - rpc: RPC configuration. - /// - transportSecurity: The transport's security configuration. public init( compression: HTTP2ServerTransport.Config.Compression, connection: HTTP2ServerTransport.Config.Connection, http2: HTTP2ServerTransport.Config.HTTP2, - rpc: HTTP2ServerTransport.Config.RPC, - transportSecurity: TransportSecurity + rpc: HTTP2ServerTransport.Config.RPC ) { self.compression = compression self.connection = connection self.http2 = http2 self.rpc = rpc - self.transportSecurity = transportSecurity + } + + public static var defaults: Self { + Self.defaults() } /// Default values for the different configurations. /// /// - Parameters: - /// - transportSecurity: The transport's security configuration. /// - configure: A closure which allows you to modify the defaults before returning them. public static func defaults( - transportSecurity: TransportSecurity, configure: (_ config: inout Self) -> Void = { _ in } ) -> Self { var config = Self( compression: .defaults, connection: .defaults, http2: .defaults, - rpc: .defaults, - transportSecurity: transportSecurity + rpc: .defaults ) configure(&config) return config @@ -222,16 +220,19 @@ extension ServerTransport where Self == HTTP2ServerTransport.TransportServices { /// /// - Parameters: /// - address: The address to which the server should be bound. + /// - transportSecurity: The security settings applied to the transport. /// - config: The transport configuration. /// - eventLoopGroup: The underlying NIO `EventLoopGroup` to the server on. This must /// be a `NIOTSEventLoopGroup` or an `EventLoop` from a `NIOTSEventLoopGroup`. public static func http2NIOTS( address: GRPCNIOTransportCore.SocketAddress, - config: HTTP2ServerTransport.TransportServices.Config, + transportSecurity: HTTP2ServerTransport.TransportServices.TransportSecurity, + config: HTTP2ServerTransport.TransportServices.Config = .defaults, eventLoopGroup: NIOTSEventLoopGroup = .singletonNIOTSEventLoopGroup ) -> Self { return HTTP2ServerTransport.TransportServices( address: address, + transportSecurity: transportSecurity, config: config, eventLoopGroup: eventLoopGroup ) @@ -239,7 +240,7 @@ extension ServerTransport where Self == HTTP2ServerTransport.TransportServices { } extension NWProtocolTLS.Options { - convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.Config.TLS) throws { + convenience init(_ tlsConfig: HTTP2ServerTransport.TransportServices.TLS) throws { self.init() guard let sec_identity = sec_identity_create(try tlsConfig.identityProvider()) else { diff --git a/Tests/GRPCNIOTransportCoreTests/Client/Connection/ClientConnectionHandlerTests.swift b/Tests/GRPCNIOTransportCoreTests/Client/Connection/ClientConnectionHandlerTests.swift index 33aa938..3902455 100644 --- a/Tests/GRPCNIOTransportCoreTests/Client/Connection/ClientConnectionHandlerTests.swift +++ b/Tests/GRPCNIOTransportCoreTests/Client/Connection/ClientConnectionHandlerTests.swift @@ -241,6 +241,8 @@ struct ClientConnectionHandlerTests { let closed = connection.closeGracefully() #expect(try connection.readEvent() == .closing(.initiatedLocally)) connection.streamClosed(1) + // Need to run the event loop to fire the close event. + connection.channel.embeddedEventLoop.run() try closed.wait() } @@ -411,6 +413,7 @@ extension ClientConnectionHandlerTests { func streamClosed(_ id: HTTP2StreamID) { self.streamDelegate.streamClosed(id, channel: self.channel) + self.channel.embeddedEventLoop.run() } func goAway( @@ -453,6 +456,7 @@ extension ClientConnectionHandlerTests { let promise = self.channel.embeddedEventLoop.makePromise(of: Void.self) let event = ClientConnectionHandler.OutboundEvent.closeGracefully self.channel.pipeline.triggerUserOutboundEvent(event, promise: promise) + self.channel.embeddedEventLoop.run() return promise.futureResult } } diff --git a/Tests/GRPCNIOTransportHTTP2Tests/ControlClient.swift b/Tests/GRPCNIOTransportHTTP2Tests/ControlClient.swift index 6ad220d..98f8262 100644 --- a/Tests/GRPCNIOTransportHTTP2Tests/ControlClient.swift +++ b/Tests/GRPCNIOTransportHTTP2Tests/ControlClient.swift @@ -37,7 +37,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } @@ -52,7 +52,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } @@ -70,7 +70,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } @@ -85,7 +85,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } @@ -102,7 +102,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } @@ -118,7 +118,7 @@ internal struct ControlClient { serializer: JSONSerializer(), deserializer: JSONDeserializer(), options: options, - handler: body + onResponse: body ) } } diff --git a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOPosixTests.swift b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOPosixTests.swift index 4270446..cef939a 100644 --- a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOPosixTests.swift +++ b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOPosixTests.swift @@ -24,7 +24,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { func testGetListeningAddress_IPv4() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -44,7 +44,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { func testGetListeningAddress_IPv6() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .ipv6(host: "::1", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -64,7 +64,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { func testGetListeningAddress_UnixDomainSocket() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .unixDomainSocket(path: "/tmp/posix-uds-test"), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -88,7 +88,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .vsock(contextID: .any, port: .any), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -107,7 +107,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { func testGetListeningAddress_InvalidAddress() async { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .unixDomainSocket(path: "/this/should/be/an/invalid/path"), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try? await withThrowingDiscardingTaskGroup { group in @@ -136,7 +136,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { func testGetListeningAddress_StoppedListening() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.Posix( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try? await withThrowingDiscardingTaskGroup { group in @@ -167,9 +167,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testServerConfig_Defaults() throws { - let grpcConfig = HTTP2ServerTransport.Posix.Config.defaults( - transportSecurity: .plaintext - ) + let grpcConfig = HTTP2ServerTransport.Posix.Config.defaults XCTAssertEqual(grpcConfig.compression, HTTP2ServerTransport.Config.Compression.defaults) XCTAssertEqual(grpcConfig.connection, HTTP2ServerTransport.Config.Connection.defaults) @@ -178,9 +176,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testClientConfig_Defaults() throws { - let grpcConfig = HTTP2ClientTransport.Posix.Config.defaults( - transportSecurity: .plaintext - ) + let grpcConfig = HTTP2ClientTransport.Posix.Config.defaults XCTAssertEqual(grpcConfig.compression, HTTP2ClientTransport.Config.Compression.defaults) XCTAssertEqual(grpcConfig.connection, HTTP2ClientTransport.Config.Connection.defaults) @@ -281,7 +277,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { """ func testServerTLSConfig_Defaults() throws { - let grpcTLSConfig = HTTP2ServerTransport.Posix.Config.TLS.defaults( + let grpcTLSConfig = HTTP2ServerTransport.Posix.TransportSecurity.TLS.defaults( certificateChain: [ .bytes(Array(Self.samplePemCert.utf8), format: .pem) ], @@ -311,7 +307,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testServerTLSConfig_mTLS() throws { - let grpcTLSConfig = HTTP2ServerTransport.Posix.Config.TLS.mTLS( + let grpcTLSConfig = HTTP2ServerTransport.Posix.TransportSecurity.TLS.mTLS( certificateChain: [ .bytes(Array(Self.samplePemCert.utf8), format: .pem) ], @@ -341,7 +337,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testServerTLSConfig_FullVerifyClient() throws { - var grpcTLSConfig = HTTP2ServerTransport.Posix.Config.TLS.defaults( + var grpcTLSConfig = HTTP2ServerTransport.Posix.TransportSecurity.TLS.defaults( certificateChain: [ .bytes(Array(Self.samplePemCert.utf8), format: .pem) ], @@ -372,7 +368,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testServerTLSConfig_CustomTrustRoots() throws { - var grpcTLSConfig = HTTP2ServerTransport.Posix.Config.TLS.defaults( + var grpcTLSConfig = HTTP2ServerTransport.Posix.TransportSecurity.TLS.defaults( certificateChain: [ .bytes(Array(Self.samplePemCert.utf8), format: .pem) ], @@ -406,7 +402,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testClientTLSConfig_Defaults() throws { - let grpcTLSConfig = HTTP2ClientTransport.Posix.Config.TLS.defaults + let grpcTLSConfig = HTTP2ClientTransport.Posix.TransportSecurity.TLS.defaults let nioSSLTLSConfig = try TLSConfiguration(grpcTLSConfig) XCTAssertEqual(nioSSLTLSConfig.certificateChain, []) @@ -418,7 +414,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testClientTLSConfig_CustomCertificateChainAndPrivateKey() throws { - var grpcTLSConfig = HTTP2ClientTransport.Posix.Config.TLS.defaults + var grpcTLSConfig = HTTP2ClientTransport.Posix.TransportSecurity.TLS.defaults grpcTLSConfig.certificateChain = [ .bytes(Array(Self.samplePemCert.utf8), format: .pem) ] @@ -447,7 +443,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testClientTLSConfig_CustomTrustRoots() throws { - var grpcTLSConfig = HTTP2ClientTransport.Posix.Config.TLS.defaults + var grpcTLSConfig = HTTP2ClientTransport.Posix.TransportSecurity.TLS.defaults grpcTLSConfig.trustRoots = .certificates([.bytes(Array(Self.samplePemCert.utf8), format: .pem)]) let nioSSLTLSConfig = try TLSConfiguration(grpcTLSConfig) @@ -463,7 +459,7 @@ final class HTTP2TransportNIOPosixTests: XCTestCase { } func testClientTLSConfig_CustomCertificateVerification() throws { - var grpcTLSConfig = HTTP2ClientTransport.Posix.Config.TLS.defaults + var grpcTLSConfig = HTTP2ClientTransport.Posix.TransportSecurity.TLS.defaults grpcTLSConfig.serverCertificateVerification = .noHostnameVerification let nioSSLTLSConfig = try TLSConfiguration(grpcTLSConfig) diff --git a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOTransportServicesTests.swift b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOTransportServicesTests.swift index 568623b..8472c2a 100644 --- a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOTransportServicesTests.swift +++ b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportNIOTransportServicesTests.swift @@ -73,7 +73,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_IPv4() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.TransportServices( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -93,7 +93,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_IPv6() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.TransportServices( address: .ipv6(host: "::1", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try await withThrowingDiscardingTaskGroup { group in @@ -113,7 +113,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_UnixDomainSocket() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.TransportServices( address: .unixDomainSocket(path: "/tmp/niots-uds-test"), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) defer { // NIOTS does not unlink the UDS on close. @@ -139,7 +139,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_InvalidAddress() async { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.TransportServices( address: .unixDomainSocket(path: "/this/should/be/an/invalid/path"), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try? await withThrowingDiscardingTaskGroup { group in @@ -168,7 +168,7 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { func testGetListeningAddress_StoppedListening() async throws { let transport = GRPCNIOTransportCore.HTTP2ServerTransport.TransportServices( address: .ipv4(host: "0.0.0.0", port: 0), - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ) try? await withThrowingDiscardingTaskGroup { group in @@ -199,12 +199,10 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { } func testServerConfig_Defaults() throws { - let grpcTLSConfig = HTTP2ServerTransport.TransportServices.Config.TLS.defaults( + let grpcTLSConfig = HTTP2ServerTransport.TransportServices.TLS.defaults( identityProvider: Self.loadIdentity ) - let grpcConfig = HTTP2ServerTransport.TransportServices.Config.defaults( - transportSecurity: .tls(grpcTLSConfig) - ) + let grpcConfig = HTTP2ServerTransport.TransportServices.Config.defaults XCTAssertEqual(grpcConfig.compression, HTTP2ServerTransport.Config.Compression.defaults) XCTAssertEqual(grpcConfig.connection, HTTP2ServerTransport.Config.Connection.defaults) @@ -218,10 +216,8 @@ final class HTTP2TransportNIOTransportServicesTests: XCTestCase { } func testClientConfig_Defaults() throws { - let grpcTLSConfig = HTTP2ClientTransport.TransportServices.Config.TLS.defaults() - let grpcConfig = HTTP2ClientTransport.TransportServices.Config.defaults( - transportSecurity: .tls(grpcTLSConfig) - ) + let grpcTLSConfig = HTTP2ClientTransport.TransportServices.TLS.defaults + let grpcConfig = HTTP2ClientTransport.TransportServices.Config.defaults XCTAssertEqual(grpcConfig.compression, HTTP2ClientTransport.Config.Compression.defaults) XCTAssertEqual(grpcConfig.connection, HTTP2ClientTransport.Config.Connection.defaults) diff --git a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTLSEnabledTests.swift b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTLSEnabledTests.swift index 8cd5f1d..1d8f42d 100644 --- a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTLSEnabledTests.swift +++ b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTLSEnabledTests.swift @@ -202,39 +202,51 @@ struct HTTP2TransportTLSEnabledTests { case posix } - enum Config { - enum Client { - case posix(HTTP2ClientTransport.Posix.Config) - } + struct Config { + var security: Security + var transport: Transport + } - enum Server { - case posix(HTTP2ServerTransport.Posix.Config) - } + enum ClientConfig { + typealias Posix = Config< + HTTP2ClientTransport.Posix.Config, + HTTP2ClientTransport.Posix.TransportSecurity + > + case posix(Posix) } - private func makeDefaultPlaintextPosixClientConfig() -> HTTP2ClientTransport.Posix.Config { - .defaults(transportSecurity: .plaintext) { config in - config.backoff.initial = .milliseconds(100) - config.backoff.multiplier = 1 - config.backoff.jitter = 0 - } + enum ServerConfig { + typealias Posix = Config< + HTTP2ServerTransport.Posix.Config, + HTTP2ServerTransport.Posix.TransportSecurity + > + case posix(Posix) + } + + private func makeDefaultPlaintextPosixClientConfig() -> ClientConfig.Posix { + ClientConfig.Posix( + security: .plaintext, + transport: .defaults { config in + config.backoff.initial = .milliseconds(100) + config.backoff.multiplier = 1 + config.backoff.jitter = 0 + } + ) } private func makeDefaultTLSClientConfig( for transportSecurity: TransportKind, certificateKeyPairs: SelfSignedCertificateKeyPairs - ) -> Config.Client { + ) -> ClientConfig { switch transportSecurity { case .posix: var config = self.makeDefaultPlaintextPosixClientConfig() - config.transportSecurity = .tls( - .defaults { - $0.trustRoots = .certificates([ - .bytes(certificateKeyPairs.server.certificate, format: .der) - ]) - } - ) - config.http2.authority = "localhost" + config.security = .tls { + $0.trustRoots = .certificates([ + .bytes(certificateKeyPairs.server.certificate, format: .der) + ]) + } + config.transport.http2.authority = "localhost" return .posix(config) } } @@ -243,41 +255,37 @@ struct HTTP2TransportTLSEnabledTests { for transportKind: TransportKind, certificateKeyPairs: SelfSignedCertificateKeyPairs, serverHostname: String? - ) -> Config.Client { + ) -> ClientConfig { switch transportKind { case .posix: var config = self.makeDefaultPlaintextPosixClientConfig() - config.transportSecurity = .tls( - .mTLS( - certificateChain: [.bytes(certificateKeyPairs.client.certificate, format: .der)], - privateKey: .bytes(certificateKeyPairs.client.key, format: .der) - ) { - $0.trustRoots = .certificates([ - .bytes(certificateKeyPairs.server.certificate, format: .der) - ]) - } - ) - config.http2.authority = serverHostname + config.security = .mTLS( + certificateChain: [.bytes(certificateKeyPairs.client.certificate, format: .der)], + privateKey: .bytes(certificateKeyPairs.client.key, format: .der) + ) { + $0.trustRoots = .certificates([ + .bytes(certificateKeyPairs.server.certificate, format: .der) + ]) + } + config.transport.http2.authority = serverHostname return .posix(config) } } - private func makeDefaultPlaintextPosixServerConfig() -> HTTP2ServerTransport.Posix.Config { - .defaults(transportSecurity: .plaintext) + private func makeDefaultPlaintextPosixServerConfig() -> ServerConfig.Posix { + ServerConfig.Posix(security: .plaintext, transport: .defaults) } private func makeDefaultTLSServerConfig( for transportKind: TransportKind, certificateKeyPairs: SelfSignedCertificateKeyPairs - ) -> Config.Server { + ) -> ServerConfig { switch transportKind { case .posix: var config = self.makeDefaultPlaintextPosixServerConfig() - config.transportSecurity = .tls( - .defaults( - certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)], - privateKey: .bytes(certificateKeyPairs.server.key, format: .der) - ) + config.security = .tls( + certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)], + privateKey: .bytes(certificateKeyPairs.server.key, format: .der) ) return .posix(config) } @@ -287,29 +295,27 @@ struct HTTP2TransportTLSEnabledTests { for transportKind: TransportKind, certificateKeyPairs: SelfSignedCertificateKeyPairs, includeClientCertificateInTrustRoots: Bool - ) -> Config.Server { + ) -> ServerConfig { switch transportKind { case .posix: var config = self.makeDefaultPlaintextPosixServerConfig() - config.transportSecurity = .tls( - .mTLS( - certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)], - privateKey: .bytes(certificateKeyPairs.server.key, format: .der) - ) { - if includeClientCertificateInTrustRoots { - $0.trustRoots = .certificates([ - .bytes(certificateKeyPairs.client.certificate, format: .der) - ]) - } + config.security = .mTLS( + certificateChain: [.bytes(certificateKeyPairs.server.certificate, format: .der)], + privateKey: .bytes(certificateKeyPairs.server.key, format: .der) + ) { + if includeClientCertificateInTrustRoots { + $0.trustRoots = .certificates([ + .bytes(certificateKeyPairs.client.certificate, format: .der) + ]) } - ) + } return .posix(config) } } func withClientAndServer( - clientConfig: Config.Client, - serverConfig: Config.Server, + clientConfig: ClientConfig, + serverConfig: ServerConfig, _ test: (ControlClient) async throws -> Void ) async throws { try await withThrowingDiscardingTaskGroup { group in @@ -338,7 +344,7 @@ struct HTTP2TransportTLSEnabledTests { } } - private func makeServer(config: Config.Server) -> GRPCServer { + private func makeServer(config: ServerConfig) -> GRPCServer { let services = [ControlService()] switch config { @@ -346,7 +352,8 @@ struct HTTP2TransportTLSEnabledTests { let server = GRPCServer( transport: .http2NIOPosix( address: .ipv4(host: "127.0.0.1", port: 0), - config: config + transportSecurity: config.security, + config: config.transport ), services: services ) @@ -356,14 +363,19 @@ struct HTTP2TransportTLSEnabledTests { } private func makeClient( - config: Config.Client, + config: ClientConfig, target: any ResolvableTarget ) throws -> GRPCClient { let transport: any ClientTransport switch config { case .posix(let config): - transport = try HTTP2ClientTransport.Posix(target: target, config: config) + transport = try HTTP2ClientTransport.Posix( + target: target, + transportSecurity: config.security, + config: config.transport, + serviceConfig: ServiceConfig() + ) } return GRPCClient(transport: transport) diff --git a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTests.swift b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTests.swift index c9a69de..fd33913 100644 --- a/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTests.swift +++ b/Tests/GRPCNIOTransportHTTP2Tests/HTTP2TransportTests.swift @@ -148,7 +148,8 @@ final class HTTP2TransportTests: XCTestCase { let server = GRPCServer( transport: .http2NIOPosix( address: address, - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.enabledAlgorithms = compression } ), @@ -167,7 +168,8 @@ final class HTTP2TransportTests: XCTestCase { let server = GRPCServer( transport: .http2NIOTS( address: address, - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.enabledAlgorithms = compression } ), @@ -200,7 +202,8 @@ final class HTTP2TransportTests: XCTestCase { serviceConfig.loadBalancingConfig = [.roundRobin] transport = try HTTP2ClientTransport.Posix( target: target, - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.algorithm = compression $0.compression.enabledAlgorithms = enabledCompression }, @@ -213,7 +216,8 @@ final class HTTP2TransportTests: XCTestCase { serviceConfig.loadBalancingConfig = [.roundRobin] transport = try HTTP2ClientTransport.TransportServices( target: target, - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.compression.algorithm = compression $0.compression.enabledAlgorithms = enabledCompression }, @@ -1508,7 +1512,7 @@ final class HTTP2TransportTests: XCTestCase { try await withGRPCServer( transport: .http2NIOPosix( address: serverAddress, - config: .defaults(transportSecurity: .plaintext) + transportSecurity: .plaintext ), services: [ControlService()] ) { server in @@ -1521,7 +1525,8 @@ final class HTTP2TransportTests: XCTestCase { try await withGRPCClient( transport: .http2NIOPosix( target: target, - config: .defaults(transportSecurity: .plaintext) { + transportSecurity: .plaintext, + config: .defaults { $0.http2.authority = override } )