diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift index 611ad85..514b611 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramBootstrap.swift @@ -152,7 +152,7 @@ public final class NIOTSDatagramBootstrap { /// - returns: An `EventLoopFuture` to deliver the `Channel` when connected. public func connect(to address: SocketAddress) -> EventLoopFuture { return self.connect0 { channel, promise in - channel.bind(to: address, promise: promise) + channel.connect(to: address, promise: promise) } } diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift index a6a48e5..31bb5f8 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramChannel.swift @@ -187,4 +187,27 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel { self.connection = connection } } + +@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) +extension NIOTSDatagramChannel { + internal struct SynchronousOptions: NIOSynchronousChannelOptions { + private let channel: NIOTSDatagramChannel + + fileprivate init(channel: NIOTSDatagramChannel) { + self.channel = channel + } + + public func setOption(_ option: Option, value: Option.Value) throws { + try self.channel.setOption0(option: option, value: value) + } + + public func getOption(_ option: Option) throws -> Option.Value { + return try self.channel.getOption0(option: option) + } + } + + public var syncOptions: NIOSynchronousChannelOptions? { + return SynchronousOptions(channel: self) + } +} #endif diff --git a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift index cd77929..a943804 100644 --- a/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift +++ b/Sources/NIOTransportServices/Datagram/NIOTSDatagramListenerChannel.swift @@ -127,10 +127,7 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel: S func newConnectionHandler(connection: NWConnection) { fatalError("This function must be overridden by the subclass") } + + // This needs to be declared here to make sure the child classes can override + // the behaviour. + internal var syncOptions: NIOSynchronousChannelOptions? { + return nil + } + } @available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) diff --git a/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift b/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift index 42e76ef..0a9d62a 100644 --- a/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSDatagramConnectionChannelTests.swift @@ -158,5 +158,51 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase { XCTAssertEqual(reads.count, 1) XCTAssertEqual(reads[0], buffer) } + + func testSyncOptionsAreSupported() throws { + func testSyncOptions(_ channel: Channel) { + if let sync = channel.syncOptions { + do { + let endpointReuse = try sync.getOption(NIOTSChannelOptions.allowLocalEndpointReuse) + try sync.setOption(NIOTSChannelOptions.allowLocalEndpointReuse, value: !endpointReuse) + XCTAssertNotEqual(endpointReuse, try sync.getOption(NIOTSChannelOptions.allowLocalEndpointReuse)) + } catch { + XCTFail("Could not get/set allowLocalEndpointReuse: \(error)") + } + } else { + XCTFail("\(channel) unexpectedly returned nil syncOptions") + } + } + + let promise = self.group.any().makePromise(of: Channel.self) + let listener = try NIOTSDatagramListenerBootstrap(group: self.group) + .serverChannelInitializer { channel in + testSyncOptions(channel) + return channel.eventLoop.makeSucceededVoidFuture() + } + .childChannelInitializer { channel in + testSyncOptions(channel) + promise.succeed(channel) + return channel.pipeline.addHandler(ReadRecorder(), name: "ByteReadRecorder") + } + .bind(host: "localhost", port: 0) + .wait() + defer { + XCTAssertNoThrow(try listener.close().wait()) + } + + let connection = try! NIOTSDatagramBootstrap(group: self.group) + .channelInitializer { channel in + testSyncOptions(channel) + return channel.eventLoop.makeSucceededVoidFuture() + } + .connect(to: listener.localAddress!) + .wait() + try connection.writeAndFlush(ByteBuffer(string: "hello world")).wait() + + let serverHandle = try promise.futureResult.wait() + _ = try serverHandle.waitForDatagrams(count: 1) + XCTAssertNoThrow(try connection.close().wait()) + } } #endif diff --git a/Tests/NIOTransportServicesTests/NIOTSListenerChannelTests.swift b/Tests/NIOTransportServicesTests/NIOTSListenerChannelTests.swift index 179b123..8c8b1f1 100644 --- a/Tests/NIOTransportServicesTests/NIOTSListenerChannelTests.swift +++ b/Tests/NIOTransportServicesTests/NIOTSListenerChannelTests.swift @@ -306,5 +306,31 @@ class NIOTSListenerChannelTests: XCTestCase { XCTAssertNoThrow(try workFuture.wait()) } + + func testSyncOptionsAreSupported() throws { + func testSyncOptions(_ channel: Channel) { + if let sync = channel.syncOptions { + do { + let endpointReuse = try sync.getOption(NIOTSChannelOptions.allowLocalEndpointReuse) + try sync.setOption(NIOTSChannelOptions.allowLocalEndpointReuse, value: !endpointReuse) + XCTAssertNotEqual(endpointReuse, try sync.getOption(NIOTSChannelOptions.allowLocalEndpointReuse)) + } catch { + XCTFail("Could not get/set allowLocalEndpointReuse: \(error)") + } + } else { + XCTFail("\(channel) unexpectedly returned nil syncOptions") + } + } + + let listener = try NIOTSListenerBootstrap(group: self.group) + .serverChannelInitializer { channel in + testSyncOptions(channel) + return channel.eventLoop.makeSucceededVoidFuture() + } + .bind(host: "localhost", port: 0) + .wait() + + XCTAssertNoThrow(try listener.close().wait()) + } } #endif