diff --git a/Sources/GRPCCore/Call/Client/ClientContext.swift b/Sources/GRPCCore/Call/Client/ClientContext.swift index 613cf0c36..679496ed4 100644 --- a/Sources/GRPCCore/Call/Client/ClientContext.swift +++ b/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, diff --git a/Sources/GRPCCore/Call/Client/ClientInterceptor.swift b/Sources/GRPCCore/Call/Client/ClientInterceptor.swift index 68a1fcf45..890a49f0c 100644 --- a/Sources/GRPCCore/Call/Client/ClientInterceptor.swift +++ b/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 diff --git a/Sources/GRPCCore/Call/Server/ServerContext.swift b/Sources/GRPCCore/Call/Server/ServerContext.swift index 504e9563a..812a22886 100644 --- a/Sources/GRPCCore/Call/Server/ServerContext.swift +++ b/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 ":
" where + /// "" 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 ":
" where + /// "" 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 } } diff --git a/Sources/GRPCInProcessTransport/InProcessTransport+Server.swift b/Sources/GRPCInProcessTransport/InProcessTransport+Server.swift index 659c53465..d8b385ce3 100644 --- a/Sources/GRPCInProcessTransport/InProcessTransport+Server.swift +++ b/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) diff --git a/Tests/GRPCCoreTests/Call/Server/Internal/ServerRPCExecutorTestSupport/ServerRPCExecutorTestHarness.swift b/Tests/GRPCCoreTests/Call/Server/Internal/ServerRPCExecutorTestSupport/ServerRPCExecutorTestHarness.swift index 6634d9e9f..d9beb0d6a 100644 --- a/Tests/GRPCCoreTests/Call/Server/Internal/ServerRPCExecutorTestSupport/ServerRPCExecutorTestHarness.swift +++ b/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 ) diff --git a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift b/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift index 786f6fe99..04b8265a0 100644 --- a/Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift +++ b/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, context: ServerContext - ) async throws -> ServerResponse { - return ServerResponse(message: context.peer) + ) async throws -> ServerResponse { + 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(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)