Skip to content

Commit b8a3c3b

Browse files
authored
Adopt ClientContext changes (#56)
This PR adopts the changes introduced in grpc/grpc-swift#2158, requiring client transports to provide a `ClientContext` alongside the stream.
1 parent 92ad3c0 commit b8a3c3b

File tree

12 files changed

+218
-67
lines changed

12 files changed

+218
-67
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ let products: [Product] = [
3535
let dependencies: [Package.Dependency] = [
3636
.package(
3737
url: "https://github.com/grpc/grpc-swift.git",
38-
exact: "2.0.0-beta.2"
38+
branch: "main"
3939
),
4040
.package(
4141
url: "https://github.com/apple/swift-nio.git",

Sources/GRPCNIOTransportCore/Client/Connection/Connection.swift

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,10 @@ package final class Connection: Sendable {
202202
descriptor: MethodDescriptor,
203203
options: CallOptions
204204
) async throws -> Stream {
205-
let (multiplexer, scheme) = try self.state.withLock { state in
205+
let (multiplexer, scheme, remotePeer, localPeer) = try self.state.withLock { state in
206206
switch state {
207207
case .connected(let connected):
208-
return (connected.multiplexer, connected.scheme)
208+
return (connected.multiplexer, connected.scheme, connected.remotePeer, connected.localPeer)
209209
case .notConnected, .closing, .closed:
210210
throw RPCError(code: .unavailable, message: "subchannel isn't ready")
211211
}
@@ -246,7 +246,13 @@ package final class Connection: Sendable {
246246
}
247247
}
248248

249-
return Stream(wrapping: stream, descriptor: descriptor)
249+
let context = ClientContext(
250+
descriptor: descriptor,
251+
remotePeer: remotePeer,
252+
localPeer: localPeer
253+
)
254+
255+
return Stream(wrapping: stream, context: context)
250256
} catch {
251257
throw RPCError(code: .unavailable, message: "subchannel is unavailable", cause: error)
252258
}
@@ -417,16 +423,16 @@ extension Connection {
417423
}
418424
}
419425

420-
let descriptor: MethodDescriptor
426+
let context: ClientContext
421427

422428
private let http2Stream: NIOAsyncChannel<RPCResponsePart, RPCRequestPart>
423429

424430
init(
425431
wrapping stream: NIOAsyncChannel<RPCResponsePart, RPCRequestPart>,
426-
descriptor: MethodDescriptor
432+
context: ClientContext
427433
) {
428434
self.http2Stream = stream
429-
self.descriptor = descriptor
435+
self.context = context
430436
}
431437

432438
package func execute<T>(
@@ -457,13 +463,19 @@ extension Connection {
457463
struct Connected: Sendable {
458464
/// The connection channel.
459465
var channel: NIOAsyncChannel<ClientConnectionEvent, Void>
466+
/// The connection's remote peer information.
467+
var remotePeer: String
468+
/// The connection's local peer information.
469+
var localPeer: String
460470
/// Multiplexer for creating HTTP/2 streams.
461471
var multiplexer: NIOHTTP2Handler.AsyncStreamMultiplexer<Void>
462472
/// Whether the connection is plaintext, `false` implies TLS is being used.
463473
var scheme: Scheme
464474

465475
init(_ connection: HTTP2Connection) {
466476
self.channel = connection.channel
477+
self.remotePeer = connection.channel.remoteAddressInfo
478+
self.localPeer = connection.channel.localAddressInfo
467479
self.multiplexer = connection.multiplexer
468480
self.scheme = connection.isPlaintext ? .http : .https
469481
}

Sources/GRPCNIOTransportCore/Client/Connection/GRPCChannel.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ package final class GRPCChannel: ClientTransport {
198198
self.input.continuation.yield(.close)
199199
}
200200

201-
/// Opens a stream using the transport, and uses it as input into a user-provided closure.
201+
/// Opens a stream using the transport, and uses it as input into a user-provided closure, alongside the client's context.
202202
package func withStream<T: Sendable>(
203203
descriptor: MethodDescriptor,
204204
options: CallOptions,
205-
_ closure: (_ stream: RPCStream<Inbound, Outbound>) async throws -> T
205+
_ closure: (_ stream: RPCStream<Inbound, Outbound>, _ context: ClientContext) async throws -> T
206206
) async throws -> T {
207207
// Merge options from the call with those from the service config.
208208
let methodConfig = self.config(forMethod: descriptor)
@@ -214,11 +214,11 @@ package final class GRPCChannel: ClientTransport {
214214
case .created(let stream):
215215
return try await stream.execute { inbound, outbound in
216216
let rpcStream = RPCStream(
217-
descriptor: stream.descriptor,
217+
descriptor: stream.context.descriptor,
218218
inbound: RPCAsyncSequence<RPCResponsePart, any Error>(wrapping: inbound),
219219
outbound: RPCWriter.Closable(wrapping: outbound)
220220
)
221-
return try await closure(rpcStream)
221+
return try await closure(rpcStream, stream.context)
222222
}
223223

224224
case .tryAgain(let error):
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/*
2+
* Copyright 2025, gRPC Authors All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
internal import NIOCore
18+
19+
extension NIOAsyncChannel {
20+
var remoteAddressInfo: String {
21+
guard let remote = self.channel.remoteAddress else {
22+
return "<unknown>"
23+
}
24+
25+
switch remote {
26+
case .v4(let address):
27+
// '!' is safe, v4 always has a port.
28+
return "ipv4:\(address.host):\(remote.port!)"
29+
30+
case .v6(let address):
31+
// '!' is safe, v6 always has a port.
32+
return "ipv6:[\(address.host)]:\(remote.port!)"
33+
34+
case .unixDomainSocket:
35+
// '!' is safe, UDS always has a path.
36+
if remote.pathname!.isEmpty {
37+
guard let local = self.channel.localAddress else {
38+
return "unix:<unknown>"
39+
}
40+
41+
switch local {
42+
case .unixDomainSocket:
43+
// '!' is safe, UDS always has a path.
44+
return "unix:\(local.pathname!)"
45+
46+
case .v4, .v6:
47+
// Remote address is UDS but local isn't. This shouldn't ever happen.
48+
return "unix:<unknown>"
49+
}
50+
} else {
51+
// '!' is safe, UDS always has a path.
52+
return "unix:\(remote.pathname!)"
53+
}
54+
}
55+
}
56+
57+
var localAddressInfo: String {
58+
guard let local = self.channel.localAddress else {
59+
return "<unknown>"
60+
}
61+
62+
switch local {
63+
case .v4(let address):
64+
// '!' is safe, v4 always has a port.
65+
return "ipv4:\(address.host):\(local.port!)"
66+
67+
case .v6(let address):
68+
// '!' is safe, v6 always has a port.
69+
return "ipv6:[\(address.host)]:\(local.port!)"
70+
71+
case .unixDomainSocket:
72+
// '!' is safe, UDS always has a path.
73+
if local.pathname!.isEmpty {
74+
guard let remote = self.channel.remoteAddress else {
75+
return "unix:<unknown>"
76+
}
77+
78+
switch remote {
79+
case .unixDomainSocket:
80+
// '!' is safe, UDS always has a path.
81+
return "unix:\(remote.pathname!)"
82+
83+
case .v4, .v6:
84+
// Remote address is UDS but local isn't. This shouldn't ever happen.
85+
return "unix:<unknown>"
86+
}
87+
} else {
88+
// '!' is safe, UDS always has a path.
89+
return "unix:\(local.pathname!)"
90+
}
91+
}
92+
}
93+
}

Sources/GRPCNIOTransportCore/Server/CommonHTTP2ServerTransport.swift

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -191,40 +191,6 @@ package final class CommonHTTP2ServerTransport<
191191
}
192192
}
193193

194-
private func peerInfo(channel: any Channel) -> String {
195-
guard let remote = channel.remoteAddress else {
196-
return "<unknown>"
197-
}
198-
199-
switch remote {
200-
case .v4(let address):
201-
// '!' is safe, v4 always has a port.
202-
return "ipv4:\(address.host):\(remote.port!)"
203-
204-
case .v6(let address):
205-
// '!' is safe, v6 always has a port.
206-
return "ipv6:[\(address.host)]:\(remote.port!)"
207-
208-
case .unixDomainSocket:
209-
// The pathname will be on the local address.
210-
guard let local = channel.localAddress else {
211-
// UDS but no local address; this shouldn't ever happen but at least note the transport
212-
// as being UDS.
213-
return "unix:<unknown>"
214-
}
215-
216-
switch local {
217-
case .unixDomainSocket:
218-
// '!' is safe, UDS always has a path.
219-
return "unix:\(local.pathname!)"
220-
221-
case .v4, .v6:
222-
// Remote address is UDS but local isn't. This shouldn't ever happen.
223-
return "unix:<unknown>"
224-
}
225-
}
226-
}
227-
228194
private func handleConnection(
229195
_ connection: NIOAsyncChannel<HTTP2Frame, HTTP2Frame>,
230196
multiplexer: ChannelPipeline.SynchronousOperations.HTTP2StreamMultiplexer,
@@ -233,7 +199,7 @@ package final class CommonHTTP2ServerTransport<
233199
_ context: ServerContext
234200
) async -> Void
235201
) async throws {
236-
let peer = self.peerInfo(channel: connection.channel)
202+
let peer = connection.remoteAddressInfo
237203
try await connection.executeThenClose { inbound, _ in
238204
await withDiscardingTaskGroup { group in
239205
group.addTask {

Sources/GRPCNIOTransportHTTP2Posix/HTTP2ClientTransport+Posix.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ extension HTTP2ClientTransport {
120120
public func withStream<T: Sendable>(
121121
descriptor: MethodDescriptor,
122122
options: CallOptions,
123-
_ closure: (RPCStream<Inbound, Outbound>) async throws -> T
123+
_ closure: (RPCStream<Inbound, Outbound>, ClientContext) async throws -> T
124124
) async throws -> T {
125125
try await self.channel.withStream(descriptor: descriptor, options: options, closure)
126126
}

Sources/GRPCNIOTransportHTTP2TransportServices/HTTP2ClientTransport+TransportServices.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ extension HTTP2ClientTransport {
118118
public func withStream<T: Sendable>(
119119
descriptor: MethodDescriptor,
120120
options: CallOptions,
121-
_ closure: (RPCStream<Inbound, Outbound>) async throws -> T
121+
_ closure: (RPCStream<Inbound, Outbound>, ClientContext) async throws -> T
122122
) async throws -> T {
123123
try await self.channel.withStream(descriptor: descriptor, options: options, closure)
124124
}

Tests/GRPCNIOTransportCoreTests/Client/Connection/GRPCChannelTests.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ final class GRPCChannelTests: XCTestCase {
353353
await channel.connect()
354354
}
355355

356-
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream in
356+
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream, _ in
357357
try await stream.outbound.write(.metadata([:]))
358358

359359
var iterator = stream.inbound.makeAsyncIterator()
@@ -441,7 +441,7 @@ final class GRPCChannelTests: XCTestCase {
441441
// be queued though.
442442
for _ in 1 ... 100 {
443443
group.addTask {
444-
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream in
444+
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream, _ in
445445
try await stream.outbound.write(.metadata([:]))
446446
await stream.outbound.finish()
447447

@@ -510,7 +510,7 @@ final class GRPCChannelTests: XCTestCase {
510510
options.waitForReady = false
511511

512512
await XCTAssertThrowsErrorAsync(ofType: RPCError.self) {
513-
try await channel.withStream(descriptor: .echoGet, options: options) { _ in
513+
try await channel.withStream(descriptor: .echoGet, options: options) { _, _ in
514514
XCTFail("Unexpected stream")
515515
}
516516
} errorHandler: { error in
@@ -780,7 +780,7 @@ final class GRPCChannelTests: XCTestCase {
780780

781781
// Try to open a new stream.
782782
await XCTAssertThrowsErrorAsync(ofType: RPCError.self) {
783-
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream in
783+
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream, _ in
784784
XCTFail("Unexpected new stream")
785785
}
786786
} errorHandler: { error in
@@ -823,7 +823,7 @@ final class GRPCChannelTests: XCTestCase {
823823
}
824824

825825
func doAnRPC() async throws {
826-
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream in
826+
try await channel.withStream(descriptor: .echoGet, options: .defaults) { stream, _ in
827827
try await stream.outbound.write(.metadata([:]))
828828
await stream.outbound.finish()
829829

@@ -873,7 +873,7 @@ extension GRPCChannel {
873873
let values: Metadata.StringValues? = try await self.withStream(
874874
descriptor: .echoGet,
875875
options: .defaults
876-
) { stream in
876+
) { stream, _ in
877877
try await stream.outbound.write(.metadata([:]))
878878
await stream.outbound.finish()
879879

Tests/GRPCNIOTransportHTTP2Tests/ControlClient.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ internal struct ControlClient {
109109
internal func peerInfo<R>(
110110
options: GRPCCore.CallOptions = .defaults,
111111
_ body: @Sendable @escaping (
112-
_ response: GRPCCore.ClientResponse<String>
112+
_ response: GRPCCore.ClientResponse<ControlService.PeerInfoResponse>
113113
) async throws -> R = { try $0.message }
114114
) async throws -> R where R: Sendable {
115115
try await self.client.unary(

0 commit comments

Comments
 (0)