Skip to content

Commit 5be11cd

Browse files
authored
Add reflection server example (#2149)
Motivation: We've added back the reflection server; we should have an example of how to use it. Modifications: - Add a reflection server example - Split the echo service into a separate file in the echo example so that it can be symlinked from the reflection server Result: More examples
1 parent dee1f1a commit 5be11cd

File tree

12 files changed

+1378
-38
lines changed

12 files changed

+1378
-38
lines changed

.licenseignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,4 @@ LICENSE
4040
**/*.swift
4141
dev/protos/**/*.proto
4242
Examples/hello-world/Protos/HelloWorld.proto
43+
**/*.pb
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2024, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import GRPCCore
18+
19+
struct EchoService: Echo_Echo.SimpleServiceProtocol {
20+
func get(
21+
request: Echo_EchoRequest,
22+
context: ServerContext
23+
) async throws -> Echo_EchoResponse {
24+
return .with { $0.text = request.text }
25+
}
26+
27+
func collect(
28+
request: RPCAsyncSequence<Echo_EchoRequest, any Error>,
29+
context: ServerContext
30+
) async throws -> Echo_EchoResponse {
31+
let messages = try await request.reduce(into: []) { $0.append($1.text) }
32+
let joined = messages.joined(separator: " ")
33+
return .with { $0.text = joined }
34+
}
35+
36+
func expand(
37+
request: Echo_EchoRequest,
38+
response: RPCWriter<Echo_EchoResponse>,
39+
context: ServerContext
40+
) async throws {
41+
let parts = request.text.split(separator: " ")
42+
let messages = parts.map { part in Echo_EchoResponse.with { $0.text = String(part) } }
43+
try await response.write(contentsOf: messages)
44+
}
45+
46+
func update(
47+
request: RPCAsyncSequence<Echo_EchoRequest, any Error>,
48+
response: RPCWriter<Echo_EchoResponse>,
49+
context: ServerContext
50+
) async throws {
51+
for try await message in request {
52+
try await response.write(.with { $0.text = message.text })
53+
}
54+
}
55+
}

Examples/echo/Sources/Subcommands/Serve.swift

Lines changed: 0 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -41,41 +41,3 @@ struct Serve: AsyncParsableCommand {
4141
}
4242
}
4343
}
44-
45-
struct EchoService: Echo_Echo.SimpleServiceProtocol {
46-
func get(
47-
request: Echo_EchoRequest,
48-
context: ServerContext
49-
) async throws -> Echo_EchoResponse {
50-
return .with { $0.text = request.text }
51-
}
52-
53-
func collect(
54-
request: RPCAsyncSequence<Echo_EchoRequest, any Error>,
55-
context: ServerContext
56-
) async throws -> Echo_EchoResponse {
57-
let messages = try await request.reduce(into: []) { $0.append($1.text) }
58-
let joined = messages.joined(separator: " ")
59-
return .with { $0.text = joined }
60-
}
61-
62-
func expand(
63-
request: Echo_EchoRequest,
64-
response: RPCWriter<Echo_EchoResponse>,
65-
context: ServerContext
66-
) async throws {
67-
let parts = request.text.split(separator: " ")
68-
let messages = parts.map { part in Echo_EchoResponse.with { $0.text = String(part) } }
69-
try await response.write(contentsOf: messages)
70-
}
71-
72-
func update(
73-
request: RPCAsyncSequence<Echo_EchoRequest, any Error>,
74-
response: RPCWriter<Echo_EchoResponse>,
75-
context: ServerContext
76-
) async throws {
77-
for try await message in request {
78-
try await response.write(.with { $0.text = message.text })
79-
}
80-
}
81-
}

Examples/reflection-server/.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// swift-tools-version:6.0
2+
/*
3+
* Copyright 2024, gRPC Authors All rights reserved.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import PackageDescription
19+
20+
let package = Package(
21+
name: "reflection-server",
22+
platforms: [.macOS(.v15)],
23+
dependencies: [
24+
.package(url: "https://github.com/grpc/grpc-swift.git", branch: "main"),
25+
.package(url: "https://github.com/grpc/grpc-swift-protobuf.git", branch: "main"),
26+
.package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", branch: "main"),
27+
.package(url: "https://github.com/grpc/grpc-swift-extras.git", branch: "main"),
28+
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
29+
],
30+
targets: [
31+
.executableTarget(
32+
name: "reflection-server",
33+
dependencies: [
34+
.product(name: "GRPCCore", package: "grpc-swift"),
35+
.product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"),
36+
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
37+
.product(name: "GRPCReflectionService", package: "grpc-swift-extras"),
38+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
39+
],
40+
resources: [
41+
.copy("DescriptorSets")
42+
]
43+
)
44+
]
45+
)

Examples/reflection-server/README.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Reflection Server
2+
3+
This example demonstrates the gRPC Reflection service which is described in more
4+
detail in the [gRPC documentation](https://github.com/grpc/grpc/blob/6fa8043bf9befb070b846993b59a3348248e6566/doc/server-reflection.md).
5+
6+
## Overview
7+
8+
A 'reflection-server' command line tool that uses the reflection service implementation
9+
from [grpc/grpc-swift-extras](https://github.com/grpc/grpc-swift-extras) and the
10+
Echo service (see the 'echo' example).
11+
12+
The reflection service requires you to initialize it with a set of Protobuf file
13+
descriptors for the services you're offering. You can use `protoc` to create a
14+
descriptor set including dependencies and source information for each service.
15+
16+
The following command will generate a descriptor set at `path/to/output.pb` from
17+
the `path/to/input.proto` file with source information and any imports used in
18+
`input.proto`:
19+
20+
```console
21+
protoc --descriptor_set_out=path/to/output.pb path/to/input.proto \
22+
--include_source_info \
23+
--include_imports
24+
```
25+
26+
## Usage
27+
28+
Build and run the server using the CLI:
29+
30+
```console
31+
$ swift run reflection-server
32+
Reflection server listening on [ipv4]127.0.0.1:31415
33+
```
34+
35+
You can use 'grpcurl' to query the reflection service. If you don't already have
36+
it installed follow the instructions in the 'grpcurl' project's
37+
[README](https://github.com/fullstorydev/grpcurl).
38+
39+
You can list all services with:
40+
41+
```console
42+
$ grpcurl -plaintext 127.0.0.1:31415 list
43+
echo.Echo
44+
```
45+
46+
And describe the 'Get' method in the 'echo.Echo' service:
47+
48+
```console
49+
$ grpcurl -plaintext 127.0.0.1:31415 describe echo.Echo.Get
50+
echo.Echo.Get is a method:
51+
// Immediately returns an echo of a request.
52+
rpc Get ( .echo.EchoRequest ) returns ( .echo.EchoResponse );
53+
```
54+
55+
You can also call the 'echo.Echo.Get' method:
56+
```console
57+
$ grpcurl -plaintext -d '{ "text": "Hello" }' 127.0.0.1:31415 echo.Echo.Get
58+
{
59+
"text": "Hello"
60+
}
61+
```
Binary file not shown.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../echo/Sources/Subcommands/EchoService.swift

0 commit comments

Comments
 (0)