From 553f2f73ed8bf40987c4663c512d9e94f77f36cf Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Wed, 1 May 2019 14:24:23 +0100 Subject: [PATCH] Side-port apple/swift-nio#597. (#41) Motivation: We never brought across the fix to apple/swift-nio#597 into swift-nio-transport-services. This has caused some users a few hard-to-track-down surprises. We fixed this in the mainline by way of #40, but users running the old codebase probably want this fix too. Modifications: - Side-ported the fix from apple/swift-nio#597. - Back-ported the test from #40. Result: Users can set multiple socket options. --- .../NIOTSConnectionBootstrap.swift | 23 +++++++++++++++++-- .../NIOTSSocketOptionsOnChannelTests.swift | 22 ++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift index f4883b9..3d391a8 100644 --- a/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift +++ b/Sources/NIOTransportServices/NIOTSConnectionBootstrap.swift @@ -177,8 +177,27 @@ public final class NIOTSConnectionBootstrap { internal struct ChannelOptionStorage { private var storage: [(Any, (Any, (Channel) -> (Any, Any) -> EventLoopFuture))] = [] + mutating func put(key: K, value: K.OptionType) { + return self.put(key: key, value: value, equalsFunc: ==) + } + + // HACK: this function should go for NIO 2.0, all ChannelOptions should be equatable + mutating func put(key: K, value: K.OptionType) { + if K.self == SocketOption.self { + return self.put(key: key as! SocketOption, value: value as! SocketOptionValue) { lhs, rhs in + switch (lhs, rhs) { + case (.const(let lLevel, let lName), .const(let rLevel, let rName)): + return lLevel == rLevel && lName == rName + } + } + } else { + return self.put(key: key, value: value) { _, _ in true } + } + } + mutating func put(key: K, - value newValue: K.OptionType) { + value newValue: K.OptionType, + equalsFunc: (K, K) -> Bool) { func applier(_ t: Channel) -> (Any, Any) -> EventLoopFuture { return { (x, y) in return t.setOption(option: x as! K, value: y as! K.OptionType) @@ -187,7 +206,7 @@ internal struct ChannelOptionStorage { var hasSet = false self.storage = self.storage.map { typeAndValue in let (type, value) = typeAndValue - if type is K { + if type is K && equalsFunc(type as! K, key) { hasSet = true return (key, (newValue, applier)) } else { diff --git a/Tests/NIOTransportServicesTests/NIOTSSocketOptionsOnChannelTests.swift b/Tests/NIOTransportServicesTests/NIOTSSocketOptionsOnChannelTests.swift index e89eda6..1efda23 100644 --- a/Tests/NIOTransportServicesTests/NIOTSSocketOptionsOnChannelTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSSocketOptionsOnChannelTests.swift @@ -120,5 +120,27 @@ class NIOTSSocketOptionsOnChannelTests: XCTestCase { func testSO_KEEPALIVE() throws { try self.assertChannelOptionAfterCreation(option: SocketOption(level: SOL_SOCKET, name: SO_KEEPALIVE), initialValue: 0, testAlternativeValue: 1) } + + func testMultipleSocketOptions() throws { + let listener = try NIOTSListenerBootstrap(group: group) + .serverChannelOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR), value: 1) + .serverChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + .bind(host: "127.0.0.1", port: 0).wait() + defer { + XCTAssertNoThrow(try listener.close().wait()) + } + let connector = try NIOTSConnectionBootstrap(group: group) + .channelOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR), value: 1) + .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) + .connect(to: listener.localAddress!).wait() + defer { + XCTAssertNoThrow(try connector.close().wait()) + } + + XCTAssertNoThrow(XCTAssertEqual(1, try listener.getOption(option: ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait())) + XCTAssertNoThrow(XCTAssertEqual(1, try listener.getOption(option: ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait())) + XCTAssertNoThrow(XCTAssertEqual(1, try connector.getOption(option: ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait())) + XCTAssertNoThrow(XCTAssertEqual(1, try connector.getOption(option: ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait())) + } }