Browse Source

Add remote and local peers to `ServerContext` (#2161)

This PR adds a `localPeer` property to the `ServerContext`, and renames
`peer` to `remotePeer`.

This is related to https://github.com/grpc/grpc-swift/pull/2158 on the
client side and will be used to improve the server tracing interceptor.
Gus Cairo 1 year ago
parent
commit
421450417a

+ 5 - 0
Sources/GRPCCore/Call/Client/ClientContext.swift

@@ -46,6 +46,11 @@ public struct ClientContext: Sendable {
   public var localPeer: String
 
   /// Create a new client interceptor context.
+  ///
+  /// - Parameters:
+  ///   - descriptor: A description of the method being called.
+  ///   - remotePeer: A description of the remote peer.
+  ///   - localPeer: A description of the local peer.
   public init(
     descriptor: MethodDescriptor,
     remotePeer: String,

+ 3 - 3
Sources/GRPCCore/Call/Client/ClientInterceptor.swift

@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+// - FIXME: Update example and documentation to show how to register an interceptor.
+
 /// A type that intercepts requests and response for clients.
 ///
 /// Interceptors allow you to inspect and modify requests and responses. Requests are intercepted
@@ -21,14 +23,12 @@
 /// received from the transport. They are typically used for cross-cutting concerns like injecting
 /// metadata, validating messages, logging additional data, and tracing.
 ///
-/// Interceptors are registered with the server via ``ClientInterceptorPipelineOperation``s.
+/// Interceptors are registered with the client via ``ClientInterceptorPipelineOperation``s.
 /// You may register them for all services registered with a server, for RPCs directed to specific services, or
 /// for RPCs directed to specific methods. If you need to modify the behavior of an interceptor on a
 /// per-RPC basis in more detail, then you can use the ``ClientContext/descriptor`` to determine
 /// which RPC is being called and conditionalise behavior accordingly.
 ///
-/// - TODO: Update example and documentation to show how to register an interceptor.
-///
 /// Some examples of simple interceptors follow.
 ///
 /// ## Metadata injection

+ 37 - 4
Sources/GRPCCore/Call/Server/ServerContext.swift

@@ -30,7 +30,37 @@ public struct ServerContext: Sendable {
   /// - "ipv4:127.0.0.1:31415",
   /// - "ipv6:[::1]:443",
   /// - "in-process:27182".
-  public var peer: String
+  @available(*, deprecated, renamed: "remotePeer")
+  public var peer: String {
+    get { remotePeer }
+    set { remotePeer = newValue }
+  }
+
+  /// A description of the remote peer.
+  ///
+  /// The format of the description should follow the pattern "<transport>:<address>" where
+  /// "<transport>" indicates the underlying network transport (such as "ipv4", "unix", or
+  /// "in-process"). This is a guideline for how descriptions should be formatted; different
+  /// implementations may not follow this format so you shouldn't make assumptions based on it.
+  ///
+  /// Some examples include:
+  /// - "ipv4:127.0.0.1:31415",
+  /// - "ipv6:[::1]:443",
+  /// - "in-process:27182".
+  public var remotePeer: String
+
+  /// A description of the local peer.
+  ///
+  /// The format of the description should follow the pattern "<transport>:<address>" where
+  /// "<transport>" indicates the underlying network transport (such as "ipv4", "unix", or
+  /// "in-process"). This is a guideline for how descriptions should be formatted; different
+  /// implementations may not follow this format so you shouldn't make assumptions based on it.
+  ///
+  /// Some examples include:
+  /// - "ipv4:127.0.0.1:31415",
+  /// - "ipv6:[::1]:443",
+  /// - "in-process:27182".
+  public var localPeer: String
 
   /// A handle for checking the cancellation status of an RPC.
   public var cancellation: RPCCancellationHandle
@@ -39,16 +69,19 @@ public struct ServerContext: Sendable {
   ///
   /// - Parameters:
   ///   - descriptor: A description of the method being called.
-  ///   - peer: A description of the remote peer.
+  ///   - remotePeer: A description of the remote peer.
+  ///   - localPeer: A description of the local peer.
   ///   - cancellation: A cancellation handle. You can create a cancellation handle
   ///     using ``withServerContextRPCCancellationHandle(_:)``.
   public init(
     descriptor: MethodDescriptor,
-    peer: String,
+    remotePeer: String,
+    localPeer: String,
     cancellation: RPCCancellationHandle
   ) {
     self.descriptor = descriptor
-    self.peer = peer
+    self.remotePeer = remotePeer
+    self.localPeer = localPeer
     self.cancellation = cancellation
   }
 }

+ 2 - 1
Sources/GRPCInProcessTransport/InProcessTransport+Server.swift

@@ -122,7 +122,8 @@ extension InProcessTransport {
 
               let context = ServerContext(
                 descriptor: stream.descriptor,
-                peer: self.peer,
+                remotePeer: self.peer,
+                localPeer: self.peer,
                 cancellation: handle
               )
               await streamHandler(stream, context)

+ 2 - 1
Tests/GRPCCoreTests/Call/Server/Internal/ServerRPCExecutorTestSupport/ServerRPCExecutorTestHarness.swift

@@ -102,7 +102,8 @@ struct ServerRPCExecutorTestHarness {
         await withServerContextRPCCancellationHandle { cancellation in
           let context = ServerContext(
             descriptor: MethodDescriptor(fullyQualifiedService: "foo", method: "bar"),
-            peer: "tests",
+            remotePeer: "remote",
+            localPeer: "local",
             cancellation: cancellation
           )
 

+ 26 - 7
Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift

@@ -1,5 +1,5 @@
 /*
- * Copyright 2024, gRPC Authors All rights reserved.
+ * Copyright 2024-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.
@@ -77,14 +77,13 @@ struct InProcessTransportTests {
         request: ClientRequest(message: ()),
         descriptor: .peerInfo,
         serializer: VoidSerializer(),
-        deserializer: UTF8Deserializer(),
+        deserializer: PeerInfoDeserializer(),
         options: .defaults
       ) {
         try $0.message
       }
 
-      let match = peerInfo.wholeMatch(of: /in-process:\d+/)
-      #expect(match != nil)
+      #expect(peerInfo.local == peerInfo.remote)
     }
   }
 }
@@ -122,8 +121,9 @@ private struct TestService: RegistrableRPCService {
   func peerInfo(
     request: ServerRequest<Void>,
     context: ServerContext
-  ) async throws -> ServerResponse<String> {
-    return ServerResponse(message: context.peer)
+  ) async throws -> ServerResponse<PeerInfo> {
+    let peerInfo = PeerInfo(local: context.localPeer, remote: context.remotePeer)
+    return ServerResponse(message: peerInfo)
   }
 
   func registerMethods(with router: inout RPCRouter) {
@@ -139,7 +139,7 @@ private struct TestService: RegistrableRPCService {
     router.registerHandler(
       forMethod: .peerInfo,
       deserializer: VoidDeserializer(),
-      serializer: UTF8Serializer(),
+      serializer: PeerInfoSerializer(),
       handler: {
         let response = try await self.peerInfo(
           request: ServerRequest<Void>(stream: $0),
@@ -163,6 +163,25 @@ extension MethodDescriptor {
   )
 }
 
+private struct PeerInfo: Codable {
+  var local: String
+  var remote: String
+}
+
+private struct PeerInfoSerializer: MessageSerializer {
+  func serialize(_ message: PeerInfo) throws -> [UInt8] {
+    Array("\(message.local) \(message.remote)".utf8)
+  }
+}
+
+private struct PeerInfoDeserializer: MessageDeserializer {
+  func deserialize(_ serializedMessageBytes: [UInt8]) throws -> PeerInfo {
+    let stringPeerInfo = String(decoding: serializedMessageBytes, as: UTF8.self)
+    let peerInfoComponents = stringPeerInfo.split(separator: " ")
+    return PeerInfo(local: String(peerInfoComponents[0]), remote: String(peerInfoComponents[1]))
+  }
+}
+
 private struct UTF8Serializer: MessageSerializer {
   func serialize(_ message: String) throws -> [UInt8] {
     Array(message.utf8)