Allow retrieval of metadata from `NWConnection` (#163)

* Allow retrieval of metadata for a specific protocol from the underlying `NWConnection`

* Address review comments

* Address review comment

* Add availability annotation to tests

* Rename `NIOTSChannelIsNotATransportServicesChannel ` to `NIOTSChannelIsNotANIOTSConnectionChannel `
This commit is contained in:
David Nadoba 2022-10-19 16:59:33 +02:00 committed by GitHub
parent de5ea3d0b4
commit c0d9a144cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 112 additions and 0 deletions

View File

@ -893,4 +893,61 @@ extension NIOTSConnectionChannel {
return SynchronousOptions(channel: self) return SynchronousOptions(channel: self)
} }
} }
public struct NIOTSConnectionNotInitialized: Error, Hashable {
public init() {}
}
public struct NIOTSChannelIsNotANIOTSConnectionChannel: Error, Hashable {
public init() {}
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel {
fileprivate func metadata(definition: NWProtocolDefinition) throws -> NWProtocolMetadata? {
guard let nwConnection = self.nwConnection else {
throw NIOTSConnectionNotInitialized()
}
return nwConnection.metadata(definition: definition)
}
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension Channel {
/// Retrieves the metadata for a specific protocol from the underlying ``NWConnection``
/// - Throws: If `self` isn't a `NIOTS` channel with a `NWConnection` this method will throw
/// ``NIOTSChannelIsNotATransportServicesChannel`` or ``NIOTSConnectionNotInitialized``.
public func getMetadata(definition: NWProtocolDefinition) -> EventLoopFuture<NWProtocolMetadata?> {
guard let channel = self as? NIOTSConnectionChannel else {
return self.eventLoop.makeFailedFuture(NIOTSChannelIsNotANIOTSConnectionChannel())
}
if self.eventLoop.inEventLoop {
return self.eventLoop.makeCompletedFuture {
try channel.metadata(definition: definition)
}
} else {
return self.eventLoop.submit {
try channel.metadata(definition: definition)
}
}
}
/// Retrieves the metadata for a specific protocol from the underlying ``NWConnection``
/// - Precondition: Must be called on the `EventLoop` the `Channel` is running on.
/// - Throws: If `self` isn't a `NIOTS` channel with a `NWConnection` this method will throw
/// ``NIOTSChannelIsNotATransportServicesChannel`` or ``NIOTSConnectionNotInitialized``.
public func getMetadataSync(
definition: NWProtocolDefinition,
file: StaticString = #fileID,
line: UInt = #line
) throws -> NWProtocolMetadata? {
self.eventLoop.preconditionInEventLoop(file: file, line: line)
guard let channel = self as? NIOTSConnectionChannel else {
throw NIOTSChannelIsNotANIOTSConnectionChannel()
}
return try channel.metadata(definition: definition)
}
}
#endif #endif

View File

@ -173,6 +173,7 @@ final class NIOTSBootstrapTests: XCTestCase {
XCTFail("can't connect to server1") XCTFail("can't connect to server1")
return return
} }
XCTAssertNotNil(try client1.getMetadata(definition: NWProtocolTCP.definition).wait() as? NWProtocolTCP.Metadata)
XCTAssertNoThrow(try client1.writeAndFlush(buffer).wait()) XCTAssertNoThrow(try client1.writeAndFlush(buffer).wait())
// The TLS connection won't actually succeed but it takes Network.framework a while to tell us, we don't // The TLS connection won't actually succeed but it takes Network.framework a while to tell us, we don't

View File

@ -0,0 +1,54 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2022 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 Network
import NIOCore
import XCTest
@testable import NIOTransportServices
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
final class NIOTSChannelMetadataTests: XCTestCase {
func testThrowsIfCalledOnWrongChannel() throws {
let eventLoopGroup = NIOTSEventLoopGroup()
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
let listenerBootsrap = NIOTSListenerBootstrap(group: eventLoopGroup)
let listenerChannel = try listenerBootsrap.bind(host: "localhost", port: 0).wait()
defer { XCTAssertNoThrow(try listenerChannel.close().wait()) }
XCTAssertThrowsError(try listenerChannel.getMetadata(definition: NWProtocolTLS.definition).wait()) { error in
XCTAssertTrue(error is NIOTSChannelIsNotANIOTSConnectionChannel, "unexpected error \(error)")
}
try! listenerChannel.eventLoop.submit {
XCTAssertThrowsError(try listenerChannel.getMetadataSync(definition: NWProtocolTLS.definition)) { error in
XCTAssertTrue(error is NIOTSChannelIsNotANIOTSConnectionChannel, "unexpected error \(error)")
}
}.wait()
}
func testThowsIfCalledOnANonInitializedChannel() {
let eventLoopGroup = NIOTSEventLoopGroup()
defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
let channel = NIOTSConnectionChannel(eventLoop: eventLoopGroup.next() as! NIOTSEventLoop, tcpOptions: .init(), tlsOptions: .init())
XCTAssertThrowsError(try channel.getMetadata(definition: NWProtocolTLS.definition).wait()) { error in
XCTAssertTrue(error is NIOTSConnectionNotInitialized, "unexpected error \(error)")
}
try! channel.eventLoop.submit {
XCTAssertThrowsError(try channel.getMetadataSync(definition: NWProtocolTLS.definition)) { error in
XCTAssertTrue(error is NIOTSConnectionNotInitialized, "unexpected error \(error)")
}
}.wait()
}
}
#endif