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
2 changes: 1 addition & 1 deletion Examples/v2/echo/Subcommands/Collect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct Collect: AsyncParsableCommand {
let client = GRPCClient(
transport: try .http2NIOPosix(
target: self.arguments.target,
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/echo/Subcommands/Expand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct Expand: AsyncParsableCommand {
let client = GRPCClient(
transport: try .http2NIOPosix(
target: self.arguments.target,
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/echo/Subcommands/Get.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ struct Get: AsyncParsableCommand {
let client = GRPCClient(
transport: try .http2NIOPosix(
target: self.arguments.target,
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/echo/Subcommands/Update.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct Update: AsyncParsableCommand {
let client = GRPCClient(
transport: try .http2NIOPosix(
target: self.arguments.target,
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/hello-world/Subcommands/Greet.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ struct Greet: AsyncParsableCommand {
let client = GRPCClient(
transport: try .http2NIOPosix(
target: .ipv4(host: "127.0.0.1", port: self.port),
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/route-guide/Subcommands/GetFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct GetFeature: AsyncParsableCommand {
func run() async throws {
let transport = try HTTP2ClientTransport.Posix(
target: .ipv4(host: "127.0.0.1", port: self.port),
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
let client = GRPCClient(transport: transport)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/route-guide/Subcommands/ListFeatures.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ struct ListFeatures: AsyncParsableCommand {
func run() async throws {
let transport = try HTTP2ClientTransport.Posix(
target: .ipv4(host: "127.0.0.1", port: self.port),
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
let client = GRPCClient(transport: transport)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/route-guide/Subcommands/RecordRoute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct RecordRoute: AsyncParsableCommand {
func run() async throws {
let transport = try HTTP2ClientTransport.Posix(
target: .ipv4(host: "127.0.0.1", port: self.port),
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
let client = GRPCClient(transport: transport)

Expand Down
2 changes: 1 addition & 1 deletion Examples/v2/route-guide/Subcommands/RouteChat.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ struct RouteChat: AsyncParsableCommand {
func run() async throws {
let transport = try HTTP2ClientTransport.Posix(
target: .ipv4(host: "127.0.0.1", port: self.port),
config: .defaults()
config: .defaults(transportSecurity: .plaintext)
)
let client = GRPCClient(transport: transport)

Expand Down
85 changes: 72 additions & 13 deletions Sources/GRPCHTTP2TransportNIOPosix/HTTP2ClientTransport+Posix.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,38 @@ public import GRPCHTTP2Core // should be @usableFromInline
public import NIOCore
public import NIOPosix // has to be public because of default argument value in init

#if canImport(NIOSSL)
import NIOSSL
#endif

@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
extension HTTP2ClientTransport {
/// A `ClientTransport` using HTTP/2 built on top of `NIOPosix`.
/// A ``GRPCCore/ClientTransport`` using HTTP/2 built on top of `NIOPosix`.
///
/// This transport builds on top of SwiftNIO's Posix networking layer and is suitable for use
/// on Linux and Darwin based platform (macOS, iOS, etc.) However, it's *strongly* recommended
/// that if you are targeting Darwin platforms then you should use the `NIOTS` variant of
/// the `HTTP2ClientTransport`.
/// the ``GRPCHTTP2Core/HTTP2ClientTransport``.
///
/// To use this transport you need to provide a 'target' to connect to which will be resolved
/// by an appropriate resolver from the resolver registry. By default the resolver registry can
/// resolve DNS targets, IPv4 and IPv6 targets, Unix domain socket targets, and Virtual Socket
/// targets. If you use a custom target you must also provide an appropriately configured
/// registry.
///
/// You can control various aspects of connection creation, management and RPC behavior via the
/// ``Config``. Load balancing policies and other RPC specific behavior can be configured via
/// You can control various aspects of connection creation, management, security and RPC behavior via
/// the ``Config``. Load balancing policies and other RPC specific behavior can be configured via
/// the ``ServiceConfig`` (if it isn't provided by a resolver).
///
/// Beyond creating the transport you don't need to interact with it directly, instead, pass it
/// to a `GRPCClient`:
///
/// ```swift
/// try await withThrowingDiscardingTaskGroup {
/// let transport = try HTTP2ClientTransport.Posix(target: .dns(host: "example.com"))
/// try await withThrowingDiscardingTaskGroup { group in
/// let transport = try HTTP2ClientTransport.Posix(
/// target: .ipv4(host: "example.com"),
/// config: .defaults(transportSecurity: .plaintext)
/// )
/// let client = GRPCClient(transport: transport)
/// group.addTask {
/// try await client.run()
Expand Down Expand Up @@ -87,7 +94,7 @@ extension HTTP2ClientTransport {
// Configure a connector.
self.channel = GRPCChannel(
resolver: resolver,
connector: Connector(eventLoopGroup: eventLoopGroup, config: config),
connector: try Connector(eventLoopGroup: eventLoopGroup, config: config),
config: GRPCChannel.Config(posix: config),
defaultServiceConfig: serviceConfig
)
Expand Down Expand Up @@ -125,9 +132,33 @@ extension HTTP2ClientTransport.Posix {
private let config: HTTP2ClientTransport.Posix.Config
private let eventLoopGroup: any EventLoopGroup

init(eventLoopGroup: any EventLoopGroup, config: HTTP2ClientTransport.Posix.Config) {
#if canImport(NIOSSL)
private let nioSSLContext: NIOSSLContext?
private let serverHostname: String?
#endif

init(eventLoopGroup: any EventLoopGroup, config: HTTP2ClientTransport.Posix.Config) throws {
self.eventLoopGroup = eventLoopGroup
self.config = config

#if canImport(NIOSSL)
switch self.config.transportSecurity.wrapped {
case .plaintext:
self.nioSSLContext = nil
self.serverHostname = nil
case .tls(let tlsConfig):
do {
self.nioSSLContext = try NIOSSLContext(configuration: TLSConfiguration(tlsConfig))
self.serverHostname = tlsConfig.serverHostname
} catch {
throw RuntimeError(
code: .transportError,
message: "Couldn't create SSL context, check your TLS configuration.",
cause: error
)
}
}
#endif
}

func establishConnection(
Expand All @@ -137,7 +168,18 @@ extension HTTP2ClientTransport.Posix {
group: self.eventLoopGroup
).connect(to: address) { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.configureGRPCClientPipeline(
#if canImport(NIOSSL)
if let nioSSLContext = self.nioSSLContext {
try channel.pipeline.syncOperations.addHandler(
NIOSSLClientHandler(
context: nioSSLContext,
serverHostname: self.serverHostname
)
)
}
#endif

return try channel.pipeline.syncOperations.configureGRPCClientPipeline(
channel: channel,
config: GRPCChannel.Config(posix: self.config)
)
Expand All @@ -164,31 +206,48 @@ extension HTTP2ClientTransport.Posix {
/// Compression configuration.
public var compression: HTTP2ClientTransport.Config.Compression

/// The transport's security.
public var transportSecurity: TransportSecurity

/// Creates a new connection configuration.
///
/// See also ``defaults``.
/// - Parameters:
/// - http2: HTTP2 configuration.
/// - backoff: Backoff configuration.
/// - connection: Connection configuration.
/// - compression: Compression configuration.
/// - transportSecurity: The transport's security configuration.
///
/// - SeeAlso: ``defaults(_:)``.
public init(
http2: HTTP2ClientTransport.Config.HTTP2,
backoff: HTTP2ClientTransport.Config.Backoff,
connection: HTTP2ClientTransport.Config.Connection,
compression: HTTP2ClientTransport.Config.Compression
compression: HTTP2ClientTransport.Config.Compression,
transportSecurity: TransportSecurity
) {
self.http2 = http2
self.connection = connection
self.backoff = backoff
self.compression = compression
self.transportSecurity = transportSecurity
}

/// Default values.
///
/// - Parameters:
/// - transportSecurity: The security settings applied to the transport.
/// - configure: A closure which allows you to modify the defaults before returning them.
public static func defaults(_ configure: (_ config: inout Self) -> Void = { _ in }) -> Self {
public static func defaults(
transportSecurity: TransportSecurity,
configure: (_ config: inout Self) -> Void = { _ in }
) -> Self {
var config = Self(
http2: .defaults,
backoff: .defaults,
connection: .defaults,
compression: .defaults
compression: .defaults,
transportSecurity: transportSecurity
)
configure(&config)
return config
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

public import GRPCCore
public import GRPCHTTP2Core
public import GRPCHTTP2Core // should be @usableFromInline
internal import NIOCore
internal import NIOExtras
internal import NIOHTTP2
Expand All @@ -27,7 +27,33 @@ import NIOSSL
#endif

extension HTTP2ServerTransport {
/// A NIOPosix-backed implementation of a server transport.
/// A ``GRPCCore/ServerTransport`` using HTTP/2 built on top of `NIOPosix`.
///
/// This transport builds on top of SwiftNIO's Posix networking layer and is suitable for use
/// on Linux and Darwin based platform (macOS, iOS, etc.) However, it's *strongly* recommended
/// that if you are targeting Darwin platforms then you should use the `NIOTS` variant of
/// the ``GRPCHTTP2Core/HTTP2ServerTransport``.
///
/// You can control various aspects of connection creation, management, security and RPC behavior via
/// the ``Config``.
///
/// Beyond creating the transport you don't need to interact with it directly, instead, pass it
/// to a `GRPCServer`:
///
/// ```swift
/// try await withThrowingDiscardingTaskGroup { group in
/// let transport = HTTP2ServerTransport.Posix(
/// address: .ipv4(host: "127.0.0.1", port: 0),
/// config: .defaults(transportSecurity: .plaintext)
/// )
/// let server = GRPCServer(transport: transport, services: someServices)
/// group.addTask {
/// try await server.serve()
/// }
///
/// // ...
/// }
/// ```
@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
public final class Posix: ServerTransport, ListeningServerTransport {
private let address: GRPCHTTP2Core.SocketAddress
Expand Down Expand Up @@ -340,27 +366,34 @@ extension HTTP2ServerTransport.Posix {
public struct Config: Sendable {
/// Compression configuration.
public var compression: HTTP2ServerTransport.Config.Compression

/// Connection configuration.
public var connection: HTTP2ServerTransport.Config.Connection

/// HTTP2 configuration.
public var http2: HTTP2ServerTransport.Config.HTTP2

/// RPC configuration.
public var rpc: HTTP2ServerTransport.Config.RPC

/// The transport's security.
public var transportSecurity: TransportSecurity

/// Construct a new `Config`.
///
/// - Parameters:
/// - compression: Compression configuration.
/// - connection: Connection configuration.
/// - http2: HTTP2 configuration.
/// - rpc: RPC configuration.
/// - connection: Connection configuration.
/// - compression: Compression configuration.
/// - transportSecurity: The transport's security configuration.
///
/// - SeeAlso: ``defaults(transportSecurity:configure:)``
public init(
compression: HTTP2ServerTransport.Config.Compression,
connection: HTTP2ServerTransport.Config.Connection,
http2: HTTP2ServerTransport.Config.HTTP2,
rpc: HTTP2ServerTransport.Config.RPC,
connection: HTTP2ServerTransport.Config.Connection,
compression: HTTP2ServerTransport.Config.Compression,
transportSecurity: TransportSecurity
) {
self.compression = compression
Expand All @@ -380,10 +413,10 @@ extension HTTP2ServerTransport.Posix {
configure: (_ config: inout Self) -> Void = { _ in }
) -> Self {
var config = Self(
compression: .defaults,
connection: .defaults,
http2: .defaults,
rpc: .defaults,
connection: .defaults,
compression: .defaults,
transportSecurity: transportSecurity
)
configure(&config)
Expand Down
28 changes: 22 additions & 6 deletions Sources/GRPCHTTP2TransportNIOPosix/NIOSSL+GRPC.swift
Original file line number Diff line number Diff line change
Expand Up @@ -131,18 +131,34 @@ extension TLSConfiguration {
let certificateChain = try tlsConfig.certificateChain.sslCertificateSources()
let privateKey = try NIOSSLPrivateKey(privateKey: tlsConfig.privateKey)

var tlsConfiguration = TLSConfiguration.makeServerConfiguration(
self = TLSConfiguration.makeServerConfiguration(
certificateChain: certificateChain,
privateKey: .privateKey(privateKey)
)
tlsConfiguration.minimumTLSVersion = .tlsv12
tlsConfiguration.certificateVerification = CertificateVerification(
self.minimumTLSVersion = .tlsv12
self.certificateVerification = CertificateVerification(
tlsConfig.clientCertificateVerification
)
tlsConfiguration.trustRoots = try NIOSSLTrustRoots(tlsConfig.trustRoots)
tlsConfiguration.applicationProtocols = ["grpc-exp", "h2"]
self.trustRoots = try NIOSSLTrustRoots(tlsConfig.trustRoots)
self.applicationProtocols = ["grpc-exp", "h2"]
}

@available(macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0, *)
package init(_ tlsConfig: HTTP2ClientTransport.Posix.Config.TLS) throws {
self = TLSConfiguration.makeClientConfiguration()
self.certificateChain = try tlsConfig.certificateChain.sslCertificateSources()

self = tlsConfiguration
if let privateKey = tlsConfig.privateKey {
let privateKeySource = try NIOSSLPrivateKey(privateKey: privateKey)
self.privateKey = .privateKey(privateKeySource)
}

self.minimumTLSVersion = .tlsv12
self.certificateVerification = CertificateVerification(
tlsConfig.serverCertificateVerification
)
self.trustRoots = try NIOSSLTrustRoots(tlsConfig.trustRoots)
self.applicationProtocols = ["grpc-exp", "h2"]
}
}
#endif
Loading
Loading