diff --git a/.licenseignore b/.licenseignore index 314cb7efe..6a74dfa65 100644 --- a/.licenseignore +++ b/.licenseignore @@ -40,3 +40,4 @@ LICENSE **/*.swift dev/protos/**/*.proto Examples/hello-world/Protos/HelloWorld.proto +**/*.pb diff --git a/Examples/echo/Sources/Subcommands/EchoService.swift b/Examples/echo/Sources/Subcommands/EchoService.swift new file mode 100644 index 000000000..e752af5b6 --- /dev/null +++ b/Examples/echo/Sources/Subcommands/EchoService.swift @@ -0,0 +1,55 @@ +/* + * Copyright 2024, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import GRPCCore + +struct EchoService: Echo_Echo.SimpleServiceProtocol { + func get( + request: Echo_EchoRequest, + context: ServerContext + ) async throws -> Echo_EchoResponse { + return .with { $0.text = request.text } + } + + func collect( + request: RPCAsyncSequence, + context: ServerContext + ) async throws -> Echo_EchoResponse { + let messages = try await request.reduce(into: []) { $0.append($1.text) } + let joined = messages.joined(separator: " ") + return .with { $0.text = joined } + } + + func expand( + request: Echo_EchoRequest, + response: RPCWriter, + context: ServerContext + ) async throws { + let parts = request.text.split(separator: " ") + let messages = parts.map { part in Echo_EchoResponse.with { $0.text = String(part) } } + try await response.write(contentsOf: messages) + } + + func update( + request: RPCAsyncSequence, + response: RPCWriter, + context: ServerContext + ) async throws { + for try await message in request { + try await response.write(.with { $0.text = message.text }) + } + } +} diff --git a/Examples/echo/Sources/Subcommands/Serve.swift b/Examples/echo/Sources/Subcommands/Serve.swift index 6bbb9ea82..17f38aec7 100644 --- a/Examples/echo/Sources/Subcommands/Serve.swift +++ b/Examples/echo/Sources/Subcommands/Serve.swift @@ -41,41 +41,3 @@ struct Serve: AsyncParsableCommand { } } } - -struct EchoService: Echo_Echo.SimpleServiceProtocol { - func get( - request: Echo_EchoRequest, - context: ServerContext - ) async throws -> Echo_EchoResponse { - return .with { $0.text = request.text } - } - - func collect( - request: RPCAsyncSequence, - context: ServerContext - ) async throws -> Echo_EchoResponse { - let messages = try await request.reduce(into: []) { $0.append($1.text) } - let joined = messages.joined(separator: " ") - return .with { $0.text = joined } - } - - func expand( - request: Echo_EchoRequest, - response: RPCWriter, - context: ServerContext - ) async throws { - let parts = request.text.split(separator: " ") - let messages = parts.map { part in Echo_EchoResponse.with { $0.text = String(part) } } - try await response.write(contentsOf: messages) - } - - func update( - request: RPCAsyncSequence, - response: RPCWriter, - context: ServerContext - ) async throws { - for try await message in request { - try await response.write(.with { $0.text = message.text }) - } - } -} diff --git a/Examples/reflection-server/.gitignore b/Examples/reflection-server/.gitignore new file mode 100644 index 000000000..0023a5340 --- /dev/null +++ b/Examples/reflection-server/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Examples/reflection-server/Package.swift b/Examples/reflection-server/Package.swift new file mode 100644 index 000000000..37a3942b2 --- /dev/null +++ b/Examples/reflection-server/Package.swift @@ -0,0 +1,45 @@ +// swift-tools-version:6.0 +/* + * Copyright 2024, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import PackageDescription + +let package = Package( + name: "reflection-server", + platforms: [.macOS(.v15)], + dependencies: [ + .package(url: "https://github.com/grpc/grpc-swift.git", branch: "main"), + .package(url: "https://github.com/grpc/grpc-swift-protobuf.git", branch: "main"), + .package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", branch: "main"), + .package(url: "https://github.com/grpc/grpc-swift-extras.git", branch: "main"), + .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"), + ], + targets: [ + .executableTarget( + name: "reflection-server", + dependencies: [ + .product(name: "GRPCCore", package: "grpc-swift"), + .product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"), + .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"), + .product(name: "GRPCReflectionService", package: "grpc-swift-extras"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + ], + resources: [ + .copy("DescriptorSets") + ] + ) + ] +) diff --git a/Examples/reflection-server/README.md b/Examples/reflection-server/README.md new file mode 100644 index 000000000..1743a8a08 --- /dev/null +++ b/Examples/reflection-server/README.md @@ -0,0 +1,61 @@ +# Reflection Server + +This example demonstrates the gRPC Reflection service which is described in more +detail in the [gRPC documentation](https://github.com/grpc/grpc/blob/6fa8043bf9befb070b846993b59a3348248e6566/doc/server-reflection.md). + +## Overview + +A 'reflection-server' command line tool that uses the reflection service implementation +from [grpc/grpc-swift-extras](https://github.com/grpc/grpc-swift-extras) and the +Echo service (see the 'echo' example). + +The reflection service requires you to initialize it with a set of Protobuf file +descriptors for the services you're offering. You can use `protoc` to create a +descriptor set including dependencies and source information for each service. + +The following command will generate a descriptor set at `path/to/output.pb` from +the `path/to/input.proto` file with source information and any imports used in +`input.proto`: + +```console +protoc --descriptor_set_out=path/to/output.pb path/to/input.proto \ + --include_source_info \ + --include_imports +``` + +## Usage + +Build and run the server using the CLI: + +```console +$ swift run reflection-server +Reflection server listening on [ipv4]127.0.0.1:31415 +``` + +You can use 'grpcurl' to query the reflection service. If you don't already have +it installed follow the instructions in the 'grpcurl' project's +[README](https://github.com/fullstorydev/grpcurl). + +You can list all services with: + +```console +$ grpcurl -plaintext 127.0.0.1:31415 list +echo.Echo +``` + +And describe the 'Get' method in the 'echo.Echo' service: + +```console +$ grpcurl -plaintext 127.0.0.1:31415 describe echo.Echo.Get +echo.Echo.Get is a method: +// Immediately returns an echo of a request. +rpc Get ( .echo.EchoRequest ) returns ( .echo.EchoResponse ); +``` + +You can also call the 'echo.Echo.Get' method: +```console +$ grpcurl -plaintext -d '{ "text": "Hello" }' 127.0.0.1:31415 echo.Echo.Get +{ + "text": "Hello" +} +``` diff --git a/Examples/reflection-server/Sources/DescriptorSets/echo.pb b/Examples/reflection-server/Sources/DescriptorSets/echo.pb new file mode 100644 index 000000000..dce4d22e3 Binary files /dev/null and b/Examples/reflection-server/Sources/DescriptorSets/echo.pb differ diff --git a/Examples/reflection-server/Sources/Echo/EchoService.swift b/Examples/reflection-server/Sources/Echo/EchoService.swift new file mode 120000 index 000000000..499718660 --- /dev/null +++ b/Examples/reflection-server/Sources/Echo/EchoService.swift @@ -0,0 +1 @@ +../../../echo/Sources/Subcommands/EchoService.swift \ No newline at end of file diff --git a/Examples/reflection-server/Sources/Generated/echo.grpc.swift b/Examples/reflection-server/Sources/Generated/echo.grpc.swift new file mode 100644 index 000000000..26c075247 --- /dev/null +++ b/Examples/reflection-server/Sources/Generated/echo.grpc.swift @@ -0,0 +1,994 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// DO NOT EDIT. +// swift-format-ignore-file +// +// Generated by the gRPC Swift generator plugin for the protocol buffer compiler. +// Source: echo.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/grpc/grpc-swift + +import GRPCCore +import GRPCProtobuf + +// MARK: - echo.Echo + +/// Namespace containing generated types for the "echo.Echo" service. +internal enum Echo_Echo { + /// Service descriptor for the "echo.Echo" service. + internal static let descriptor = GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo") + /// Namespace for method metadata. + internal enum Method { + /// Namespace for "Get" metadata. + internal enum Get { + /// Request type for "Get". + internal typealias Input = Echo_EchoRequest + /// Response type for "Get". + internal typealias Output = Echo_EchoResponse + /// Descriptor for "Get". + internal static let descriptor = GRPCCore.MethodDescriptor( + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo"), + method: "Get" + ) + } + /// Namespace for "Expand" metadata. + internal enum Expand { + /// Request type for "Expand". + internal typealias Input = Echo_EchoRequest + /// Response type for "Expand". + internal typealias Output = Echo_EchoResponse + /// Descriptor for "Expand". + internal static let descriptor = GRPCCore.MethodDescriptor( + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo"), + method: "Expand" + ) + } + /// Namespace for "Collect" metadata. + internal enum Collect { + /// Request type for "Collect". + internal typealias Input = Echo_EchoRequest + /// Response type for "Collect". + internal typealias Output = Echo_EchoResponse + /// Descriptor for "Collect". + internal static let descriptor = GRPCCore.MethodDescriptor( + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo"), + method: "Collect" + ) + } + /// Namespace for "Update" metadata. + internal enum Update { + /// Request type for "Update". + internal typealias Input = Echo_EchoRequest + /// Response type for "Update". + internal typealias Output = Echo_EchoResponse + /// Descriptor for "Update". + internal static let descriptor = GRPCCore.MethodDescriptor( + service: GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo"), + method: "Update" + ) + } + /// Descriptors for all methods in the "echo.Echo" service. + internal static let descriptors: [GRPCCore.MethodDescriptor] = [ + Get.descriptor, + Expand.descriptor, + Collect.descriptor, + Update.descriptor + ] + } +} + +extension GRPCCore.ServiceDescriptor { + /// Service descriptor for the "echo.Echo" service. + internal static let echo_Echo = GRPCCore.ServiceDescriptor(fullyQualifiedService: "echo.Echo") +} + +// MARK: echo.Echo (server) + +extension Echo_Echo { + /// Streaming variant of the service protocol for the "echo.Echo" 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 "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func get( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func expand( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func collect( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func update( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Service protocol for the "echo.Echo" 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: Echo_Echo.StreamingServiceProtocol { + /// Handle the "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` 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 `Echo_EchoResponse` message. + func get( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + + /// Handle the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func expand( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + + /// Handle the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` message. + func collect( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse + + /// Handle the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A streaming request of `Echo_EchoRequest` 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 `Echo_EchoResponse` messages. + func update( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse + } + + /// Simple service protocol for the "echo.Echo" 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: Echo_Echo.ServiceProtocol { + /// Handle the "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A `Echo_EchoRequest` 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 `Echo_EchoResponse` to respond with. + func get( + request: Echo_EchoRequest, + context: GRPCCore.ServerContext + ) async throws -> Echo_EchoResponse + + /// Handle the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A `Echo_EchoRequest` message. + /// - response: A response stream of `Echo_EchoResponse` 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 expand( + request: Echo_EchoRequest, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + + /// Handle the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A stream of `Echo_EchoRequest` 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 `Echo_EchoResponse` to respond with. + func collect( + request: GRPCCore.RPCAsyncSequence, + context: GRPCCore.ServerContext + ) async throws -> Echo_EchoResponse + + /// Handle the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A stream of `Echo_EchoRequest` messages. + /// - response: A response stream of `Echo_EchoResponse` 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 update( + request: GRPCCore.RPCAsyncSequence, + response: GRPCCore.RPCWriter, + context: GRPCCore.ServerContext + ) async throws + } +} + +// Default implementation of 'registerMethods(with:)'. +extension Echo_Echo.StreamingServiceProtocol { + internal func registerMethods(with router: inout GRPCCore.RPCRouter) { + router.registerHandler( + forMethod: Echo_Echo.Method.Get.descriptor, + deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + handler: { request, context in + try await self.get( + request: request, + context: context + ) + } + ) + router.registerHandler( + forMethod: Echo_Echo.Method.Expand.descriptor, + deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + handler: { request, context in + try await self.expand( + request: request, + context: context + ) + } + ) + router.registerHandler( + forMethod: Echo_Echo.Method.Collect.descriptor, + deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + handler: { request, context in + try await self.collect( + request: request, + context: context + ) + } + ) + router.registerHandler( + forMethod: Echo_Echo.Method.Update.descriptor, + deserializer: GRPCProtobuf.ProtobufDeserializer(), + serializer: GRPCProtobuf.ProtobufSerializer(), + handler: { request, context in + try await self.update( + request: request, + context: context + ) + } + ) + } +} + +// Default implementation of streaming methods from 'StreamingServiceProtocol'. +extension Echo_Echo.ServiceProtocol { + internal func get( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + let response = try await self.get( + request: GRPCCore.ServerRequest(stream: request), + context: context + ) + return GRPCCore.StreamingServerResponse(single: response) + } + + internal func expand( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + let response = try await self.expand( + request: GRPCCore.ServerRequest(stream: request), + context: context + ) + return response + } + + internal func collect( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + let response = try await self.collect( + request: request, + context: context + ) + return GRPCCore.StreamingServerResponse(single: response) + } +} + +// Default implementation of methods from 'ServiceProtocol'. +extension Echo_Echo.SimpleServiceProtocol { + internal func get( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.get( + request: request.message, + context: context + ), + metadata: [:] + ) + } + + internal func expand( + request: GRPCCore.ServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.expand( + request: request.message, + response: writer, + context: context + ) + return [:] + } + ) + } + + internal func collect( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.ServerResponse { + return GRPCCore.ServerResponse( + message: try await self.collect( + request: request.messages, + context: context + ), + metadata: [:] + ) + } + + internal func update( + request: GRPCCore.StreamingServerRequest, + context: GRPCCore.ServerContext + ) async throws -> GRPCCore.StreamingServerResponse { + return GRPCCore.StreamingServerResponse( + metadata: [:], + producer: { writer in + try await self.update( + request: request.messages, + response: writer, + context: context + ) + return [:] + } + ) + } +} + +// MARK: echo.Echo (client) + +extension Echo_Echo { + /// Generated client protocol for the "echo.Echo" service. + /// + /// You don't need to implement this protocol directly, use the generated + /// implementation, ``Client``. + internal protocol ClientProtocol: Sendable { + /// Call the "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` message. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 get( + 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 "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` message. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 expand( + 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 "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` messages. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 collect( + 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 "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` messages. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 update( + 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 "echo.Echo" 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 "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` message. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 get( + 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: Echo_Echo.Method.Get.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` message. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 expand( + 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: Echo_Echo.Method.Expand.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` messages. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 collect( + 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: Echo_Echo.Method.Collect.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` messages. + /// - serializer: A serializer for `Echo_EchoRequest` messages. + /// - deserializer: A deserializer for `Echo_EchoResponse` 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 update( + 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: Echo_Echo.Method.Update.descriptor, + serializer: serializer, + deserializer: deserializer, + options: options, + onResponse: handleResponse + ) + } + } +} + +// Helpers providing default arguments to 'ClientProtocol' methods. +extension Echo_Echo.ClientProtocol { + /// Call the "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` 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 get( + request: GRPCCore.ClientRequest, + 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.get( + request: request, + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - Parameters: + /// - request: A request containing a single `Echo_EchoRequest` 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 expand( + request: GRPCCore.ClientRequest, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + try await self.expand( + request: request, + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` 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 collect( + request: GRPCCore.StreamingClientRequest, + 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.collect( + request: request, + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - Parameters: + /// - request: A streaming request producing `Echo_EchoRequest` 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 update( + request: GRPCCore.StreamingClientRequest, + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + try await self.update( + request: request, + serializer: GRPCProtobuf.ProtobufSerializer(), + deserializer: GRPCProtobuf.ProtobufDeserializer(), + options: options, + onResponse: handleResponse + ) + } +} + +// Helpers providing sugared APIs for 'ClientProtocol' methods. +extension Echo_Echo.ClientProtocol { + /// Call the "Get" method. + /// + /// > Source IDL Documentation: + /// > + /// > Immediately returns an echo of a request. + /// + /// - 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 get( + _ message: Echo_EchoRequest, + metadata: GRPCCore.Metadata = [:], + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.ClientResponse) async throws -> Result = { response in + try response.message + } + ) async throws -> Result where Result: Sendable { + let request = GRPCCore.ClientRequest( + message: message, + metadata: metadata + ) + return try await self.get( + request: request, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Expand" method. + /// + /// > Source IDL Documentation: + /// > + /// > Splits a request into words and returns each word in a stream of messages. + /// + /// - 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 expand( + _ message: Echo_EchoRequest, + metadata: GRPCCore.Metadata = [:], + options: GRPCCore.CallOptions = .defaults, + onResponse handleResponse: @Sendable @escaping (GRPCCore.StreamingClientResponse) async throws -> Result + ) async throws -> Result where Result: Sendable { + let request = GRPCCore.ClientRequest( + message: message, + metadata: metadata + ) + return try await self.expand( + request: request, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Collect" method. + /// + /// > Source IDL Documentation: + /// > + /// > Collects a stream of messages and returns them concatenated when the caller closes. + /// + /// - 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 collect( + metadata: GRPCCore.Metadata = [:], + options: GRPCCore.CallOptions = .defaults, + 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: producer + ) + return try await self.collect( + request: request, + options: options, + onResponse: handleResponse + ) + } + + /// Call the "Update" method. + /// + /// > Source IDL Documentation: + /// > + /// > Streams back messages as they are received in an input stream. + /// + /// - 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 update( + metadata: GRPCCore.Metadata = [:], + options: GRPCCore.CallOptions = .defaults, + 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: producer + ) + return try await self.update( + request: request, + options: options, + onResponse: handleResponse + ) + } +} \ No newline at end of file diff --git a/Examples/reflection-server/Sources/Generated/echo.pb.swift b/Examples/reflection-server/Sources/Generated/echo.pb.swift new file mode 100644 index 000000000..88ef21ca9 --- /dev/null +++ b/Examples/reflection-server/Sources/Generated/echo.pb.swift @@ -0,0 +1,129 @@ +// DO NOT EDIT. +// swift-format-ignore-file +// swiftlint:disable all +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: echo.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that you are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +struct Echo_EchoRequest: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The text of a message to be echoed. + var text: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +struct Echo_EchoResponse: Sendable { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + /// The text of an echo response. + var text: String = String() + + var unknownFields = SwiftProtobuf.UnknownStorage() + + init() {} +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "echo" + +extension Echo_EchoRequest: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".EchoRequest" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "text"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.text) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.text.isEmpty { + try visitor.visitSingularStringField(value: self.text, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Echo_EchoRequest, rhs: Echo_EchoRequest) -> Bool { + if lhs.text != rhs.text {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Echo_EchoResponse: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + static let protoMessageName: String = _protobuf_package + ".EchoResponse" + static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "text"), + ] + + mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + // The use of inline closures is to circumvent an issue where the compiler + // allocates stack space for every case branch when no optimizations are + // enabled. https://github.com/apple/swift-protobuf/issues/1034 + switch fieldNumber { + case 1: try { try decoder.decodeSingularStringField(value: &self.text) }() + default: break + } + } + } + + func traverse(visitor: inout V) throws { + if !self.text.isEmpty { + try visitor.visitSingularStringField(value: self.text, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + static func ==(lhs: Echo_EchoResponse, rhs: Echo_EchoResponse) -> Bool { + if lhs.text != rhs.text {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/Examples/reflection-server/Sources/ReflectionServer.swift b/Examples/reflection-server/Sources/ReflectionServer.swift new file mode 100644 index 000000000..1e70abe2c --- /dev/null +++ b/Examples/reflection-server/Sources/ReflectionServer.swift @@ -0,0 +1,71 @@ +/* + * Copyright 2024, gRPC Authors All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import ArgumentParser +import Foundation +import GRPCCore +import GRPCNIOTransportHTTP2 +import GRPCProtobuf +import GRPCReflectionService + +@main +struct ReflectionServer: AsyncParsableCommand { + @Option(help: "The port to listen on") + var port: Int = 31415 + + func run() async throws { + // Find descriptor sets ('*.pb') bundled with this example. + let paths = Bundle.module.paths(forResourcesOfType: "pb", inDirectory: "DescriptorSets") + + // Start the server with the reflection service and the echo service. + let server = GRPCServer( + transport: .http2NIOPosix( + address: .ipv4(host: "127.0.0.1", port: self.port), + transportSecurity: .plaintext + ), + services: [ + try ReflectionService(descriptorSetFilePaths: paths), + EchoService(), + ] + ) + + try await withThrowingDiscardingTaskGroup { group in + group.addTask { try await server.serve() } + if let address = try await server.listeningAddress?.ipv4 { + print("Reflection server listening on \(address)") + print(String(repeating: "-", count: 80)) + + let example = """ + If you have grpcurl installed you can query the service to discover services + and make calls against them. You can install grpcurl by following the + instruction in its repository: https://github.com/fullstorydev/grpcurl + + Here are some example commands: + + List all services: + $ grpcurl -plaintext \(address.host):\(address.port) list + + Describe the 'Get' method in the 'echo.Echo' service: + $ grpcurl -plaintext \(address.host):\(address.port) describe echo.Echo.Get + + Call the 'echo.Echo.Get' method: + $ grpcurl -plaintext -d '{ "text": "Hello" }' \(address.host):\(address.port) echo.Echo.Get + """ + print(example) + } + } + } +} diff --git a/dev/protos/generate.sh b/dev/protos/generate.sh index 294145be4..0dc5cabb4 100755 --- a/dev/protos/generate.sh +++ b/dev/protos/generate.sh @@ -105,6 +105,18 @@ function generate_error_details_example { generate_grpc "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" } +function generate_reflection_server_example { + local proto="$here/examples/echo/echo.proto" + local output="$root/Examples/reflection-server/Sources/Generated" + local pb_output="$root/Examples/reflection-server/Sources/DescriptorSets/echo.pb" + + generate_message "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" + generate_grpc "$proto" "$(dirname "$proto")" "$output" "Visibility=Internal" + invoke_protoc --descriptor_set_out="$pb_output" "$proto" -I "$(dirname "$proto")" \ + --include_source_info \ + --include_imports +} + #- TESTS ---------------------------------------------------------------------- function generate_service_config_for_tests { @@ -128,6 +140,7 @@ generate_echo_example generate_helloworld_example generate_routeguide_example generate_error_details_example +generate_reflection_server_example # Tests generate_service_config_for_tests