Skip to content

Commit

Permalink
Merge branch 'main' into bodyparts-var
Browse files Browse the repository at this point in the history
  • Loading branch information
glbrntt authored Jan 31, 2025
2 parents 9d12f1a + 2ed95d8 commit 3dccf85
Show file tree
Hide file tree
Showing 31 changed files with 581 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
linux_5_9_enabled: false
linux_5_10_enabled: false
linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"

benchmarks:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
linux_5_9_enabled: false
linux_5_10_enabled: false
linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_1_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"

construct-examples-matrix:
Expand Down
8 changes: 8 additions & 0 deletions Examples/echo-metadata/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
43 changes: 43 additions & 0 deletions Examples/echo-metadata/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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: "echo-metadata",
platforms: [.macOS("15.0")],
dependencies: [
.package(url: "https://github.com/grpc/grpc-swift.git", from: "2.0.0"),
.package(url: "https://github.com/grpc/grpc-swift-protobuf.git", from: "1.0.0"),
.package(url: "https://github.com/grpc/grpc-swift-nio-transport.git", from: "1.0.0"),
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
],
targets: [
.executableTarget(
name: "echo-metadata",
dependencies: [
.product(name: "GRPCCore", package: "grpc-swift"),
.product(name: "GRPCNIOTransportHTTP2", package: "grpc-swift-nio-transport"),
.product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
.product(name: "ArgumentParser", package: "swift-argument-parser"),
],
plugins: [
.plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf")
]
)
]
)
58 changes: 58 additions & 0 deletions Examples/echo-metadata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Echo-Metadata

This example demonstrates how to interact with `Metadata` on RPCs: how to set and read it on unary
and streaming requests, as well as how to set and read both initial and trailing metadata on unary
and streaming responses. This is done using a simple 'echo' server and client and the SwiftNIO
based HTTP/2 transport.

## Overview

An `echo-metadata` command line tool that uses generated stubs for an 'echo-metadata' service
which allows you to start a server and to make requests against it.

You can use any of the client's subcommands (`get`, `collect`, `expand` and `update`) to send the
provided `message` as both the request's message, and as the value for the `echo-message` key in
the request's metadata.

The server will then echo back the message and the metadata's `echo-message` key-value pair sent
by the client. The request's metadata will be echoed both in the initial and the trailing metadata.

