New channel options exposing properties of underlying NWConnection (#90)
* Initial draft implementation of new channel options for NWConnection.currentPath, metadata for a given NWProtocol, establishment report and data transfer report. Add a new error for not existing connection. Add tests for all of these. The data transfer report option has to be implemented in another way, as the collection of the final data transfer report does not seem very straightforward. * Fix incorrect available attributes. Make the Value of NIOTSEstablishmentReportOption an EventLoopFuture returning an optional report. Move handling of NIOTSEstablishmentReportOption and NIOTSDataTransferReportOption to getOption0. Remove nwConnection0(). * Use full type on currentPath too. * Add a DispatchQueue to do collect of the pending data transfer report, use a DispatchGroup to wait for completion of report collection. Remove redundant attribute. Fix nits related to spacing and file header. * Fix available attributes for tests too.
This commit is contained in:
parent
998422e195
commit
7e330732f2
|
|
@ -13,6 +13,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
#if canImport(Network)
|
||||
import NIO
|
||||
import Network
|
||||
|
||||
/// Options that can be set explicitly and only on bootstraps provided by `NIOTransportServices`.
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
|
||||
|
|
@ -21,6 +22,18 @@ public struct NIOTSChannelOptions {
|
|||
public static let waitForActivity = NIOTSChannelOptions.Types.NIOTSWaitForActivityOption()
|
||||
|
||||
public static let enablePeerToPeer = NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption()
|
||||
|
||||
public static let currentPath = NIOTSChannelOptions.Types.NIOTSCurrentPathOption()
|
||||
|
||||
public static let metadata = { (definition: NWProtocolDefinition) -> NIOTSChannelOptions.Types.NIOTSMetadataOption in
|
||||
.init(definition: definition)
|
||||
}
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public static let establishmentReport = NIOTSChannelOptions.Types.NIOTSEstablishmentReportOption()
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public static let dataTransferReport = NIOTSChannelOptions.Types.NIOTSDataTransferReportOption()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -54,6 +67,50 @@ extension NIOTSChannelOptions {
|
|||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// `NIOTSCurrentPathOption` accesses the `NWConnection.currentPath` of the underlying connection.
|
||||
///
|
||||
/// This option is only valid with `NIOTSConnectionBootstrap`.
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
|
||||
public struct NIOTSCurrentPathOption: ChannelOption, Equatable {
|
||||
public typealias Value = NWPath
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// `NIOTSMetadataOption` accesses the metadata for a given `NWProtocol`.
|
||||
///
|
||||
/// This option is only valid with `NIOTSConnectionBootstrap`.
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 5.0, *)
|
||||
public struct NIOTSMetadataOption: ChannelOption, Equatable {
|
||||
public typealias Value = NWProtocolMetadata
|
||||
|
||||
let definition: NWProtocolDefinition
|
||||
|
||||
public init(definition: NWProtocolDefinition) {
|
||||
self.definition = definition
|
||||
}
|
||||
}
|
||||
|
||||
/// `NIOTSEstablishmentReportOption` accesses the `NWConnection.EstablishmentReport` of the underlying connection.
|
||||
///
|
||||
/// This option is only valid with `NIOTSConnectionBootstrap`.
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public struct NIOTSEstablishmentReportOption: ChannelOption, Equatable {
|
||||
public typealias Value = EventLoopFuture<NWConnection.EstablishmentReport?>
|
||||
|
||||
public init() {}
|
||||
}
|
||||
|
||||
/// `NIOTSDataTransferReportOption` accesses the `NWConnection.DataTransferReport` of the underlying connection.
|
||||
///
|
||||
/// This option is only valid with `NIOTSConnectionBootstrap`.
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
public struct NIOTSDataTransferReportOption: ChannelOption, Equatable {
|
||||
public typealias Value = NWConnection.PendingDataTransferReport
|
||||
|
||||
public init() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -388,7 +388,39 @@ extension NIOTSConnectionChannel: Channel {
|
|||
return self.options.waitForActivity as! Option.Value
|
||||
case is NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption:
|
||||
return self.enablePeerToPeer as! Option.Value
|
||||
case is NIOTSChannelOptions.Types.NIOTSCurrentPathOption:
|
||||
guard let currentPath = self.nwConnection?.currentPath else {
|
||||
throw NIOTSErrors.NoCurrentPath()
|
||||
}
|
||||
return currentPath as! Option.Value
|
||||
case is NIOTSChannelOptions.Types.NIOTSMetadataOption:
|
||||
let optionValue = option as! NIOTSChannelOptions.Types.NIOTSMetadataOption
|
||||
guard let nwConnection = self.nwConnection else {
|
||||
throw NIOTSErrors.NoCurrentConnection()
|
||||
}
|
||||
return nwConnection.metadata(definition: optionValue.definition) as! Option.Value
|
||||
default:
|
||||
if #available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) {
|
||||
switch option {
|
||||
case is NIOTSChannelOptions.Types.NIOTSEstablishmentReportOption:
|
||||
guard let nwConnection = self.nwConnection else {
|
||||
throw NIOTSErrors.NoCurrentConnection()
|
||||
}
|
||||
let promise: EventLoopPromise<NWConnection.EstablishmentReport?> = eventLoop.makePromise()
|
||||
nwConnection.requestEstablishmentReport(queue: connectionQueue) { report in
|
||||
promise.succeed(report)
|
||||
}
|
||||
return promise.futureResult as! Option.Value
|
||||
case is NIOTSChannelOptions.Types.NIOTSDataTransferReportOption:
|
||||
guard let nwConnection = self.nwConnection else {
|
||||
throw NIOTSErrors.NoCurrentConnection()
|
||||
}
|
||||
return nwConnection.startDataTransferReport() as! Option.Value
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
fatalError("option \(type(of: option)).\(option) not supported")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,10 @@ public enum NIOTSErrors {
|
|||
/// that channel has no path available. This can manifest, for example, when asking for remote
|
||||
/// or local addresses.
|
||||
public struct NoCurrentPath: NIOTSError { }
|
||||
|
||||
/// `NoCurrentConnection` is thrown when an attempt is made to request connection details from a channel and
|
||||
/// that channel has no connection available.
|
||||
public struct NoCurrentConnection: NIOTSError { }
|
||||
|
||||
/// `InvalidPort` is thrown when the port passed to a method is not valid.
|
||||
public struct InvalidPort: NIOTSError {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// This source file is part of the SwiftNIO open source project
|
||||
//
|
||||
// Copyright (c) 2020 Apple Inc. and the SwiftNIO project authors
|
||||
// Licensed under Apache License v2.0
|
||||
//
|
||||
// See LICENSE.txt for license information
|
||||
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#if canImport(Network)
|
||||
import XCTest
|
||||
import NIO
|
||||
import NIOConcurrencyHelpers
|
||||
import NIOTransportServices
|
||||
import Network
|
||||
|
||||
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
|
||||
class NIOTSChannelOptionsTests: XCTestCase {
|
||||
private var group: NIOTSEventLoopGroup!
|
||||
|
||||
override func setUp() {
|
||||
self.group = NIOTSEventLoopGroup()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
XCTAssertNoThrow(try self.group.syncShutdownGracefully())
|
||||
}
|
||||
|
||||
func testCurrentPath() throws {
|
||||
let listener = try NIOTSListenerBootstrap(group: self.group)
|
||||
.bind(host: "localhost", port: 0).wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try listener.close().wait())
|
||||
}
|
||||
|
||||
let connection = try NIOTSConnectionBootstrap(group: self.group)
|
||||
.connect(to: listener.localAddress!)
|
||||
.wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try connection.close().wait())
|
||||
}
|
||||
|
||||
let currentPath = try connection.getOption(NIOTSChannelOptions.currentPath).wait()
|
||||
XCTAssertEqual(currentPath.status, NWPath.Status.satisfied)
|
||||
}
|
||||
|
||||
func testMetadata() throws {
|
||||
let listener = try NIOTSListenerBootstrap(group: self.group)
|
||||
.bind(host: "localhost", port: 0).wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try listener.close().wait())
|
||||
}
|
||||
|
||||
let connection = try NIOTSConnectionBootstrap(group: self.group)
|
||||
.connect(to: listener.localAddress!)
|
||||
.wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try connection.close().wait())
|
||||
}
|
||||
|
||||
let metadata = try connection.getOption(NIOTSChannelOptions.metadata(NWProtocolTCP.definition)).wait() as! NWProtocolTCP.Metadata
|
||||
XCTAssertEqual(metadata.availableReceiveBuffer, 0)
|
||||
}
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
func testEstablishmentReport() throws {
|
||||
let listener = try NIOTSListenerBootstrap(group: self.group)
|
||||
.bind(host: "localhost", port: 0).wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try listener.close().wait())
|
||||
}
|
||||
|
||||
let connection = try NIOTSConnectionBootstrap(group: self.group)
|
||||
.connect(to: listener.localAddress!)
|
||||
.wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try connection.close().wait())
|
||||
}
|
||||
|
||||
let reportFuture = try connection.getOption(NIOTSChannelOptions.establishmentReport).wait()
|
||||
let establishmentReport = try reportFuture.wait()
|
||||
|
||||
XCTAssertEqual(establishmentReport!.resolutions.count, 0)
|
||||
}
|
||||
|
||||
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||
func testDataTransferReport() throws {
|
||||
let syncQueue = DispatchQueue(label: "syncQueue")
|
||||
let collectGroup = DispatchGroup()
|
||||
|
||||
let listener = try NIOTSListenerBootstrap(group: self.group)
|
||||
.bind(host: "localhost", port: 0).wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try listener.close().wait())
|
||||
}
|
||||
|
||||
let connection = try NIOTSConnectionBootstrap(group: self.group)
|
||||
.connect(to: listener.localAddress!)
|
||||
.wait()
|
||||
defer {
|
||||
XCTAssertNoThrow(try connection.close().wait())
|
||||
}
|
||||
|
||||
let pendingReport = try connection.getOption(NIOTSChannelOptions.dataTransferReport).wait()
|
||||
|
||||
collectGroup.enter()
|
||||
pendingReport.collect(queue: syncQueue) { report in
|
||||
XCTAssertEqual(report.pathReports.count, 1)
|
||||
collectGroup.leave()
|
||||
}
|
||||
|
||||
collectGroup.wait()
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
Loading…
Reference in New Issue