From afd169118ca1e6798a77bbedc33468eca8daef55 Mon Sep 17 00:00:00 2001 From: Anthony Doeraene <78789735+Aperence@users.noreply.github.com> Date: Wed, 18 Sep 2024 18:50:40 +0200 Subject: [PATCH] Add configuration of multipathServiceType in NIOTSConnectionBootstrap (#205) ### Motivation: Allow different devices to leverage the capabilities of Mutipath TCP (MPTCP) to enhance the network reliability. Thanks to MPTCP, a connection can for example automatically migrate to another interface if it deteriorates on the first one. This can be especially interesting on MacOS X and iOS, where devices may frequently benefit from multiple interfaces (ethernet + Wi-Fi for Macs and Wi-fi + cellular for iOS). Allowing developers to enable MPTCP on their connections seems thus like a fine addition to this library. ### Modifications: Added a function "withMultipath" on NIOTSConnectionBootstrap, that allow to configure the type of service used for MPTCP (defaults to disabled). This value will be stored in a field, and then propagated to the underlying channel when the connect method will be called. Also updated the parameters field of NIOTSConnectionChannel to set the multipathServiceType accordingly. ### Result: Users will now be able to easily enable Multipath TCP, allowing them to benefit from seamless handover, interactive mode to automatically use the lowest delay interface or aggregate mode to send data in parallel on both interfaces. Example: ```swift let group = NIOTSEventLoopGroup() defer { try! group.syncShutdownGracefully() } let bootstrap = NIOTSConnectionBootstrap(group: group) .withMultipath(.handover) // set handover mode .channelInitializer { channel in channel.pipeline.addHandler(MyChannelHandler()) } try! bootstrap.connect(host: "example.org", port: 12345).wait() /* the Channel is now connected */ ``` Co-authored-by: Cory Benfield --- .../NIOTSConnectionBootstrap.swift | 6 ++++++ .../NIOTSListenerBootstrap.swift | 10 ++++++++++ .../NIOTSBootstrapTests.swift | 19 +++++++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift index 277ea02..4dd4889 100644 --- a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift @@ -154,6 +154,12 @@ public final class NIOTSConnectionBootstrap { return self } + /// Specifies a type of Multipath service to use for this connection, instead of the default + /// service type for the event loop. + public func withMultipath(_ type: NWParameters.MultipathServiceType) -> Self { + self.channelOption(NIOTSChannelOptions.multipathServiceType, value: type) + } + /// Specify the `host` and `port` to connect to for the TCP `Channel` that will be established. /// /// - parameters: diff --git a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift index ed5241a..f489b4a 100644 --- a/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSListenerBootstrap.swift @@ -235,6 +235,16 @@ public final class NIOTSListenerBootstrap { return self } + /// Specifies a type of Multipath service to use for this listener, instead of the default + /// service type for the event loop. + /// + /// Warning: Multipath service doesn't seem supported on the listener side yet, as + /// described on https://www.mptcp.dev/macOS.html. Note that enabling Multipath + /// may then generate unexpected errors, use this function with caution. + public func withMultipath(_ type: NWParameters.MultipathServiceType) -> Self { + self.serverChannelOption(NIOTSChannelOptions.multipathServiceType, value: type) + } + /// Bind the `NIOTSListenerChannel` to `host` and `port`. /// /// - parameters: diff --git a/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift b/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift index 27c43de..0358bed 100644 --- a/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSBootstrapTests.swift @@ -336,6 +336,25 @@ final class NIOTSBootstrapTests: XCTestCase { try? connectionChannel?.close().wait() } } + + func testBootstrapsMultipath() throws { + let group = NIOTSEventLoopGroup() + defer { + try! group.syncShutdownGracefully() + } + + let listenerBootstrap = NIOTSListenerBootstrap(group: group).withMultipath(.handover) + let connectionBootstrap = NIOTSConnectionBootstrap(group: group).withMultipath(.handover) + + let listenerChannel: Channel = try listenerBootstrap.bind(host: "localhost", port: 0).wait() + let connectionChannel: Channel = try connectionBootstrap.connect(to: listenerChannel.localAddress!).wait() + defer{ + try? listenerChannel.close().wait() + try? connectionChannel.close().wait() + } + XCTAssertEqual(try listenerChannel.getOption(NIOTSChannelOptions.multipathServiceType).wait(), .handover) + XCTAssertEqual(try connectionChannel.getOption(NIOTSChannelOptions.multipathServiceType).wait(), .handover) + } } extension Channel {