From 69d07271d75ba12f46301e7cfd657f7bd10a33ca Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Tue, 8 Apr 2025 15:13:32 +0100 Subject: [PATCH 1/7] Add NWParameters configurator to bootstraps --- .../Datagram/NIOTSDatagramBootstrap.swift | 12 ++++++++++- .../Datagram/NIOTSDatagramChannel.swift | 16 ++++++++++---- .../Datagram/NIOTSDatagramListener.swift | 17 +++++++++++++-- .../NIOTSDatagramListenerChannel.swift | 19 ++++++++++++----- .../NIOTSConnectionBootstrap.swift | 21 +++++++++++++++---- .../NIOTSConnectionChannel.swift | 16 ++++++++++---- .../NIOTSListenerBootstrap.swift | 17 +++++++++++---- .../NIOTSListenerChannel.swift | 19 ++++++++++++----- .../StateManagedListenerChannel.swift | 16 +++++++++++--- .../NIOTSChannelMetadataTests.swift | 5 +++-- 10 files changed, 124 insertions(+), 34 deletions(-) diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift index e8ea793..0872cc8 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift @@ -47,6 +47,7 @@ public final class NIOTSDatagramBootstrap { private var qos: DispatchQoS? private var udpOptions: NWProtocolUDP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? + private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? /// Create a `NIOTSDatagramConnectionBootstrap` on the `EventLoopGroup` `group`. /// @@ -133,6 +134,14 @@ public final class NIOTSDatagramBootstrap { return self } + /// Customise the `NWParameters` to be used when creating the connection. + public func configureNWParameters( + _ configurator: @Sendable @escaping (inout NWParameters) -> Void + ) -> Self { + self.nwParametersConfigurator = configurator + return self + } + /// Specify the `host` and `port` to connect to for the UDP `Channel` that will be established. /// /// - parameters: @@ -188,7 +197,8 @@ public final class NIOTSDatagramBootstrap { eventLoop: self.group.next() as! NIOTSEventLoop, qos: self.qos, udpOptions: self.udpOptions, - tlsOptions: self.tlsOptions + tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator ) let initializer = self.channelInitializer ?? { @Sendable _ in conn.eventLoop.makeSucceededFuture(()) } diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift index 9f9e20c..80d2c7e 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift @@ -140,8 +140,12 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { internal var allowLocalEndpointReuse = false internal var multipathServiceType: NWParameters.MultipathServiceType = .disabled + private let nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + var parameters: NWParameters { - NWParameters(dtls: self.tlsOptions, udp: self.udpOptions) + var parameters = NWParameters(dtls: self.tlsOptions, udp: self.udpOptions) + self.nwParametersConfigurator?(¶meters) + return parameters } var _inboundStreamOpen: Bool { @@ -182,7 +186,8 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: Int = 1, maximumReceiveLength: Int = 8192, udpOptions: NWProtocolUDP.Options, - tlsOptions: NWProtocolTLS.Options? + tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -192,6 +197,7 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { self.connectionQueue = eventLoop.channelQueue(label: "nio.nioTransportServices.connectionchannel", qos: qos) self.udpOptions = udpOptions self.tlsOptions = tlsOptions + self.nwParametersConfigurator = nwParametersConfigurator // Must come last, as it requires self to be completely initialized. self._pipeline = ChannelPipeline(channel: self) @@ -206,7 +212,8 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: Int = 1, maximumReceiveLength: Int = 8192, udpOptions: NWProtocolUDP.Options, - tlsOptions: NWProtocolTLS.Options? + tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, @@ -215,7 +222,8 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: minimumIncompleteReceiveLength, maximumReceiveLength: maximumReceiveLength, udpOptions: udpOptions, - tlsOptions: tlsOptions + tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator ) self.connection = connection } diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift index 7ee0a5c..b81ef96 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift @@ -66,6 +66,7 @@ public final class NIOTSDatagramListenerBootstrap { private var udpOptions: NWProtocolUDP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? private var bindTimeout: TimeAmount? + private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? /// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`. /// @@ -236,6 +237,14 @@ public final class NIOTSDatagramListenerBootstrap { return self } + /// Customise the `NWParameters` to be used when creating the connection. + public func configureNWParameters( + _ configurator: @Sendable @escaping (inout NWParameters) -> Void + ) -> Self { + self.nwParametersConfigurator = configurator + return self + } + /// Bind the `NIOTSListenerChannel` to `host` and `port`. /// /// - parameters: @@ -327,10 +336,12 @@ public final class NIOTSDatagramListenerBootstrap { qos: self.serverQoS, udpOptions: self.udpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childUDPOptions: self.udpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } else { serverChannel = NIOTSDatagramListenerChannel( @@ -338,10 +349,12 @@ public final class NIOTSDatagramListenerBootstrap { qos: self.serverQoS, udpOptions: self.udpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childUDPOptions: self.udpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift index 74ba87b..ec504df 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift @@ -81,19 +81,23 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childUDPOptions: NWProtocolUDP.Options, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, protocolOptions: .udp(udpOptions), tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator, childLoopGroup: childLoopGroup, childChannelQoS: childChannelQoS, childProtocolOptions: .udp(childUDPOptions), - childTLSOptions: childTLSOptions + childTLSOptions: childTLSOptions, + childNWParametersConfigurator: childNWParametersConfigurator ) } @@ -104,20 +108,24 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childUDPOptions: NWProtocolUDP.Options, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( wrapping: listener, eventLoop: eventLoop, protocolOptions: .udp(udpOptions), tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator, childLoopGroup: childLoopGroup, childChannelQoS: childChannelQoS, childProtocolOptions: .udp(childUDPOptions), - childTLSOptions: childTLSOptions + childTLSOptions: childTLSOptions, + childNWParametersConfigurator: childNWParametersConfigurator ) } @@ -132,7 +140,8 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel [ChannelHandler])? = nil + private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? /// Create a `NIOTSConnectionBootstrap` on the `EventLoopGroup` `group`. /// @@ -165,6 +166,14 @@ public final class NIOTSConnectionBootstrap { self.channelOption(NIOTSChannelOptions.multipathServiceType, value: type) } + /// Customise the `NWParameters` to be used when creating the connection. + public func configureNWParameters( + _ configurator: @Sendable @escaping (inout NWParameters) -> Void + ) -> Self { + self.nwParametersConfigurator = configurator + return self + } + /// Specify the `host` and `port` to connect to for the TCP `Channel` that will be established. /// /// - parameters: @@ -243,14 +252,16 @@ public final class NIOTSConnectionBootstrap { wrapping: newConnection, on: self.group.next() as! NIOTSEventLoop, tcpOptions: self.tcpOptions, - tlsOptions: self.tlsOptions + tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator ) } else { conn = NIOTSConnectionChannel( eventLoop: self.group.next() as! NIOTSEventLoop, qos: self.qos, tcpOptions: self.tcpOptions, - tlsOptions: self.tlsOptions + tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator ) } let initializer = self.channelInitializer @@ -437,14 +448,16 @@ extension NIOTSConnectionBootstrap { wrapping: newConnection, on: self.group.next() as! NIOTSEventLoop, tcpOptions: self.tcpOptions, - tlsOptions: self.tlsOptions + tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator ) } else { connectionChannel = NIOTSConnectionChannel( eventLoop: self.group.next() as! NIOTSEventLoop, qos: self.qos, tcpOptions: self.tcpOptions, - tlsOptions: self.tlsOptions + tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator ) } let initializer = self.channelInitializer diff --git a/Sources/NIOTransportServices/NIOTSConnectionChannel.swift b/Sources/NIOTransportServices/NIOTSConnectionChannel.swift index a29258f..4588d59 100644 --- a/Sources/NIOTransportServices/NIOTSConnectionChannel.swift +++ b/Sources/NIOTransportServices/NIOTSConnectionChannel.swift @@ -164,8 +164,12 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { /// An `EventLoopPromise` that will be succeeded or failed when a connection attempt succeeds or fails. internal var connectPromise: EventLoopPromise? + private let nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + internal var parameters: NWParameters { - NWParameters(tls: self.tlsOptions, tcp: self.tcpOptions) + var parameters = NWParameters(tls: self.tlsOptions, tcp: self.tcpOptions) + self.nwParametersConfigurator?(¶meters) + return parameters } /// The TCP options for this connection. @@ -242,7 +246,8 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: Int = 1, maximumReceiveLength: Int = 8192, tcpOptions: NWProtocolTCP.Options, - tlsOptions: NWProtocolTLS.Options? + tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -252,6 +257,7 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { self.connectionQueue = eventLoop.channelQueue(label: "nio.nioTransportServices.connectionchannel", qos: qos) self.tcpOptions = tcpOptions self.tlsOptions = tlsOptions + self.nwParametersConfigurator = nwParametersConfigurator // Must come last, as it requires self to be completely initialized. self._pipeline = ChannelPipeline(channel: self) @@ -266,7 +272,8 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: Int = 1, maximumReceiveLength: Int = 8192, tcpOptions: NWProtocolTCP.Options, - tlsOptions: NWProtocolTLS.Options? + tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, @@ -275,7 +282,8 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { minimumIncompleteReceiveLength: minimumIncompleteReceiveLength, maximumReceiveLength: maximumReceiveLength, tcpOptions: tcpOptions, - tlsOptions: tlsOptions + tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator ) self.connection = connection } diff --git a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift index dd36a0f..d621cfe 100644 --- a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift @@ -66,6 +66,7 @@ public final class NIOTSListenerBootstrap { private var tcpOptions: NWProtocolTCP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? private var bindTimeout: TimeAmount? + private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? /// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`. /// @@ -337,10 +338,12 @@ public final class NIOTSListenerBootstrap { qos: self.serverQoS, tcpOptions: self.tcpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childTCPOptions: self.tcpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } else { serverChannel = NIOTSListenerChannel( @@ -348,10 +351,12 @@ public final class NIOTSListenerBootstrap { qos: self.serverQoS, tcpOptions: self.tcpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childTCPOptions: self.tcpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } @@ -558,10 +563,12 @@ extension NIOTSListenerBootstrap { qos: self.serverQoS, tcpOptions: self.tcpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childTCPOptions: self.tcpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } else { serverChannel = NIOTSListenerChannel( @@ -569,10 +576,12 @@ extension NIOTSListenerBootstrap { qos: self.serverQoS, tcpOptions: self.tcpOptions, tlsOptions: self.tlsOptions, + nwParametersConfigurator: self.nwParametersConfigurator, childLoopGroup: self.childGroup, childChannelQoS: self.childQoS, childTCPOptions: self.tcpOptions, - childTLSOptions: self.tlsOptions + childTLSOptions: self.tlsOptions, + childNWParametersConfigurator: self.nwParametersConfigurator ) } diff --git a/Sources/NIOTransportServices/NIOTSListenerChannel.swift b/Sources/NIOTransportServices/NIOTSListenerChannel.swift index cbf8079..b14d9d3 100644 --- a/Sources/NIOTransportServices/NIOTSListenerChannel.swift +++ b/Sources/NIOTransportServices/NIOTSListenerChannel.swift @@ -81,19 +81,23 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childTCPOptions: NWProtocolTCP.Options, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, protocolOptions: .tcp(tcpOptions), tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator, childLoopGroup: childLoopGroup, childChannelQoS: childChannelQoS, childProtocolOptions: .tcp(childTCPOptions), - childTLSOptions: childTLSOptions + childTLSOptions: childTLSOptions, + childNWParametersConfigurator: childNWParametersConfigurator ) } @@ -104,10 +108,12 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childTCPOptions: NWProtocolTCP.Options, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( wrapping: listener, @@ -115,10 +121,12 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel: S /// The TLS options for this listener. internal let tlsOptions: NWProtocolTLS.Options? + internal var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + /// The `DispatchQueue` that socket events for this connection will be dispatched onto. internal let connectionQueue: DispatchQueue @@ -113,6 +115,8 @@ internal class StateManagedListenerChannel: S /// The TLS options to use for child channels. internal let childTLSOptions: NWProtocolTLS.Options? + internal var childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + /// The cache of the local and remote socket addresses. Must be accessed using _addressCacheLock. internal var addressCache = AddressCache(local: nil, remote: nil) @@ -130,10 +134,12 @@ internal class StateManagedListenerChannel: S qos: DispatchQoS? = nil, protocolOptions: ProtocolOptions, tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childProtocolOptions: ProtocolOptions, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -155,20 +161,24 @@ internal class StateManagedListenerChannel: S qos: DispatchQoS? = nil, protocolOptions: ProtocolOptions, tlsOptions: NWProtocolTLS.Options?, + nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childProtocolOptions: ProtocolOptions, - childTLSOptions: NWProtocolTLS.Options? + childTLSOptions: NWProtocolTLS.Options?, + childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, qos: qos, protocolOptions: protocolOptions, tlsOptions: tlsOptions, + nwParametersConfigurator: nwParametersConfigurator, childLoopGroup: childLoopGroup, childChannelQoS: childChannelQoS, childProtocolOptions: childProtocolOptions, - childTLSOptions: childTLSOptions + childTLSOptions: childTLSOptions, + childNWParametersConfigurator: childNWParametersConfigurator ) self.nwListener = listener } diff --git a/Tests/NIOTransportServicesTests/NIOTSChannelMetadataTests.swift b/Tests/NIOTransportServicesTests/NIOTSChannelMetadataTests.swift index f26aa04..7cb8822 100644 --- a/Tests/NIOTransportServicesTests/NIOTSChannelMetadataTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSChannelMetadataTests.swift @@ -37,13 +37,14 @@ final class NIOTSChannelMetadataTests: XCTestCase { }.wait() } - func testThowsIfCalledOnANonInitializedChannel() { + func testThrowsIfCalledOnANonInitializedChannel() { let eventLoopGroup = NIOTSEventLoopGroup() defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) } let channel = NIOTSConnectionChannel( eventLoop: eventLoopGroup.next() as! NIOTSEventLoop, tcpOptions: .init(), - tlsOptions: .init() + tlsOptions: .init(), + nwParametersConfigurator: nil ) XCTAssertThrowsError(try channel.getMetadata(definition: NWProtocolTLS.definition).wait()) { error in XCTAssertTrue(error is NIOTSConnectionNotInitialized, "unexpected error \(error)") From d1825d918b8a3e52cf4369e2f9f4df402aaf03bc Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Wed, 9 Apr 2025 14:41:54 +0100 Subject: [PATCH 2/7] Undo NWParameters as inout in configurator closure NWParameters is a class, so we don't need it to be inout when passing it to the configurator closure --- .../Datagram/NIOTSDatagramBootstrap.swift | 4 ++-- .../Datagram/NIOTSDatagramChannel.swift | 10 +++++----- .../Datagram/NIOTSDatagramListener.swift | 4 ++-- .../Datagram/NIOTSDatagramListenerChannel.swift | 8 ++++---- .../NIOTSConnectionBootstrap.swift | 4 ++-- .../NIOTSConnectionChannel.swift | 10 +++++----- .../NIOTSListenerBootstrap.swift | 2 +- .../NIOTransportServices/NIOTSListenerChannel.swift | 8 ++++---- .../StateManagedListenerChannel.swift | 12 ++++++------ 9 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift index 0872cc8..5ed0e40 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift @@ -47,7 +47,7 @@ public final class NIOTSDatagramBootstrap { private var qos: DispatchQoS? private var udpOptions: NWProtocolUDP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? - private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// Create a `NIOTSDatagramConnectionBootstrap` on the `EventLoopGroup` `group`. /// @@ -136,7 +136,7 @@ public final class NIOTSDatagramBootstrap { /// Customise the `NWParameters` to be used when creating the connection. public func configureNWParameters( - _ configurator: @Sendable @escaping (inout NWParameters) -> Void + _ configurator: @Sendable @escaping (NWParameters) -> Void ) -> Self { self.nwParametersConfigurator = configurator return self diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift index 80d2c7e..7bbba93 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift @@ -140,11 +140,11 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { internal var allowLocalEndpointReuse = false internal var multipathServiceType: NWParameters.MultipathServiceType = .disabled - private let nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private let nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? var parameters: NWParameters { - var parameters = NWParameters(dtls: self.tlsOptions, udp: self.udpOptions) - self.nwParametersConfigurator?(¶meters) + let parameters = NWParameters(dtls: self.tlsOptions, udp: self.udpOptions) + self.nwParametersConfigurator?(parameters) return parameters } @@ -187,7 +187,7 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { maximumReceiveLength: Int = 8192, udpOptions: NWProtocolUDP.Options, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -213,7 +213,7 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { maximumReceiveLength: Int = 8192, udpOptions: NWProtocolUDP.Options, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift index b81ef96..9f6640e 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListener.swift @@ -66,7 +66,7 @@ public final class NIOTSDatagramListenerBootstrap { private var udpOptions: NWProtocolUDP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? private var bindTimeout: TimeAmount? - private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`. /// @@ -239,7 +239,7 @@ public final class NIOTSDatagramListenerBootstrap { /// Customise the `NWParameters` to be used when creating the connection. public func configureNWParameters( - _ configurator: @Sendable @escaping (inout NWParameters) -> Void + _ configurator: @Sendable @escaping (NWParameters) -> Void ) -> Self { self.nwParametersConfigurator = configurator return self diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift index ec504df..de8fdec 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift @@ -81,12 +81,12 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childUDPOptions: NWProtocolUDP.Options, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, @@ -108,12 +108,12 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childUDPOptions: NWProtocolUDP.Options, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( wrapping: listener, diff --git a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift index ad632d4..4649704 100644 --- a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift @@ -62,7 +62,7 @@ public final class NIOTSConnectionBootstrap { private var tcpOptions: NWProtocolTCP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? private var protocolHandlers: (@Sendable () -> [ChannelHandler])? = nil - private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// Create a `NIOTSConnectionBootstrap` on the `EventLoopGroup` `group`. /// @@ -168,7 +168,7 @@ public final class NIOTSConnectionBootstrap { /// Customise the `NWParameters` to be used when creating the connection. public func configureNWParameters( - _ configurator: @Sendable @escaping (inout NWParameters) -> Void + _ configurator: @Sendable @escaping (NWParameters) -> Void ) -> Self { self.nwParametersConfigurator = configurator return self diff --git a/Sources/NIOTransportServices/NIOTSConnectionChannel.swift b/Sources/NIOTransportServices/NIOTSConnectionChannel.swift index 4588d59..11c3f87 100644 --- a/Sources/NIOTransportServices/NIOTSConnectionChannel.swift +++ b/Sources/NIOTransportServices/NIOTSConnectionChannel.swift @@ -164,11 +164,11 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { /// An `EventLoopPromise` that will be succeeded or failed when a connection attempt succeeds or fails. internal var connectPromise: EventLoopPromise? - private let nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private let nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? internal var parameters: NWParameters { - var parameters = NWParameters(tls: self.tlsOptions, tcp: self.tcpOptions) - self.nwParametersConfigurator?(¶meters) + let parameters = NWParameters(tls: self.tlsOptions, tcp: self.tcpOptions) + self.nwParametersConfigurator?(parameters) return parameters } @@ -247,7 +247,7 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { maximumReceiveLength: Int = 8192, tcpOptions: NWProtocolTCP.Options, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -273,7 +273,7 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel { maximumReceiveLength: Int = 8192, tcpOptions: NWProtocolTCP.Options, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, diff --git a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift index d621cfe..22b1280 100644 --- a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift @@ -66,7 +66,7 @@ public final class NIOTSListenerBootstrap { private var tcpOptions: NWProtocolTCP.Options = .init() private var tlsOptions: NWProtocolTLS.Options? private var bindTimeout: TimeAmount? - private var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`. /// diff --git a/Sources/NIOTransportServices/NIOTSListenerChannel.swift b/Sources/NIOTransportServices/NIOTSListenerChannel.swift index b14d9d3..d8b4d3e 100644 --- a/Sources/NIOTransportServices/NIOTSListenerChannel.swift +++ b/Sources/NIOTransportServices/NIOTSListenerChannel.swift @@ -81,12 +81,12 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childTCPOptions: NWProtocolTCP.Options, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, @@ -108,12 +108,12 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childTCPOptions: NWProtocolTCP.Options, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( wrapping: listener, diff --git a/Sources/NIOTransportServices/StateManagedListenerChannel.swift b/Sources/NIOTransportServices/StateManagedListenerChannel.swift index 2762ba2..c28d8fb 100644 --- a/Sources/NIOTransportServices/StateManagedListenerChannel.swift +++ b/Sources/NIOTransportServices/StateManagedListenerChannel.swift @@ -67,7 +67,7 @@ internal class StateManagedListenerChannel: S /// The TLS options for this listener. internal let tlsOptions: NWProtocolTLS.Options? - internal var nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + internal var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// The `DispatchQueue` that socket events for this connection will be dispatched onto. internal let connectionQueue: DispatchQueue @@ -115,7 +115,7 @@ internal class StateManagedListenerChannel: S /// The TLS options to use for child channels. internal let childTLSOptions: NWProtocolTLS.Options? - internal var childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + internal var childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// The cache of the local and remote socket addresses. Must be accessed using _addressCacheLock. internal var addressCache = AddressCache(local: nil, remote: nil) @@ -134,12 +134,12 @@ internal class StateManagedListenerChannel: S qos: DispatchQoS? = nil, protocolOptions: ProtocolOptions, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childProtocolOptions: ProtocolOptions, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.tsEventLoop = eventLoop self.closePromise = eventLoop.makePromise() @@ -161,12 +161,12 @@ internal class StateManagedListenerChannel: S qos: DispatchQoS? = nil, protocolOptions: ProtocolOptions, tlsOptions: NWProtocolTLS.Options?, - nwParametersConfigurator: (@Sendable (inout NWParameters) -> Void)?, + nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?, childLoopGroup: EventLoopGroup, childChannelQoS: DispatchQoS?, childProtocolOptions: ProtocolOptions, childTLSOptions: NWProtocolTLS.Options?, - childNWParametersConfigurator: (@Sendable (inout NWParameters) -> Void)? + childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? ) { self.init( eventLoop: eventLoop, From a5f1273feabfd46cb4f7b99946eb2173fe26be36 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Wed, 9 Apr 2025 14:42:44 +0100 Subject: [PATCH 3/7] Add missed initialization of the configurator to StateManagedListenerChannel --- Sources/NIOTransportServices/StateManagedListenerChannel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/NIOTransportServices/StateManagedListenerChannel.swift b/Sources/NIOTransportServices/StateManagedListenerChannel.swift index c28d8fb..8dc3501 100644 --- a/Sources/NIOTransportServices/StateManagedListenerChannel.swift +++ b/Sources/NIOTransportServices/StateManagedListenerChannel.swift @@ -146,10 +146,12 @@ internal class StateManagedListenerChannel: S self.connectionQueue = eventLoop.channelQueue(label: "nio.transportservices.listenerchannel", qos: qos) self.protocolOptions = protocolOptions self.tlsOptions = tlsOptions + self.nwParametersConfigurator = nwParametersConfigurator self.childLoopGroup = childLoopGroup self.childChannelQoS = childChannelQoS self.childProtocolOptions = childProtocolOptions self.childTLSOptions = childTLSOptions + self.childNWParametersConfigurator = childNWParametersConfigurator // Must come last, as it requires self to be completely initialized. self._pipeline = ChannelPipeline(channel: self) From 73c30da46eeb4a49e69217928f41b401931088b0 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Wed, 9 Apr 2025 14:42:57 +0100 Subject: [PATCH 4/7] Add missing configurator method to the NIOTSListenerBootstrap --- Sources/NIOTransportServices/NIOTSListenerBootstrap.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift index 22b1280..ef4cd32 100644 --- a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift @@ -240,6 +240,14 @@ public final class NIOTSListenerBootstrap { return self } + /// Customise the `NWParameters` to be used when creating the connection. + public func configureNWParameters( + _ configurator: @Sendable @escaping (NWParameters) -> Void + ) -> Self { + self.nwParametersConfigurator = configurator + return self + } + /// Specifies a type of Multipath service to use for this listener, instead of the default /// service type for the event loop. /// From 1d43eb3246b6554e955d71d7527074cca7f66348 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Wed, 9 Apr 2025 14:43:09 +0100 Subject: [PATCH 5/7] Add missing call to configurator to NIOTSManagedListenerChannel --- Sources/NIOTransportServices/StateManagedListenerChannel.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/NIOTransportServices/StateManagedListenerChannel.swift b/Sources/NIOTransportServices/StateManagedListenerChannel.swift index 8dc3501..42dcb7c 100644 --- a/Sources/NIOTransportServices/StateManagedListenerChannel.swift +++ b/Sources/NIOTransportServices/StateManagedListenerChannel.swift @@ -410,6 +410,8 @@ extension StateManagedListenerChannel { parameters.multipathServiceType = self.multipathServiceType + self.nwParametersConfigurator?(parameters) + let listener: NWListener do { listener = try NWListener(using: parameters) From 3958806697ae27d404e5944cf77994cafd83b8c5 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Wed, 9 Apr 2025 14:43:16 +0100 Subject: [PATCH 6/7] Add tests --- .../NIOTSBootstrapTests.swift | 30 ++++++++++++++++++ .../NIOTSDatagramConnectionChannelTests.swift | 31 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift b/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift index 33c0bd0..c6242cc 100644 --- a/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift @@ -371,6 +371,36 @@ final class NIOTSBootstrapTests: XCTestCase { XCTAssertEqual(try listenerChannel.getOption(NIOTSChannelOptions.multipathServiceType).wait(), .handover) XCTAssertEqual(try connectionChannel.getOption(NIOTSChannelOptions.multipathServiceType).wait(), .handover) } + + func testNWParametersConfigurator() throws { + let group = NIOTSEventLoopGroup() + defer { + try! group.syncShutdownGracefully() + } + + let configuratorListenerCounter = NIOLockedValueBox(0) + let configuratorConnectionCounter = NIOLockedValueBox(0) + + let listenerChannel = try NIOTSListenerBootstrap(group: group) + .configureNWParameters { _ in + configuratorListenerCounter.withLockedValue { $0 += 1 } + } + .bind(host: "localhost", port: 0) + .wait() + + let connectionChannel: Channel = try NIOTSConnectionBootstrap(group: group) + .configureNWParameters { _ in + configuratorConnectionCounter.withLockedValue { $0 += 1 } + } + .connect(to: listenerChannel.localAddress!) + .wait() + + try listenerChannel.close().wait() + try connectionChannel.close().wait() + + XCTAssertEqual(1, configuratorListenerCounter.withLockedValue { $0 }) + XCTAssertEqual(1, configuratorConnectionCounter.withLockedValue { $0 }) + } } extension Channel { diff --git a/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift b/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift index 9b74395..b9f8b2f 100644 --- a/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift @@ -18,6 +18,7 @@ import Network import NIOCore import NIOTransportServices import Foundation +import NIOConcurrencyHelpers extension Channel { func wait(for type: T.Type, count: Int) throws -> [T] { @@ -232,6 +233,36 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase { XCTAssertNoThrow(try connection.close().wait()) } + func testNWParametersConfigurator() throws { + let group = NIOTSEventLoopGroup() + defer { + try! group.syncShutdownGracefully() + } + + let configuratorListenerCounter = NIOLockedValueBox(0) + let configuratorConnectionCounter = NIOLockedValueBox(0) + + let listenerChannel = try NIOTSDatagramListenerBootstrap(group: group) + .configureNWParameters { _ in + configuratorListenerCounter.withLockedValue { $0 += 1 } + } + .bind(host: "localhost", port: 0) + .wait() + + let connectionChannel: Channel = try NIOTSDatagramBootstrap(group: group) + .configureNWParameters { _ in + configuratorConnectionCounter.withLockedValue { $0 += 1 } + } + .connect(to: listenerChannel.localAddress!) + .wait() + + try listenerChannel.close().wait() + try connectionChannel.close().wait() + + XCTAssertEqual(1, configuratorListenerCounter.withLockedValue { $0 }) + XCTAssertEqual(1, configuratorConnectionCounter.withLockedValue { $0 }) + } + func testCanExtractTheConnection() throws { guard #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) else { throw XCTSkip("Option not available") From 72e94c6ce9013a1034a1cb8aa129681f1a960ff5 Mon Sep 17 00:00:00 2001 From: Gus Cairo Date: Thu, 10 Apr 2025 16:01:27 +0100 Subject: [PATCH 7/7] Turn vars into lets --- .../NIOTransportServices/StateManagedListenerChannel.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Sources/NIOTransportServices/StateManagedListenerChannel.swift b/Sources/NIOTransportServices/StateManagedListenerChannel.swift index 42dcb7c..4f19c9e 100644 --- a/Sources/NIOTransportServices/StateManagedListenerChannel.swift +++ b/Sources/NIOTransportServices/StateManagedListenerChannel.swift @@ -67,7 +67,8 @@ internal class StateManagedListenerChannel: S /// The TLS options for this listener. internal let tlsOptions: NWProtocolTLS.Options? - internal var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? + /// A customization point for this listener's `NWParameters`. + internal let nwParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// The `DispatchQueue` that socket events for this connection will be dispatched onto. internal let connectionQueue: DispatchQueue @@ -115,7 +116,8 @@ internal class StateManagedListenerChannel: S /// The TLS options to use for child channels. internal let childTLSOptions: NWProtocolTLS.Options? - internal var childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? + /// A customization point for each child's `NWParameters`. + internal let childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)? /// The cache of the local and remote socket addresses. Must be accessed using _addressCacheLock. internal var addressCache = AddressCache(local: nil, remote: nil)