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 } )