Skip to content

Commit 068512c

Browse files
committed
Add an option to enable Multipath TCP on clients
Multipath TCP (MPTCP) is a TCP extension allowing to enhance the reliability of the network by using multiple interfaces. This extension provides a seamless handover between interfaces in case of deterioration of the connection on the original one. In the context of iOS and Mac OS X, it could be really interesting to leverage the capabilities of MPTCP as they could benefit from their multiple interfaces (ethernet + Wi-fi for Mac OS X, Wi-fi + cellular for iOS). This contribution introduces patches to HTTPClient.Configuration and establishment of the Bootstraps. A supplementary field "enableMultipath" was added to the configuration, allowing to request the use of MPTCP. This flag is then used when creating the channels to configure the client. Note that in the future, it might also be potentially interesting to offer more precise configuration options for MPTCP on MacOS, as the Network framework allows also to select a type of service, instead of just offering the option to create MPTCP connections. Currently, when enabling MPTCP, only the Handover mode is used.
1 parent e8babad commit 068512c

File tree

3 files changed

+25
-0
lines changed

3 files changed

+25
-0
lines changed

Sources/AsyncHTTPClient/ConnectionPool/HTTPConnectionPool+Factory.swift

+4
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ extension HTTPConnectionPool.ConnectionFactory {
322322
if #available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *), let tsBootstrap = NIOTSConnectionBootstrap(validatingGroup: eventLoop) {
323323
return tsBootstrap
324324
.channelOption(NIOTSChannelOptions.waitForActivity, value: self.clientConfiguration.networkFrameworkWaitForConnectivity)
325+
.channelOption(NIOTSChannelOptions.multipathServiceType, value: self.clientConfiguration.enableMultipath ? .handover : .disabled)
325326
.connectTimeout(deadline - NIODeadline.now())
326327
.channelInitializer { channel in
327328
do {
@@ -338,6 +339,7 @@ extension HTTPConnectionPool.ConnectionFactory {
338339
if let nioBootstrap = ClientBootstrap(validatingGroup: eventLoop) {
339340
return nioBootstrap
340341
.connectTimeout(deadline - NIODeadline.now())
342+
.enableMPTCP(clientConfiguration.enableMultipath)
341343
}
342344

343345
preconditionFailure("No matching bootstrap found")
@@ -415,6 +417,7 @@ extension HTTPConnectionPool.ConnectionFactory {
415417

416418
tsBootstrap
417419
.channelOption(NIOTSChannelOptions.waitForActivity, value: self.clientConfiguration.networkFrameworkWaitForConnectivity)
420+
.channelOption(NIOTSChannelOptions.multipathServiceType, value: self.clientConfiguration.enableMultipath ? .handover : .disabled)
418421
.connectTimeout(deadline - NIODeadline.now())
419422
.tlsOptions(options)
420423
.channelInitializer { channel in
@@ -443,6 +446,7 @@ extension HTTPConnectionPool.ConnectionFactory {
443446

444447
let bootstrap = ClientBootstrap(group: eventLoop)
445448
.connectTimeout(deadline - NIODeadline.now())
449+
.enableMPTCP(clientConfiguration.enableMultipath)
446450
.channelInitializer { channel in
447451
sslContextFuture.flatMap { sslContext -> EventLoopFuture<Void> in
448452
do {

Sources/AsyncHTTPClient/HTTPClient.swift

+5
Original file line numberDiff line numberDiff line change
@@ -738,6 +738,10 @@ public class HTTPClient {
738738
}
739739
}
740740

741+
/// Whether ``HTTPClient`` will use Multipath TCP or not
742+
/// By default, don't use it
743+
public var enableMultipath: Bool
744+
741745
public init(
742746
tlsConfiguration: TLSConfiguration? = nil,
743747
redirectConfiguration: RedirectConfiguration? = nil,
@@ -755,6 +759,7 @@ public class HTTPClient {
755759
self.decompression = decompression
756760
self.httpVersion = .automatic
757761
self.networkFrameworkWaitForConnectivity = true
762+
self.enableMultipath = false
758763
}
759764

760765
public init(tlsConfiguration: TLSConfiguration? = nil,

Tests/AsyncHTTPClientTests/HTTPClientTests.swift

+16
Original file line numberDiff line numberDiff line change
@@ -3590,6 +3590,22 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass {
35903590
XCTAssertEqual(.ok, response.status)
35913591
}
35923592

3593+
func testClientWithMultipath() throws {
3594+
do {
3595+
var conf = HTTPClient.Configuration()
3596+
conf.enableMultipath = true
3597+
let client = HTTPClient(configuration: conf)
3598+
defer {
3599+
XCTAssertNoThrow(try client.shutdown().wait())
3600+
}
3601+
let response = try client.get(url: self.defaultHTTPBinURLPrefix + "get").wait()
3602+
XCTAssertEqual(.ok, response.status)
3603+
}catch let error as IOError where error.errnoCode == EINVAL{
3604+
// some old Linux kernels don't support MPTCP, skip this test in this case
3605+
throw XCTSkip()
3606+
}
3607+
}
3608+
35933609
func testSingletonClientWorks() throws {
35943610
let response = try HTTPClient.shared.get(url: self.defaultHTTPBinURLPrefix + "get").wait()
35953611
XCTAssertEqual(.ok, response.status)

0 commit comments

Comments
 (0)