Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Sources/GRPCCore/Call/Client/ClientContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 3 additions & 3 deletions Sources/GRPCCore/Call/Client/ClientInterceptor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@
* 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
/// before they are handed to a transport and responses are intercepted after they have been
/// 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
Expand Down
41 changes: 37 additions & 4 deletions Sources/GRPCCore/Call/Server/ServerContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
33 changes: 26 additions & 7 deletions Tests/GRPCInProcessTransportTests/InProcessTransportTests.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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)
}
}
}
Expand Down Expand Up @@ -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) {
Expand All @@ -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),
Expand All @@ -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)
Expand Down
Loading