The tool uses the [SwiftNIO](https://github.com/grpc/grpc-swift-nio-transport) HTTP/2 transport.

## Prerequisites

You must have the Protocol Buffers compiler (`protoc`) installed. You can find
the instructions for doing this in the [gRPC Swift Protobuf documentation][0].
The `swift` commands below are all prefixed with `PROTOC_PATH=$(which protoc)`,
this is to let the build system know where `protoc` is located so that it can
generate stubs for you. You can read more about it in the [gRPC Swift Protobuf
documentation][1].

## Usage

Build and run the server using the CLI:

```console
$ PROTOC_PATH=$(which protoc) swift run echo-metadata serve
Echo-Metadata listening on [ipv4]127.0.0.1:1234
```

Use the CLI to run the client and make a `get` (unary) request:

```console
$ PROTOC_PATH=$(which protoc) swift run echo-metadata get --message "hello"
get → metadata: [("echo-message", "hello")]
get → message: hello
get ← initial metadata: [("echo-message", "hello")]
get ← message: hello
get ← trailing metadata: [("echo-message", "hello")]
```

Get help with the CLI by running:

```console
$ PROTOC_PATH=$(which protoc) swift run echo-metadata --help
```

[0]: https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/installing-protoc
[1]: https://swiftpackageindex.com/grpc/grpc-swift-protobuf/documentation/grpcprotobuf/generating-stubs
35 changes: 35 additions & 0 deletions Examples/echo-metadata/Sources/ClientArguments.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright 2025, 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 GRPCNIOTransportHTTP2

struct ClientArguments: ParsableArguments {
@Option(help: "The server's listening port")
var port: Int = 1234

@Option(
help:
"Message to send to the server. It will also be sent in the request's metadata as the value for `echo-message`."
)
var message: String
}

extension ClientArguments {
var target: any ResolvableTarget {
return .ipv4(host: "127.0.0.1", port: self.port)
}
}
27 changes: 27 additions & 0 deletions Examples/echo-metadata/Sources/EchoMetadata.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright 2025, 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 GRPCCore

@main
struct EchoMetadata: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "echo-metadata",
abstract: "A multi-tool to run an echo-metadata server and execute RPCs against it.",
subcommands: [Serve.self, Get.self, Collect.self, Update.self, Expand.self]
)
}
73 changes: 73 additions & 0 deletions Examples/echo-metadata/Sources/EchoService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright 2025, 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.ServiceProtocol {
func get(
request: ServerRequest<Echo_EchoRequest>,
context: ServerContext
) async throws -> ServerResponse<Echo_EchoResponse> {
let responseMetadata = Metadata(request.metadata.filter({ $0.key.starts(with: "echo-") }))
return ServerResponse(
message: .with { $0.text = request.message.text },
metadata: responseMetadata,
trailingMetadata: responseMetadata
)
}

func collect(
request: StreamingServerRequest<Echo_EchoRequest>,
context: ServerContext
) async throws -> ServerResponse<Echo_EchoResponse> {
let responseMetadata = Metadata(request.metadata.filter({ $0.key.starts(with: "echo-") }))
let messages = try await request.messages.reduce(into: []) { $0.append($1.text) }
let joined = messages.joined(separator: " ")

return ServerResponse(
message: .with { $0.text = joined },
metadata: responseMetadata,
trailingMetadata: responseMetadata
)
}

func expand(
request: ServerRequest<Echo_EchoRequest>,
context: ServerContext
) async throws -> StreamingServerResponse<Echo_EchoResponse> {
let responseMetadata = Metadata(request.metadata.filter({ $0.key.starts(with: "echo-") }))
let parts = request.message.text.split(separator: " ")
let messages = parts.map { part in Echo_EchoResponse.with { $0.text = String(part) } }

return StreamingServerResponse(metadata: responseMetadata) { writer in
try await writer.write(contentsOf: messages)
return responseMetadata
}
}

func update(
request: StreamingServerRequest<Echo_EchoRequest>,
context: ServerContext
) async throws -> StreamingServerResponse<Echo_EchoResponse> {
let responseMetadata = Metadata(request.metadata.filter({ $0.key.starts(with: "echo-") }))
return StreamingServerResponse(metadata: responseMetadata) { writer in
for try await message in request.messages {
try await writer.write(.with { $0.text = message.text })
}
return responseMetadata
}
}
}
1 change: 1 addition & 0 deletions Examples/echo-metadata/Sources/Protos/echo
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"generate": {
"clients": true,
"servers": true,
"messages": true
}
}
58 changes: 58 additions & 0 deletions Examples/echo-metadata/Sources/Subcommands/Collect.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2025, 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 GRPCCore
import GRPCNIOTransportHTTP2

struct Collect: AsyncParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Makes a client streaming RPC to the echo-metadata server."
)

@OptionGroup
var arguments: ClientArguments

func run() async throws {
try await withGRPCClient(
transport: .http2NIOPosix(
target: self.arguments.target,
transportSecurity: .plaintext
)
) { client in
let echo = Echo_Echo.Client(wrapping: client)
let requestMetadata: Metadata = ["echo-message": "\(arguments.message)"]

print("collect → metadata: \(requestMetadata)")
try await echo.collect(metadata: requestMetadata) { writer in
for part in self.arguments.message.split(separator: " ") {
print("collect → \(part)")
try await writer.write(.with { $0.text = String(part) })
}
} onResponse: { response in
let initialMetadata = Metadata(response.metadata.filter({ $0.key.starts(with: "echo-") }))
print("collect ← initial metadata: \(initialMetadata)")

print("collect ← message: \(try response.message.text)")

let trailingMetadata = Metadata(
response.trailingMetadata.filter({ $0.key.starts(with: "echo-") })
)
print("collect ← trailing metadata: \(trailingMetadata)")
}
}
}
}
Loading

0 comments on commit 3dccf85

Please sign in to comment.