Browse Source

Add a service for testing (#1916)

Motivation:

The interop tests cover core functionality. However, we'd like to test
more scenarios in more depth. To do so we can use a service which
responds however the client asks it to. This allows, for example, the
client to ask the server to send a status-only response, or to respond
with N messages before failing with a particular status.

Modifications:

- Add a definition for a 'control' service
- Implement the control service

Result:

We have a service which the client can control the behaviour of allowing
us to exercise different code paths.
George Barnett 1 year ago
parent
commit
8255b2e4b4

+ 194 - 0
Tests/GRPCHTTP2TransportTests/ControlService.swift

@@ -0,0 +1,194 @@
+/*
+ * 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
+
+import struct Foundation.Data
+
+struct ControlService: ControlStreamingServiceProtocol {
+  func unary(
+    request: ServerRequest.Stream<Control.Method.Unary.Input>
+  ) async throws -> ServerResponse.Stream<Control.Method.Unary.Output> {
+    try await self.handle(request: request)
+  }
+
+  func serverStream(
+    request: ServerRequest.Stream<Control.Method.ServerStream.Input>
+  ) async throws -> ServerResponse.Stream<Control.Method.ServerStream.Output> {
+    try await self.handle(request: request)
+  }
+
+  func clientStream(
+    request: ServerRequest.Stream<Control.Method.ClientStream.Input>
+  ) async throws -> ServerResponse.Stream<Control.Method.ClientStream.Output> {
+    try await self.handle(request: request)
+  }
+
+  func bidiStream(
+    request: ServerRequest.Stream<Control.Method.BidiStream.Input>
+  ) async throws -> ServerResponse.Stream<Control.Method.BidiStream.Output> {
+    try await self.handle(request: request)
+  }
+}
+
+extension ControlService {
+  private func handle(
+    request: ServerRequest.Stream<ControlInput>
+  ) async throws -> ServerResponse.Stream<ControlOutput> {
+    var iterator = request.messages.makeAsyncIterator()
+
+    guard let message = try await iterator.next() else {
+      // Empty input stream, empty output stream.
+      return ServerResponse.Stream { _ in [:] }
+    }
+
+    // Check if the request is for a trailers-only response.
+    if message.hasStatus, message.isTrailersOnly {
+      let trailers = message.echoMetadataInTrailers ? request.metadata.echo() : [:]
+      let code = Status.Code(rawValue: message.status.code.rawValue).flatMap { RPCError.Code($0) }
+
+      if let code = code {
+        throw RPCError(code: code, message: message.status.message, metadata: trailers)
+      } else {
+        // Invalid code, the request is invalid, so throw an appropriate error.
+        throw RPCError(
+          code: .invalidArgument,
+          message: "Trailers only response must use a non-OK status code"
+        )
+      }
+    }
+
+    // Not a trailers-only response. Should the metadata be echo'd back?
+    let metadata = message.echoMetadataInHeaders ? request.metadata.echo() : [:]
+
+    // The iterator needs to be transferred into the response. This is okay: we won't touch the
+    // iterator again from the current concurrency domain.
+    let transfer = UnsafeTransfer(iterator)
+
+    return ServerResponse.Stream(metadata: metadata) { writer in
+      // Finish dealing with the first message.
+      switch try await self.processMessage(message, metadata: request.metadata, writer: writer) {
+      case .return(let metadata):
+        return metadata
+      case .continue:
+        ()
+      }
+
+      var iterator = transfer.wrappedValue
+      // Process the rest of the messages.
+      while let message = try await iterator.next() {
+        switch try await self.processMessage(message, metadata: request.metadata, writer: writer) {
+        case .return(let metadata):
+          return metadata
+        case .continue:
+          ()
+        }
+      }
+
+      // Input stream finished without explicitly setting a status; finish the RPC cleanly.
+      return [:]
+    }
+  }
+
+  private enum NextProcessingStep {
+    case `return`(Metadata)
+    case `continue`
+  }
+
+  private func processMessage(
+    _ input: ControlInput,
+    metadata: Metadata,
+    writer: RPCWriter<ControlOutput>
+  ) async throws -> NextProcessingStep {
+    // If messages were requested, build a response and send them back.
+    if input.numberOfMessages > 0 {
+      let output = ControlOutput.with {
+        $0.payload = Data(
+          repeating: UInt8(truncatingIfNeeded: input.messageParams.content),
+          count: Int(input.messageParams.size)
+        )
+      }
+
+      for _ in 0 ..< input.numberOfMessages {
+        try await writer.write(output)
+      }
+    }
+
+    // Check whether the RPC should be finished (i.e. the input `hasStatus`).
+    guard input.hasStatus else {
+      if input.echoMetadataInTrailers {
+        // There was no status in the input, but echo metadata in trailers was set. This is an
+        // implicit 'ok' status.
+        let trailers = input.echoMetadataInTrailers ? metadata.echo() : [:]
+        return .return(trailers)
+      } else {
+        // No status, and not echoing back metadata. Continue consuming the input stream.
+        return .continue
+      }
+    }
+
+    // Build the trailers.
+    let trailers = input.echoMetadataInTrailers ? metadata.echo() : [:]
+
+    if input.status.code == .ok {
+      return .return(trailers)
+    }
+
+    // Non-OK status code, throw an error.
+    let code = Status.Code(rawValue: input.status.code.rawValue).flatMap { RPCError.Code($0) }
+
+    if let code = code {
+      // Valid error code, throw it.
+      throw RPCError(code: code, message: input.status.message, metadata: trailers)
+    } else {
+      // Invalid error code, throw an appropriate error.
+      throw RPCError(
+        code: .invalidArgument,
+        message: "Invalid error code '\(input.status.code)'"
+      )
+    }
+  }
+}
+
+extension Metadata {
+  fileprivate func echo() -> Self {
+    var copy = Metadata()
+    copy.reserveCapacity(self.count)
+
+    for (key, value) in self {
+      // Header field names mustn't contain ":".
+      let key = "echo-" + key.replacingOccurrences(of: ":", with: "")
+      switch value {
+      case .string(let stringValue):
+        copy.addString(stringValue, forKey: key)
+      case .binary(let binaryValue):
+        copy.addBinary(binaryValue, forKey: key)
+      }
+    }
+
+    return copy
+  }
+}
+
+private struct UnsafeTransfer<Wrapped> {
+  var wrappedValue: Wrapped
+
+  init(_ wrappedValue: Wrapped) {
+    self.wrappedValue = wrappedValue
+  }
+}
+
+extension UnsafeTransfer: @unchecked Sendable {}

+ 345 - 0
Tests/GRPCHTTP2TransportTests/Generated/control.grpc.swift

@@ -0,0 +1,345 @@
+//
+// 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.
+
+// DO NOT EDIT.
+// swift-format-ignore-file
+//
+// Generated by the gRPC Swift generator plugin for the protocol buffer compiler.
+// Source: control.proto
+//
+// For information on using the generated types, please see the documentation:
+//   https://github.com/grpc/grpc-swift
+
+import GRPCCore
+import GRPCProtobuf
+
+internal enum Control {
+    internal enum Method {
+        internal enum Unary {
+            internal typealias Input = ControlInput
+            internal typealias Output = ControlOutput
+            internal static let descriptor = MethodDescriptor(
+                service: "Control",
+                method: "Unary"
+            )
+        }
+        internal enum ServerStream {
+            internal typealias Input = ControlInput
+            internal typealias Output = ControlOutput
+            internal static let descriptor = MethodDescriptor(
+                service: "Control",
+                method: "ServerStream"
+            )
+        }
+        internal enum ClientStream {
+            internal typealias Input = ControlInput
+            internal typealias Output = ControlOutput
+            internal static let descriptor = MethodDescriptor(
+                service: "Control",
+                method: "ClientStream"
+            )
+        }
+        internal enum BidiStream {
+            internal typealias Input = ControlInput
+            internal typealias Output = ControlOutput
+            internal static let descriptor = MethodDescriptor(
+                service: "Control",
+                method: "BidiStream"
+            )
+        }
+        internal static let descriptors: [MethodDescriptor] = [
+            Unary.descriptor,
+            ServerStream.descriptor,
+            ClientStream.descriptor,
+            BidiStream.descriptor
+        ]
+    }
+    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+    internal typealias StreamingServiceProtocol = ControlStreamingServiceProtocol
+    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+    internal typealias ServiceProtocol = ControlServiceProtocol
+    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+    internal typealias ClientProtocol = ControlClientProtocol
+    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+    internal typealias Client = ControlClient
+}
+
+/// A controllable service for testing.
+///
+/// The control service has one RPC of each kind, the input to each RPC controls
+/// the output.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+internal protocol ControlStreamingServiceProtocol: GRPCCore.RegistrableRPCService {
+    func unary(request: ServerRequest.Stream<Control.Method.Unary.Input>) async throws -> ServerResponse.Stream<Control.Method.Unary.Output>
+
+    func serverStream(request: ServerRequest.Stream<Control.Method.ServerStream.Input>) async throws -> ServerResponse.Stream<Control.Method.ServerStream.Output>
+
+    func clientStream(request: ServerRequest.Stream<Control.Method.ClientStream.Input>) async throws -> ServerResponse.Stream<Control.Method.ClientStream.Output>
+
+    func bidiStream(request: ServerRequest.Stream<Control.Method.BidiStream.Input>) async throws -> ServerResponse.Stream<Control.Method.BidiStream.Output>
+}
+
+/// Conformance to `GRPCCore.RegistrableRPCService`.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+extension Control.StreamingServiceProtocol {
+    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+    internal func registerMethods(with router: inout GRPCCore.RPCRouter) {
+        router.registerHandler(
+            forMethod: Control.Method.Unary.descriptor,
+            deserializer: ProtobufDeserializer<Control.Method.Unary.Input>(),
+            serializer: ProtobufSerializer<Control.Method.Unary.Output>(),
+            handler: { request in
+                try await self.unary(request: request)
+            }
+        )
+        router.registerHandler(
+            forMethod: Control.Method.ServerStream.descriptor,
+            deserializer: ProtobufDeserializer<Control.Method.ServerStream.Input>(),
+            serializer: ProtobufSerializer<Control.Method.ServerStream.Output>(),
+            handler: { request in
+                try await self.serverStream(request: request)
+            }
+        )
+        router.registerHandler(
+            forMethod: Control.Method.ClientStream.descriptor,
+            deserializer: ProtobufDeserializer<Control.Method.ClientStream.Input>(),
+            serializer: ProtobufSerializer<Control.Method.ClientStream.Output>(),
+            handler: { request in
+                try await self.clientStream(request: request)
+            }
+        )
+        router.registerHandler(
+            forMethod: Control.Method.BidiStream.descriptor,
+            deserializer: ProtobufDeserializer<Control.Method.BidiStream.Input>(),
+            serializer: ProtobufSerializer<Control.Method.BidiStream.Output>(),
+            handler: { request in
+                try await self.bidiStream(request: request)
+            }
+        )
+    }
+}
+
+/// A controllable service for testing.
+///
+/// The control service has one RPC of each kind, the input to each RPC controls
+/// the output.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+internal protocol ControlServiceProtocol: Control.StreamingServiceProtocol {
+    func unary(request: ServerRequest.Single<Control.Method.Unary.Input>) async throws -> ServerResponse.Single<Control.Method.Unary.Output>
+
+    func serverStream(request: ServerRequest.Single<Control.Method.ServerStream.Input>) async throws -> ServerResponse.Stream<Control.Method.ServerStream.Output>
+
+    func clientStream(request: ServerRequest.Stream<Control.Method.ClientStream.Input>) async throws -> ServerResponse.Single<Control.Method.ClientStream.Output>
+
+    func bidiStream(request: ServerRequest.Stream<Control.Method.BidiStream.Input>) async throws -> ServerResponse.Stream<Control.Method.BidiStream.Output>
+}
+
+/// Partial conformance to `ControlStreamingServiceProtocol`.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+extension Control.ServiceProtocol {
+    internal func unary(request: ServerRequest.Stream<Control.Method.Unary.Input>) async throws -> ServerResponse.Stream<Control.Method.Unary.Output> {
+        let response = try await self.unary(request: ServerRequest.Single(stream: request))
+        return ServerResponse.Stream(single: response)
+    }
+
+    internal func serverStream(request: ServerRequest.Stream<Control.Method.ServerStream.Input>) async throws -> ServerResponse.Stream<Control.Method.ServerStream.Output> {
+        let response = try await self.serverStream(request: ServerRequest.Single(stream: request))
+        return response
+    }
+
+    internal func clientStream(request: ServerRequest.Stream<Control.Method.ClientStream.Input>) async throws -> ServerResponse.Stream<Control.Method.ClientStream.Output> {
+        let response = try await self.clientStream(request: request)
+        return ServerResponse.Stream(single: response)
+    }
+}
+
+/// A controllable service for testing.
+///
+/// The control service has one RPC of each kind, the input to each RPC controls
+/// the output.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+internal protocol ControlClientProtocol: Sendable {
+    func unary<R>(
+        request: ClientRequest.Single<Control.Method.Unary.Input>,
+        serializer: some MessageSerializer<Control.Method.Unary.Input>,
+        deserializer: some MessageDeserializer<Control.Method.Unary.Output>,
+        options: CallOptions,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.Unary.Output>) async throws -> R
+    ) async throws -> R where R: Sendable
+
+    func serverStream<R>(
+        request: ClientRequest.Single<Control.Method.ServerStream.Input>,
+        serializer: some MessageSerializer<Control.Method.ServerStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.ServerStream.Output>,
+        options: CallOptions,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.ServerStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable
+
+    func clientStream<R>(
+        request: ClientRequest.Stream<Control.Method.ClientStream.Input>,
+        serializer: some MessageSerializer<Control.Method.ClientStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.ClientStream.Output>,
+        options: CallOptions,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.ClientStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable
+
+    func bidiStream<R>(
+        request: ClientRequest.Stream<Control.Method.BidiStream.Input>,
+        serializer: some MessageSerializer<Control.Method.BidiStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.BidiStream.Output>,
+        options: CallOptions,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.BidiStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable
+}
+
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+extension Control.ClientProtocol {
+    internal func unary<R>(
+        request: ClientRequest.Single<Control.Method.Unary.Input>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.Unary.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.unary(
+            request: request,
+            serializer: ProtobufSerializer<Control.Method.Unary.Input>(),
+            deserializer: ProtobufDeserializer<Control.Method.Unary.Output>(),
+            options: options,
+            body
+        )
+    }
+
+    internal func serverStream<R>(
+        request: ClientRequest.Single<Control.Method.ServerStream.Input>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.ServerStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.serverStream(
+            request: request,
+            serializer: ProtobufSerializer<Control.Method.ServerStream.Input>(),
+            deserializer: ProtobufDeserializer<Control.Method.ServerStream.Output>(),
+            options: options,
+            body
+        )
+    }
+
+    internal func clientStream<R>(
+        request: ClientRequest.Stream<Control.Method.ClientStream.Input>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.ClientStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.clientStream(
+            request: request,
+            serializer: ProtobufSerializer<Control.Method.ClientStream.Input>(),
+            deserializer: ProtobufDeserializer<Control.Method.ClientStream.Output>(),
+            options: options,
+            body
+        )
+    }
+
+    internal func bidiStream<R>(
+        request: ClientRequest.Stream<Control.Method.BidiStream.Input>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.BidiStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.bidiStream(
+            request: request,
+            serializer: ProtobufSerializer<Control.Method.BidiStream.Input>(),
+            deserializer: ProtobufDeserializer<Control.Method.BidiStream.Output>(),
+            options: options,
+            body
+        )
+    }
+}
+
+/// A controllable service for testing.
+///
+/// The control service has one RPC of each kind, the input to each RPC controls
+/// the output.
+@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
+internal struct ControlClient: Control.ClientProtocol {
+    private let client: GRPCCore.GRPCClient
+
+    internal init(client: GRPCCore.GRPCClient) {
+        self.client = client
+    }
+
+    internal func unary<R>(
+        request: ClientRequest.Single<Control.Method.Unary.Input>,
+        serializer: some MessageSerializer<Control.Method.Unary.Input>,
+        deserializer: some MessageDeserializer<Control.Method.Unary.Output>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.Unary.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.client.unary(
+            request: request,
+            descriptor: Control.Method.Unary.descriptor,
+            serializer: serializer,
+            deserializer: deserializer,
+            options: options,
+            handler: body
+        )
+    }
+
+    internal func serverStream<R>(
+        request: ClientRequest.Single<Control.Method.ServerStream.Input>,
+        serializer: some MessageSerializer<Control.Method.ServerStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.ServerStream.Output>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.ServerStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.client.serverStreaming(
+            request: request,
+            descriptor: Control.Method.ServerStream.descriptor,
+            serializer: serializer,
+            deserializer: deserializer,
+            options: options,
+            handler: body
+        )
+    }
+
+    internal func clientStream<R>(
+        request: ClientRequest.Stream<Control.Method.ClientStream.Input>,
+        serializer: some MessageSerializer<Control.Method.ClientStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.ClientStream.Output>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Single<Control.Method.ClientStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.client.clientStreaming(
+            request: request,
+            descriptor: Control.Method.ClientStream.descriptor,
+            serializer: serializer,
+            deserializer: deserializer,
+            options: options,
+            handler: body
+        )
+    }
+
+    internal func bidiStream<R>(
+        request: ClientRequest.Stream<Control.Method.BidiStream.Input>,
+        serializer: some MessageSerializer<Control.Method.BidiStream.Input>,
+        deserializer: some MessageDeserializer<Control.Method.BidiStream.Output>,
+        options: CallOptions = .defaults,
+        _ body: @Sendable @escaping (ClientResponse.Stream<Control.Method.BidiStream.Output>) async throws -> R
+    ) async throws -> R where R: Sendable {
+        try await self.client.bidirectionalStreaming(
+            request: request,
+            descriptor: Control.Method.BidiStream.descriptor,
+            serializer: serializer,
+            deserializer: deserializer,
+            options: options,
+            handler: body
+        )
+    }
+}

+ 459 - 0
Tests/GRPCHTTP2TransportTests/Generated/control.pb.swift

@@ -0,0 +1,459 @@
+// DO NOT EDIT.
+// swift-format-ignore-file
+//
+// Generated by the Swift generator plugin for the protocol buffer compiler.
+// Source: control.proto
+//
+// For information on using the generated types, please see the documentation:
+//   https://github.com/apple/swift-protobuf/
+
+//
+// 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 Foundation
+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
+}
+
+enum StatusCode: SwiftProtobuf.Enum {
+  typealias RawValue = Int
+  case ok // = 0
+  case cancelled // = 1
+  case unknown // = 2
+  case invalidArgument // = 3
+  case deadlineExceeded // = 4
+  case notFound // = 5
+  case alreadyExists // = 6
+  case permissionDenied // = 7
+  case resourceExhausted // = 8
+  case failedPrecondition // = 9
+  case aborted // = 10
+  case outOfRange // = 11
+  case unimplemented // = 12
+  case `internal` // = 13
+  case unavailable // = 14
+  case dataLoss // = 15
+  case unauthenticated // = 16
+  case UNRECOGNIZED(Int)
+
+  init() {
+    self = .ok
+  }
+
+  init?(rawValue: Int) {
+    switch rawValue {
+    case 0: self = .ok
+    case 1: self = .cancelled
+    case 2: self = .unknown
+    case 3: self = .invalidArgument
+    case 4: self = .deadlineExceeded
+    case 5: self = .notFound
+    case 6: self = .alreadyExists
+    case 7: self = .permissionDenied
+    case 8: self = .resourceExhausted
+    case 9: self = .failedPrecondition
+    case 10: self = .aborted
+    case 11: self = .outOfRange
+    case 12: self = .unimplemented
+    case 13: self = .internal
+    case 14: self = .unavailable
+    case 15: self = .dataLoss
+    case 16: self = .unauthenticated
+    default: self = .UNRECOGNIZED(rawValue)
+    }
+  }
+
+  var rawValue: Int {
+    switch self {
+    case .ok: return 0
+    case .cancelled: return 1
+    case .unknown: return 2
+    case .invalidArgument: return 3
+    case .deadlineExceeded: return 4
+    case .notFound: return 5
+    case .alreadyExists: return 6
+    case .permissionDenied: return 7
+    case .resourceExhausted: return 8
+    case .failedPrecondition: return 9
+    case .aborted: return 10
+    case .outOfRange: return 11
+    case .unimplemented: return 12
+    case .internal: return 13
+    case .unavailable: return 14
+    case .dataLoss: return 15
+    case .unauthenticated: return 16
+    case .UNRECOGNIZED(let i): return i
+    }
+  }
+
+}
+
+#if swift(>=4.2)
+
+extension StatusCode: CaseIterable {
+  // The compiler won't synthesize support with the UNRECOGNIZED case.
+  static let allCases: [StatusCode] = [
+    .ok,
+    .cancelled,
+    .unknown,
+    .invalidArgument,
+    .deadlineExceeded,
+    .notFound,
+    .alreadyExists,
+    .permissionDenied,
+    .resourceExhausted,
+    .failedPrecondition,
+    .aborted,
+    .outOfRange,
+    .unimplemented,
+    .internal,
+    .unavailable,
+    .dataLoss,
+    .unauthenticated,
+  ]
+}
+
+#endif  // swift(>=4.2)
+
+struct ControlInput {
+  // 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.
+
+  /// Whether metadata should be echo'd back in the initial metadata.
+  ///
+  /// Ignored if the initial metadata has already been sent back to the
+  /// client.
+  ///
+  /// Each header field name in the request headers will be prefixed with
+  /// "echo-". For example the header field name "foo" will be returned
+  /// as "echo-foo. Note that semicolons aren't valid in HTTP header field
+  /// names (apart from pseudo headers). As such all semicolons should be
+  /// removed (":path" should become "echo-path").
+  var echoMetadataInHeaders: Bool = false
+
+  /// Parameters for response messages.
+  var messageParams: PayloadParameters {
+    get {return _messageParams ?? PayloadParameters()}
+    set {_messageParams = newValue}
+  }
+  /// Returns true if `messageParams` has been explicitly set.
+  var hasMessageParams: Bool {return self._messageParams != nil}
+  /// Clears the value of `messageParams`. Subsequent reads from it will return its default value.
+  mutating func clearMessageParams() {self._messageParams = nil}
+
+  /// The number of response messages.
+  var numberOfMessages: Int32 = 0
+
+  /// The status code and message to use at the end of the RPC.
+  ///
+  /// If this is set then the RPC will be ended after `number_of_messages`
+  /// messages have been sent back to the client.
+  var status: RPCStatus {
+    get {return _status ?? RPCStatus()}
+    set {_status = newValue}
+  }
+  /// Returns true if `status` has been explicitly set.
+  var hasStatus: Bool {return self._status != nil}
+  /// Clears the value of `status`. Subsequent reads from it will return its default value.
+  mutating func clearStatus() {self._status = nil}
+
+  /// Whether the response should be trailers only.
+  ///
+  /// Ignored unless it's set on the first message on the stream. When set
+  /// the RPC will be completed with a trailers-only response using the
+  /// status code and message from 'status'. The request metadata will be
+  /// included if 'echo_metadata_in_trailers' is set.
+  ///
+  /// If this is set then 'number_of_messages', 'message_params', and
+  /// 'echo_metadata_in_headers' are ignored.
+  var isTrailersOnly: Bool = false
+
+  /// Whether metadata should be echo'd back in the trailing metadata.
+  ///
+  /// Ignored unless 'status' is set.
+  ///
+  /// Each header field name in the request headers will be prefixed with
+  /// "echo-". For example the header field name "foo" will be returned
+  /// as "echo-foo. Note that semicolons aren't valid in HTTP header field
+  /// names (apart from pseudo headers). As such all semicolons should be
+  /// removed (":path" should become "echo-path").
+  var echoMetadataInTrailers: Bool = false
+
+  var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  init() {}
+
+  fileprivate var _messageParams: PayloadParameters? = nil
+  fileprivate var _status: RPCStatus? = nil
+}
+
+struct RPCStatus {
+  // 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.
+
+  /// Status code indicating the outcome of the RPC.
+  var code: StatusCode = .ok
+
+  /// The message to include with the 'code' at the end of the RPC.
+  var message: String = String()
+
+  var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  init() {}
+}
+
+struct PayloadParameters {
+  // 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 number of bytes to put into the output payload.
+  var size: Int32 = 0
+
+  /// The conent to use in the payload. The value is truncated to an octet.
+  var content: UInt32 = 0
+
+  var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  init() {}
+}
+
+struct ControlOutput {
+  // 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.
+
+  var payload: Data = Data()
+
+  var unknownFields = SwiftProtobuf.UnknownStorage()
+
+  init() {}
+}
+
+#if swift(>=5.5) && canImport(_Concurrency)
+extension StatusCode: @unchecked Sendable {}
+extension ControlInput: @unchecked Sendable {}
+extension RPCStatus: @unchecked Sendable {}
+extension PayloadParameters: @unchecked Sendable {}
+extension ControlOutput: @unchecked Sendable {}
+#endif  // swift(>=5.5) && canImport(_Concurrency)
+
+// MARK: - Code below here is support for the SwiftProtobuf runtime.
+
+extension StatusCode: SwiftProtobuf._ProtoNameProviding {
+  static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    0: .same(proto: "OK"),
+    1: .same(proto: "CANCELLED"),
+    2: .same(proto: "UNKNOWN"),
+    3: .same(proto: "INVALID_ARGUMENT"),
+    4: .same(proto: "DEADLINE_EXCEEDED"),
+    5: .same(proto: "NOT_FOUND"),
+    6: .same(proto: "ALREADY_EXISTS"),
+    7: .same(proto: "PERMISSION_DENIED"),
+    8: .same(proto: "RESOURCE_EXHAUSTED"),
+    9: .same(proto: "FAILED_PRECONDITION"),
+    10: .same(proto: "ABORTED"),
+    11: .same(proto: "OUT_OF_RANGE"),
+    12: .same(proto: "UNIMPLEMENTED"),
+    13: .same(proto: "INTERNAL"),
+    14: .same(proto: "UNAVAILABLE"),
+    15: .same(proto: "DATA_LOSS"),
+    16: .same(proto: "UNAUTHENTICATED"),
+  ]
+}
+
+extension ControlInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = "ControlInput"
+  static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .standard(proto: "echo_metadata_in_headers"),
+    2: .standard(proto: "message_params"),
+    3: .standard(proto: "number_of_messages"),
+    5: .same(proto: "status"),
+    6: .standard(proto: "is_trailers_only"),
+    4: .standard(proto: "echo_metadata_in_trailers"),
+  ]
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularBoolField(value: &self.echoMetadataInHeaders) }()
+      case 2: try { try decoder.decodeSingularMessageField(value: &self._messageParams) }()
+      case 3: try { try decoder.decodeSingularInt32Field(value: &self.numberOfMessages) }()
+      case 4: try { try decoder.decodeSingularBoolField(value: &self.echoMetadataInTrailers) }()
+      case 5: try { try decoder.decodeSingularMessageField(value: &self._status) }()
+      case 6: try { try decoder.decodeSingularBoolField(value: &self.isTrailersOnly) }()
+      default: break
+      }
+    }
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    // The use of inline closures is to circumvent an issue where the compiler
+    // allocates stack space for every if/case branch local when no optimizations
+    // are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
+    // https://github.com/apple/swift-protobuf/issues/1182
+    if self.echoMetadataInHeaders != false {
+      try visitor.visitSingularBoolField(value: self.echoMetadataInHeaders, fieldNumber: 1)
+    }
+    try { if let v = self._messageParams {
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
+    } }()
+    if self.numberOfMessages != 0 {
+      try visitor.visitSingularInt32Field(value: self.numberOfMessages, fieldNumber: 3)
+    }
+    if self.echoMetadataInTrailers != false {
+      try visitor.visitSingularBoolField(value: self.echoMetadataInTrailers, fieldNumber: 4)
+    }
+    try { if let v = self._status {
+      try visitor.visitSingularMessageField(value: v, fieldNumber: 5)
+    } }()
+    if self.isTrailersOnly != false {
+      try visitor.visitSingularBoolField(value: self.isTrailersOnly, fieldNumber: 6)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  static func ==(lhs: ControlInput, rhs: ControlInput) -> Bool {
+    if lhs.echoMetadataInHeaders != rhs.echoMetadataInHeaders {return false}
+    if lhs._messageParams != rhs._messageParams {return false}
+    if lhs.numberOfMessages != rhs.numberOfMessages {return false}
+    if lhs._status != rhs._status {return false}
+    if lhs.isTrailersOnly != rhs.isTrailersOnly {return false}
+    if lhs.echoMetadataInTrailers != rhs.echoMetadataInTrailers {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
+extension RPCStatus: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = "RPCStatus"
+  static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "code"),
+    2: .same(proto: "message"),
+  ]
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularEnumField(value: &self.code) }()
+      case 2: try { try decoder.decodeSingularStringField(value: &self.message) }()
+      default: break
+      }
+    }
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if self.code != .ok {
+      try visitor.visitSingularEnumField(value: self.code, fieldNumber: 1)
+    }
+    if !self.message.isEmpty {
+      try visitor.visitSingularStringField(value: self.message, fieldNumber: 2)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  static func ==(lhs: RPCStatus, rhs: RPCStatus) -> Bool {
+    if lhs.code != rhs.code {return false}
+    if lhs.message != rhs.message {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
+extension PayloadParameters: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = "PayloadParameters"
+  static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "size"),
+    2: .same(proto: "content"),
+  ]
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularInt32Field(value: &self.size) }()
+      case 2: try { try decoder.decodeSingularUInt32Field(value: &self.content) }()
+      default: break
+      }
+    }
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if self.size != 0 {
+      try visitor.visitSingularInt32Field(value: self.size, fieldNumber: 1)
+    }
+    if self.content != 0 {
+      try visitor.visitSingularUInt32Field(value: self.content, fieldNumber: 2)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  static func ==(lhs: PayloadParameters, rhs: PayloadParameters) -> Bool {
+    if lhs.size != rhs.size {return false}
+    if lhs.content != rhs.content {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}
+
+extension ControlOutput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
+  static let protoMessageName: String = "ControlOutput"
+  static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
+    1: .same(proto: "payload"),
+  ]
+
+  mutating func decodeMessage<D: SwiftProtobuf.Decoder>(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.decodeSingularBytesField(value: &self.payload) }()
+      default: break
+      }
+    }
+  }
+
+  func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
+    if !self.payload.isEmpty {
+      try visitor.visitSingularBytesField(value: self.payload, fieldNumber: 1)
+    }
+    try unknownFields.traverse(visitor: &visitor)
+  }
+
+  static func ==(lhs: ControlOutput, rhs: ControlOutput) -> Bool {
+    if lhs.payload != rhs.payload {return false}
+    if lhs.unknownFields != rhs.unknownFields {return false}
+    return true
+  }
+}