Compare commits

...

2 Commits

Author SHA1 Message Date
Gus Cairo 885de32ed5 PR changes 2025-04-16 14:22:56 +01:00
Gus Cairo 4efad51013 Add child TCP/UDP/TLS options and child NWParameters configurator 2025-04-16 13:53:18 +01:00
6 changed files with 114 additions and 66 deletions

View File

@ -64,9 +64,12 @@ public final class NIOTSDatagramListenerBootstrap {
private var serverQoS: DispatchQoS?
private var childQoS: DispatchQoS?
private var udpOptions: NWProtocolUDP.Options = .init()
private var childUDPOptions: NWProtocolUDP.Options = .init()
private var tlsOptions: NWProtocolTLS.Options?
private var childTLSOptions: NWProtocolTLS.Options?
private var bindTimeout: TimeAmount?
private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?
private var childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)?
/// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`.
///
@ -225,19 +228,31 @@ public final class NIOTSDatagramListenerBootstrap {
return self
}
/// Specifies the TCP options to use on the child `Channel`s.
/// Specifies the TCP options to use on the listener.
public func udpOptions(_ options: NWProtocolUDP.Options) -> Self {
self.udpOptions = options
return self
}
/// Specifies the TLS options to use on the child `Channel`s.
/// Specifies the TCP options to use on the child `Channel`s.
public func childUDPOptions(_ options: NWProtocolUDP.Options) -> Self {
self.childUDPOptions = options
return self
}
/// Specifies the TLS options to use on the listener.
public func tlsOptions(_ options: NWProtocolTLS.Options) -> Self {
self.tlsOptions = options
return self
}
/// Customise the `NWParameters` to be used when creating the connection.
/// Specifies the TLS options to use on the child `Channel`s.
public func childTLSOptions(_ options: NWProtocolTLS.Options) -> Self {
self.childTLSOptions = options
return self
}
/// Customise the `NWParameters` to be used when creating the `NWConnection` for the listener.
public func configureNWParameters(
_ configurator: @Sendable @escaping (NWParameters) -> Void
) -> Self {
@ -245,6 +260,14 @@ public final class NIOTSDatagramListenerBootstrap {
return self
}
/// Customise the `NWParameters` to be used when creating the `NWConnection`s for the child `Channel`s.
public func configureChildNWParameters(
_ configurator: @Sendable @escaping (NWParameters) -> Void
) -> Self {
self.childNWParametersConfigurator = configurator
return self
}
/// Bind the `NIOTSListenerChannel` to `host` and `port`.
///
/// - parameters:
@ -339,9 +362,9 @@ public final class NIOTSDatagramListenerBootstrap {
nwParametersConfigurator: self.nwParametersConfigurator,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions,
childNWParametersConfigurator: self.nwParametersConfigurator
childUDPOptions: self.childUDPOptions,
childTLSOptions: self.childTLSOptions,
childNWParametersConfigurator: self.childNWParametersConfigurator
)
} else {
serverChannel = NIOTSDatagramListenerChannel(
@ -352,9 +375,9 @@ public final class NIOTSDatagramListenerBootstrap {
nwParametersConfigurator: self.nwParametersConfigurator,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions,
childNWParametersConfigurator: self.nwParametersConfigurator
childUDPOptions: self.childUDPOptions,
childTLSOptions: self.childTLSOptions,
childNWParametersConfigurator: self.childNWParametersConfigurator
)
}

View File

@ -64,9 +64,12 @@ public final class NIOTSListenerBootstrap {
private var serverQoS: DispatchQoS?
private var childQoS: DispatchQoS?
private var tcpOptions: NWProtocolTCP.Options = .init()
private var childTCPOptions: NWProtocolTCP.Options = .init()
private var tlsOptions: NWProtocolTLS.Options?
private var childTLSOptions: NWProtocolTLS.Options?
private var bindTimeout: TimeAmount?
private var nwParametersConfigurator: (@Sendable (NWParameters) -> Void)?
private var childNWParametersConfigurator: (@Sendable (NWParameters) -> Void)?
/// Create a ``NIOTSListenerBootstrap`` for the `EventLoopGroup` `group`.
///
@ -228,19 +231,31 @@ public final class NIOTSListenerBootstrap {
return self
}
/// Specifies the TCP options to use on the child `Channel`s.
/// Specifies the TCP options to use on the listener.
public func tcpOptions(_ options: NWProtocolTCP.Options) -> Self {
self.tcpOptions = options
return self
}
/// Specifies the TLS options to use on the child `Channel`s.
/// Specifies the TCP options to use on the child `Channel`s.
public func childTCPOptions(_ options: NWProtocolTCP.Options) -> Self {
self.childTCPOptions = options
return self
}
/// Specifies the TLS options to use on the listener.
public func tlsOptions(_ options: NWProtocolTLS.Options) -> Self {
self.tlsOptions = options
return self
}
/// Customise the `NWParameters` to be used when creating the connection.
/// Specifies the TLS options to use on the child `Channel`s.
public func childTLSOptions(_ options: NWProtocolTLS.Options) -> Self {
self.childTLSOptions = options
return self
}
/// Customise the `NWParameters` to be used when creating the `NWConnection` for the listener.
public func configureNWParameters(
_ configurator: @Sendable @escaping (NWParameters) -> Void
) -> Self {
@ -248,6 +263,14 @@ public final class NIOTSListenerBootstrap {
return self
}
/// Customise the `NWParameters` to be used when creating the `NWConnection`s for the child `Channel`s.
public func configureChildNWParameters(
_ configurator: @Sendable @escaping (NWParameters) -> Void
) -> Self {
self.childNWParametersConfigurator = configurator
return self
}
/// Specifies a type of Multipath service to use for this listener, instead of the default
/// service type for the event loop.
///
@ -349,9 +372,9 @@ public final class NIOTSListenerBootstrap {
nwParametersConfigurator: self.nwParametersConfigurator,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childTCPOptions: self.tcpOptions,
childTLSOptions: self.tlsOptions,
childNWParametersConfigurator: self.nwParametersConfigurator
childTCPOptions: self.childTCPOptions,
childTLSOptions: self.childTLSOptions,
childNWParametersConfigurator: self.childNWParametersConfigurator
)
} else {
serverChannel = NIOTSListenerChannel(
@ -362,9 +385,9 @@ public final class NIOTSListenerBootstrap {
nwParametersConfigurator: self.nwParametersConfigurator,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childTCPOptions: self.tcpOptions,
childTLSOptions: self.tlsOptions,
childNWParametersConfigurator: self.nwParametersConfigurator
childTCPOptions: self.childTCPOptions,
childTLSOptions: self.childTLSOptions,
childNWParametersConfigurator: self.childNWParametersConfigurator
)
}

View File

@ -373,24 +373,10 @@ final class NIOTSBootstrapTests: XCTestCase {
}
func testNWParametersConfigurator() async throws {
final class WaitForConnectionHandler: ChannelInboundHandler, Sendable {
typealias InboundIn = Never
let connectionPromise: EventLoopPromise<Void>
init(connectionPromise: EventLoopPromise<Void>) {
self.connectionPromise = connectionPromise
}
func channelActive(context: ChannelHandlerContext) {
self.connectionPromise.succeed()
context.fireChannelActive()
}
}
try await withEventLoopGroup { group in
let configuratorListenerCounter = NIOLockedValueBox(0)
let configuratorConnectionCounter = NIOLockedValueBox(0)
let configuratorServerListenerCounter = NIOLockedValueBox(0)
let configuratorServerConnectionCounter = NIOLockedValueBox(0)
let configuratorClientConnectionCounter = NIOLockedValueBox(0)
let waitForConnectionHandler = WaitForConnectionHandler(
connectionPromise: group.next().makePromise()
)
@ -402,14 +388,17 @@ final class NIOTSBootstrapTests: XCTestCase {
}
}
.configureNWParameters { _ in
configuratorListenerCounter.withLockedValue { $0 += 1 }
configuratorServerListenerCounter.withLockedValue { $0 += 1 }
}
.configureChildNWParameters { _ in
configuratorServerConnectionCounter.withLockedValue { $0 += 1 }
}
.bind(host: "localhost", port: 0)
.get()
let connectionChannel: Channel = try await NIOTSConnectionBootstrap(group: group)
.configureNWParameters { _ in
configuratorConnectionCounter.withLockedValue { $0 += 1 }
configuratorClientConnectionCounter.withLockedValue { $0 += 1 }
}
.connect(to: listenerChannel.localAddress!)
.get()
@ -420,8 +409,9 @@ final class NIOTSBootstrapTests: XCTestCase {
try await listenerChannel.close().get()
try await connectionChannel.close().get()
XCTAssertEqual(2, configuratorListenerCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorConnectionCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorServerListenerCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorServerConnectionCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorClientConnectionCounter.withLockedValue { $0 })
}
}
}

View File

@ -272,11 +272,15 @@ class NIOTSConnectionChannelTests: XCTestCase {
}
func testSettingTCPOptionsWholesale() throws {
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.disableAckStretching = true
let listenerTCPOptions = NWProtocolTCP.Options()
listenerTCPOptions.disableAckStretching = true
let connectionTCPOptions = NWProtocolTCP.Options()
connectionTCPOptions.disableAckStretching = true
let listener = try NIOTSListenerBootstrap(group: self.group)
.tcpOptions(tcpOptions)
.tcpOptions(listenerTCPOptions)
.childTCPOptions(connectionTCPOptions)
.serverChannelInitializer { channel in
channel.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_SENDMOREACKS)).map { value in
XCTAssertEqual(value, 1)
@ -293,7 +297,7 @@ class NIOTSConnectionChannelTests: XCTestCase {
}
let connection = try NIOTSConnectionBootstrap(group: self.group)
.tcpOptions(tcpOptions)
.tcpOptions(connectionTCPOptions)
.channelInitializer { channel in
channel.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_SENDMOREACKS)).map { value in
XCTAssertEqual(value, 1)

View File

@ -234,24 +234,10 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase {
}
func testNWParametersConfigurator() async throws {
final class WaitForConnectionHandler: ChannelInboundHandler, Sendable {
typealias InboundIn = Never
let connectionPromise: EventLoopPromise<Void>
init(connectionPromise: EventLoopPromise<Void>) {
self.connectionPromise = connectionPromise
}
func channelActive(context: ChannelHandlerContext) {
self.connectionPromise.succeed()
context.fireChannelActive()
}
}
try await withEventLoopGroup { group in
let configuratorListenerCounter = NIOLockedValueBox(0)
let configuratorConnectionCounter = NIOLockedValueBox(0)
let configuratorServerListenerCounter = NIOLockedValueBox(0)
let configuratorServerConnectionCounter = NIOLockedValueBox(0)
let configuratorClientConnectionCounter = NIOLockedValueBox(0)
let waitForConnectionHandler = WaitForConnectionHandler(
connectionPromise: group.next().makePromise()
)
@ -263,21 +249,24 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase {
}
}
.configureNWParameters { _ in
configuratorListenerCounter.withLockedValue { $0 += 1 }
configuratorServerListenerCounter.withLockedValue { $0 += 1 }
}
.configureChildNWParameters { _ in
configuratorServerConnectionCounter.withLockedValue { $0 += 1 }
}
.bind(host: "localhost", port: 0)
.get()
let connectionChannel: Channel = try await NIOTSDatagramBootstrap(group: group)
.configureNWParameters { _ in
configuratorConnectionCounter.withLockedValue { $0 += 1 }
configuratorClientConnectionCounter.withLockedValue { $0 += 1 }
}
.connect(to: listenerChannel.localAddress!)
.get()
// Need to write something so the server can activate the connection channel: this is UDP,
// so there is no handshaking that happens and thus the server cannot know that the
// connection has been established and the channel can be activated.
// connection has been established and the channel can be activated until we receive something.
try await connectionChannel.writeAndFlush(ByteBuffer(bytes: [42]))
// Wait for the server to activate the connection channel to the client.
@ -286,8 +275,9 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase {
try await listenerChannel.close().get()
try await connectionChannel.close().get()
XCTAssertEqual(2, configuratorListenerCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorConnectionCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorServerListenerCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorServerConnectionCounter.withLockedValue { $0 })
XCTAssertEqual(1, configuratorClientConnectionCounter.withLockedValue { $0 })
}
}

View File

@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
#if canImport(Network)
import NIOCore
import NIOTransportServices
@ -19,8 +20,25 @@ func withEventLoopGroup(_ test: (EventLoopGroup) async throws -> Void) async ret
let group = NIOTSEventLoopGroup()
do {
try await test(group)
try? await group.shutdownGracefully()
} catch {
try await group.shutdownGracefully()
try? await group.shutdownGracefully()
throw error
}
}
final class WaitForConnectionHandler: ChannelInboundHandler, Sendable {
typealias InboundIn = Never
let connectionPromise: EventLoopPromise<Void>
init(connectionPromise: EventLoopPromise<Void>) {
self.connectionPromise = connectionPromise
}
func channelActive(context: ChannelHandlerContext) {
self.connectionPromise.succeed()
context.fireChannelActive()
}
}
#endif