Migrate CI to use GitHub Actions. (#213)

### Motivation:

To migrate to GitHub actions and centralised infrastructure.

### Modifications:

Changes of note:
* Adopt swift-format using rules from SwiftNIO. Modified so that it
ignores `NoAssignmentInExpressions` in some `XCTAssert` functions.
* Remove scripts and docker files which are no longer needed

### Result:

Feature parity with old CI.
This commit is contained in:
Rick Newton-Rogers 2024-10-28 09:51:24 +00:00 committed by GitHub
parent bbd5e63cf9
commit c73112efc0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
50 changed files with 1340 additions and 1033 deletions

24
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Main
on:
push:
branches: [main]
jobs:
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_8_enabled: false
linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
integration-tests:
name: Integration test
uses: apple/swift-nio/.github/workflows/swift_matrix.yml@main
with:
name: "Integration test"
matrix_linux_command: "apt-get update -yq && apt-get install -yq lsof dnsutils netcat-openbsd net-tools expect curl jq && ./scripts/integration_tests.sh"

30
.github/workflows/pull_request.yml vendored Normal file
View File

@ -0,0 +1,30 @@
name: PR
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
soundness:
name: Soundness
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main
with:
license_header_check_project_name: "SwiftNIO"
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
cxx-interop:
name: Cxx interop
uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main
swift-6-language-mode:
name: Swift 6 Language Mode
uses: apple/swift-nio/.github/workflows/swift_6_language_mode.yml@main
if: false # Disabled for now.

View File

@ -0,0 +1,18 @@
name: PR label
on:
pull_request:
types: [labeled, unlabeled, opened, reopened, synchronize]
jobs:
semver-label-check:
name: Semantic Version label check
runs-on: ubuntu-latest
timeout-minutes: 1
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
persist-credentials: false
- name: Check for Semantic Version label
uses: apple/swift-nio/.github/actions/pull_request_semver_label_checker@main

24
.github/workflows/scheduled.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Scheduled
on:
schedule:
- cron: "0 8,20 * * *"
jobs:
unit-tests:
name: Unit tests
uses: apple/swift-nio/.github/workflows/unit_tests.yml@main
with:
linux_5_8_enabled: false
linux_5_9_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_5_10_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error"
linux_6_0_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
integration-tests:
name: Integration test
uses: apple/swift-nio/.github/workflows/swift_matrix.yml@main
with:
name: "Integration test"
matrix_linux_command: "apt-get update -yq && apt-get install -yq lsof dnsutils netcat-openbsd net-tools expect curl jq && ./scripts/integration_tests.sh"

49
.licenseignore Normal file
View File

@ -0,0 +1,49 @@
.gitignore
**/.gitignore
.licenseignore
.gitattributes
.git-blame-ignore-revs
.mailfilter
.mailmap
.spi.yml
.swift-format
.editorconfig
.github/*
*.md
*.txt
*.yml
*.yaml
*.json
Package.swift
**/Package.swift
Package@-*.swift
**/Package@-*.swift
Package.resolved
**/Package.resolved
Makefile
*.modulemap
**/*.modulemap
**/*.docc/*
*.xcprivacy
**/*.xcprivacy
*.symlink
**/*.symlink
Dockerfile
**/Dockerfile
Snippets/*
Sources/CNIOAtomics/src/cpp_magic.h
Sources/CNIOLLHTTP/LICENSE-MIT
Sources/CNIOLLHTTP/c_nio_api.c
Sources/CNIOLLHTTP/c_nio_http.c
Sources/CNIOLLHTTP/c_nio_llhttp.c
Sources/CNIOLLHTTP/include/c_nio_llhttp.h
Sources/CNIOSHA1/c_nio_sha1.c
Sources/CNIOSHA1/include/CNIOSHA1.h
dev/alloc-limits-from-test-output
dev/boxed-existentials.d
dev/git.commit.template
dev/lldb-smoker
dev/make-single-file-spm
dev/malloc-aggregation.d
dev/update-alloc-limits-to-last-completed-ci-build
scripts/nio-diagnose

68
.swift-format Normal file
View File

@ -0,0 +1,68 @@
{
"version" : 1,
"indentation" : {
"spaces" : 4
},
"tabWidth" : 4,
"fileScopedDeclarationPrivacy" : {
"accessLevel" : "private"
},
"spacesAroundRangeFormationOperators" : false,
"indentConditionalCompilationBlocks" : false,
"indentSwitchCaseLabels" : false,
"lineBreakAroundMultilineExpressionChainComponents" : false,
"lineBreakBeforeControlFlowKeywords" : false,
"lineBreakBeforeEachArgument" : true,
"lineBreakBeforeEachGenericRequirement" : true,
"lineLength" : 120,
"maximumBlankLines" : 1,
"respectsExistingLineBreaks" : true,
"prioritizeKeepingFunctionOutputTogether" : true,
"noAssignmentInExpressions" : {
"allowedFunctions" : [
"XCTAssertNoThrow",
"XCTAssertThrowsError"
]
},
"rules" : {
"AllPublicDeclarationsHaveDocumentation" : false,
"AlwaysUseLiteralForEmptyCollectionInit" : false,
"AlwaysUseLowerCamelCase" : false,
"AmbiguousTrailingClosureOverload" : true,
"BeginDocumentationCommentWithOneLineSummary" : false,
"DoNotUseSemicolons" : true,
"DontRepeatTypeInStaticProperties" : true,
"FileScopedDeclarationPrivacy" : true,
"FullyIndirectEnum" : true,
"GroupNumericLiterals" : true,
"IdentifiersMustBeASCII" : true,
"NeverForceUnwrap" : false,
"NeverUseForceTry" : false,
"NeverUseImplicitlyUnwrappedOptionals" : false,
"NoAccessLevelOnExtensionDeclaration" : true,
"NoAssignmentInExpressions" : true,
"NoBlockComments" : true,
"NoCasesWithOnlyFallthrough" : true,
"NoEmptyTrailingClosureParentheses" : true,
"NoLabelsInCasePatterns" : true,
"NoLeadingUnderscores" : false,
"NoParensAroundConditions" : true,
"NoVoidReturnOnFunctionSignature" : true,
"OmitExplicitReturns" : true,
"OneCasePerLine" : true,
"OneVariableDeclarationPerLine" : true,
"OnlyOneTrailingClosureArgument" : true,
"OrderedImports" : true,
"ReplaceForEachWithForLoop" : true,
"ReturnVoidInsteadOfEmptyTuple" : true,
"UseEarlyExits" : false,
"UseExplicitNilCheckInConditions" : false,
"UseLetInEveryBoundCaseVariable" : false,
"UseShorthandTypeNames" : true,
"UseSingleLinePropertyGetter" : false,
"UseSynthesizedInitializer" : false,
"UseTripleSlashForDocumentationComments" : true,
"UseWhereClausesInForLoops" : false,
"ValidateDocumentationComments" : false
}
}

View File

@ -59,6 +59,11 @@ We require that your commit messages match our template. The easiest way to do t
git config commit.template dev/git.commit.template
### Run CI checks locally
You can run the Github Actions workflows locally using [act](https://github.com/nektos/act). For detailed steps on how to do this please see [https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally](https://github.com/swiftlang/github-workflows?tab=readme-ov-file#running-workflows-locally).
## How to contribute your work
Please open a pull request at https://github.com/apple/swift-nio-transport-services. Make sure the CI passes, and then wait for code review.

View File

@ -18,7 +18,7 @@ import PackageDescription
let package = Package(
name: "swift-nio-transport-services",
products: [
.library(name: "NIOTransportServices", targets: ["NIOTransportServices"]),
.library(name: "NIOTransportServices", targets: ["NIOTransportServices"])
],
dependencies: [
.package(url: "https://github.com/apple/swift-nio.git", from: "2.62.0"),
@ -33,21 +33,24 @@ let package = Package(
.product(name: "NIOFoundationCompat", package: "swift-nio"),
.product(name: "NIOTLS", package: "swift-nio"),
.product(name: "Atomics", package: "swift-atomics"),
]),
]
),
.executableTarget(
name: "NIOTSHTTPClient",
dependencies: [
"NIOTransportServices",
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOHTTP1", package: "swift-nio"),
]),
]
),
.executableTarget(
name: "NIOTSHTTPServer",
dependencies: [
"NIOTransportServices",
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOHTTP1", package: "swift-nio"),
]),
]
),
.testTarget(
name: "NIOTransportServicesTests",
dependencies: [
@ -55,6 +58,7 @@ let package = Package(
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOEmbedded", package: "swift-nio"),
.product(name: "Atomics", package: "swift-atomics"),
]),
]
),
]
)

View File

@ -41,9 +41,10 @@ if #available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *) {
let group = NIOTSEventLoopGroup()
let channel = try! NIOTSListenerBootstrap(group: group)
.childChannelInitializer { channel in
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true, withErrorHandling: true).flatMap {
channel.pipeline.addHandler(HTTP1ServerHandler())
}
channel.pipeline.configureHTTPServerPipeline(withPipeliningAssistance: true, withErrorHandling: true)
.flatMap {
channel.pipeline.addHandler(HTTP1ServerHandler())
}
}.bind(host: "127.0.0.1", port: 8888).wait()
print("Server listening on \(channel.localAddress!)")

View File

@ -23,8 +23,10 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
private let childChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?
private let childChannelOptions: ChannelOptions.Storage
init(childChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?,
childChannelOptions: ChannelOptions.Storage) {
init(
childChannelInitializer: ((Channel) -> EventLoopFuture<Void>)?,
childChannelOptions: ChannelOptions.Storage
) {
self.childChannelInitializer = childChannelInitializer
self.childChannelOptions = childChannelOptions
}
@ -35,10 +37,9 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
let ctxEventLoop = context.eventLoop
let childInitializer = self.childChannelInitializer ?? { _ in childLoop.makeSucceededFuture(()) }
@inline(__always)
func setupChildChannel() -> EventLoopFuture<Void> {
return self.childChannelOptions.applyAllChannelOptions(to: newChannel).flatMap { () -> EventLoopFuture<Void> in
self.childChannelOptions.applyAllChannelOptions(to: newChannel).flatMap { () -> EventLoopFuture<Void> in
childLoop.assertInEventLoop()
return childInitializer(newChannel)
}
@ -66,9 +67,11 @@ internal class AcceptHandler<ChildChannel: Channel>: ChannelInboundHandler {
if childLoop === ctxEventLoop {
fireThroughPipeline(setupChildChannel())
} else {
fireThroughPipeline(childLoop.flatSubmit {
return setupChildChannel()
}.hop(to: ctxEventLoop))
fireThroughPipeline(
childLoop.flatSubmit {
setupChildChannel()
}.hop(to: ctxEventLoop)
)
}
}
}

View File

@ -69,7 +69,7 @@ public final class NIOTSDatagramBootstrap {
/// - parameters:
/// - group: The `NIOTSEventLoopGroup` to use.
public convenience init(group: NIOTSEventLoopGroup) {
self.init(group: group as EventLoopGroup)
self.init(group: group as EventLoopGroup)
}
/// Initialize the connected `NIOTSDatagramConnectionChannel` with `initializer`. The most common task in initializer is to add
@ -151,7 +151,7 @@ public final class NIOTSDatagramBootstrap {
/// - address: The address to connect to.
/// - returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
public func connect(to address: SocketAddress) -> EventLoopFuture<Channel> {
return self.connect0 { channel, promise in
self.connect0 { channel, promise in
channel.connect(to: address, promise: promise)
}
}
@ -172,7 +172,7 @@ public final class NIOTSDatagramBootstrap {
/// Specify the `endpoint` to connect to for the UDP `Channel` that will be established.
public func connect(endpoint: NWEndpoint) -> EventLoopFuture<Channel> {
return self.connect0 { channel, promise in
self.connect0 { channel, promise in
channel.triggerUserOutboundEvent(
NIOTSNetworkEvents.ConnectToNWEndpoint(endpoint: endpoint),
promise: promise
@ -181,15 +181,17 @@ public final class NIOTSDatagramBootstrap {
}
private func connect0(_ binder: @escaping (Channel, EventLoopPromise<Void>) -> Void) -> EventLoopFuture<Channel> {
let conn: Channel = NIOTSDatagramChannel(eventLoop: self.group.next() as! NIOTSEventLoop,
qos: self.qos,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions)
let conn: Channel = NIOTSDatagramChannel(
eventLoop: self.group.next() as! NIOTSEventLoop,
qos: self.qos,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions
)
let initializer = self.channelInitializer ?? { _ in conn.eventLoop.makeSucceededFuture(()) }
let channelOptions = self.channelOptions
return conn.eventLoop.submit {
return channelOptions.applyAllChannelOptions(to: conn).flatMap {
channelOptions.applyAllChannelOptions(to: conn).flatMap {
initializer(conn)
}.flatMap {
conn.eventLoop.assertInEventLoop()

View File

@ -29,15 +29,15 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
enum UDPSubstate: NWConnectionSubstate {
case open, closed
init() {
self = .open
}
static func closeInput(state: inout ChannelState<NIOTSDatagramChannel.UDPSubstate>) throws {
throw NIOTSErrors.InvalidChannelStateTransition()
}
static func closeOutput(state: inout ChannelState<NIOTSDatagramChannel.UDPSubstate>) throws {
throw NIOTSErrors.InvalidChannelStateTransition()
}
@ -45,13 +45,13 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
/// The kinds of channel activation this channel supports
internal let supportedActivationType: ActivationType = .connect
/// The `ByteBufferAllocator` for this `Channel`.
public let allocator = ByteBufferAllocator()
/// An `EventLoopFuture` that will complete when this channel is finally closed.
public var closeFuture: EventLoopFuture<Void> {
return self.closePromise.futureResult
self.closePromise.futureResult
}
/// The parent `Channel` for this one, if any.
@ -60,7 +60,9 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
/// The `EventLoop` this `Channel` belongs to.
internal let tsEventLoop: NIOTSEventLoop
private(set) var _pipeline: ChannelPipeline! = nil // this is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`. Do not change as this needs to accessed from arbitrary threads.
// This is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`.
// *Do not change* as this needs to accessed from arbitrary threads.
private(set) var _pipeline: ChannelPipeline! = nil
internal let closePromise: EventLoopPromise<Void>
@ -121,8 +123,8 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
internal var addressCache: AddressCache {
get {
return self._addressCacheLock.withLock {
return self._addressCache
self._addressCacheLock.withLock {
self._addressCache
}
}
set {
@ -137,11 +139,11 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
internal var allowLocalEndpointReuse = false
internal var multipathServiceType: NWParameters.MultipathServiceType = .disabled
var parameters: NWParameters {
NWParameters(dtls: self.tlsOptions, udp: self.udpOptions)
}
var _inboundStreamOpen: Bool {
switch self.state {
case .active(.open):
@ -151,11 +153,12 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
}
}
func setChannelSpecificOption0<Option>(option: Option, value: Option.Value) throws where Option : NIOCore.ChannelOption {
func setChannelSpecificOption0<Option>(option: Option, value: Option.Value) throws
where Option: NIOCore.ChannelOption {
fatalError("option \(type(of: option)).\(option) not supported")
}
func getChannelSpecificOption0<Option>(option: Option) throws -> Option.Value where Option : ChannelOption {
func getChannelSpecificOption0<Option>(option: Option) throws -> Option.Value where Option: ChannelOption {
if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) {
switch option {
case is NIOTSChannelOptions.Types.NIOTSConnectionOption:
@ -172,13 +175,15 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
/// Create a `NIOTSDatagramConnectionChannel` on a given `NIOTSEventLoop`.
///
/// Note that `NIOTSDatagramConnectionChannel` objects cannot be created on arbitrary loops types.
internal init(eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?) {
internal init(
eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?
) {
self.tsEventLoop = eventLoop
self.closePromise = eventLoop.makePromise()
self.parent = parent
@ -193,21 +198,25 @@ internal final class NIOTSDatagramChannel: StateManagedNWConnectionChannel {
}
/// Create a `NIOTSDatagramConnectionChannel` with an already-established `NWConnection`.
internal convenience init(wrapping connection: NWConnection,
on eventLoop: NIOTSEventLoop,
parent: Channel,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?) {
self.init(eventLoop: eventLoop,
parent: parent,
qos: qos,
minimumIncompleteReceiveLength: minimumIncompleteReceiveLength,
maximumReceiveLength: maximumReceiveLength,
udpOptions: udpOptions,
tlsOptions: tlsOptions)
internal convenience init(
wrapping connection: NWConnection,
on eventLoop: NIOTSEventLoop,
parent: Channel,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?
) {
self.init(
eventLoop: eventLoop,
parent: parent,
qos: qos,
minimumIncompleteReceiveLength: minimumIncompleteReceiveLength,
maximumReceiveLength: maximumReceiveLength,
udpOptions: udpOptions,
tlsOptions: tlsOptions
)
self.connection = connection
}
}
@ -226,12 +235,12 @@ extension NIOTSDatagramChannel {
}
public func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
return try self.channel.getOption0(option: option)
try self.channel.getOption0(option: option)
}
}
public var syncOptions: NIOSynchronousChannelOptions? {
return SynchronousOptions(channel: self)
SynchronousOptions(channel: self)
}
}
#endif

View File

@ -107,9 +107,11 @@ public final class NIOTSDatagramListenerBootstrap {
/// - childGroup: The `EventLoopGroup` to run the accepted `NIOTSConnectionChannel`s on.
public convenience init(group: EventLoopGroup, childGroup: EventLoopGroup) {
guard NIOTSBootstraps.isCompatible(group: group) && NIOTSBootstraps.isCompatible(group: childGroup) else {
preconditionFailure("NIOTSListenerBootstrap is only compatible with NIOTSEventLoopGroup and " +
"NIOTSEventLoop. You tried constructing one with group: \(group) and " +
"childGroup: \(childGroup) at least one of which is incompatible.")
preconditionFailure(
"NIOTSListenerBootstrap is only compatible with NIOTSEventLoopGroup and "
+ "NIOTSEventLoop. You tried constructing one with group: \(group) and "
+ "childGroup: \(childGroup) at least one of which is incompatible."
)
}
self.init(validatingGroup: group, childGroup: childGroup)!
@ -210,7 +212,6 @@ public final class NIOTSDatagramListenerBootstrap {
return self
}
/// Specifies a QoS to use for the child connections created from the server channel,
/// instead of the default QoS for the event loop.
///
@ -261,7 +262,7 @@ public final class NIOTSDatagramListenerBootstrap {
/// - parameters:
/// - address: The `SocketAddress` to bind on.
public func bind(to address: SocketAddress) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
channel.bind(to: address, promise: promise)
}
}
@ -271,7 +272,7 @@ public final class NIOTSDatagramListenerBootstrap {
/// - parameters:
/// - unixDomainSocketPath: The _Unix domain socket_ path to bind to. `unixDomainSocketPath` must not exist, it will be created by the system.
public func bind(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
do {
let address = try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
channel.bind(to: address, promise: promise)
@ -286,7 +287,7 @@ public final class NIOTSDatagramListenerBootstrap {
/// - parameters:
/// - endpoint: The `NWEndpoint` to bind this channel to.
public func bind(endpoint: NWEndpoint) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
channel.triggerUserOutboundEvent(NIOTSNetworkEvents.BindToNWEndpoint(endpoint: endpoint), promise: promise)
}
}
@ -295,13 +296,17 @@ public final class NIOTSDatagramListenerBootstrap {
///
/// - parameters:
/// - listener: The NWListener to wrap.
public func withNWListener(_ listener:NWListener) -> EventLoopFuture<Channel>{
return self.bind0(existingNWListener: listener,shouldRegister: false) { channel, promise in
public func withNWListener(_ listener: NWListener) -> EventLoopFuture<Channel> {
self.bind0(existingNWListener: listener, shouldRegister: false) { channel, promise in
channel.registerAlreadyConfigured0(promise: promise)
}
}
private func bind0(existingNWListener: NWListener? = nil, shouldRegister: Bool, _ binder: @escaping (NIOTSDatagramListenerChannel, EventLoopPromise<Void>) -> Void) -> EventLoopFuture<Channel> {
private func bind0(
existingNWListener: NWListener? = nil,
shouldRegister: Bool,
_ binder: @escaping (NIOTSDatagramListenerChannel, EventLoopPromise<Void>) -> Void
) -> EventLoopFuture<Channel> {
let eventLoop = self.group.next() as! NIOTSEventLoop
let serverChannelInit = self.serverChannelInit ?? { _ in eventLoop.makeSucceededFuture(()) }
let childChannelInit = self.childChannelInit
@ -310,35 +315,43 @@ public final class NIOTSDatagramListenerBootstrap {
let serverChannel: NIOTSDatagramListenerChannel
if let newListener = existingNWListener {
serverChannel = NIOTSDatagramListenerChannel(wrapping: newListener,
on: eventLoop,
qos: self.serverQoS,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions)
serverChannel = NIOTSDatagramListenerChannel(
wrapping: newListener,
on: eventLoop,
qos: self.serverQoS,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions
)
} else {
serverChannel = NIOTSDatagramListenerChannel(eventLoop: eventLoop,
qos: self.serverQoS,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions)
serverChannel = NIOTSDatagramListenerChannel(
eventLoop: eventLoop,
qos: self.serverQoS,
udpOptions: self.udpOptions,
tlsOptions: self.tlsOptions,
childLoopGroup: self.childGroup,
childChannelQoS: self.childQoS,
childUDPOptions: self.udpOptions,
childTLSOptions: self.tlsOptions
)
}
return eventLoop.submit {
return serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelInit(serverChannel)
}.flatMap {
eventLoop.assertInEventLoop()
return serverChannel.pipeline.addHandler(AcceptHandler<NIOTSDatagramChannel>(childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions))
return serverChannel.pipeline.addHandler(
AcceptHandler<NIOTSDatagramChannel>(
childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions
)
)
}.flatMap {
if shouldRegister{
if shouldRegister {
return serverChannel.register()
} else {
return eventLoop.makeSucceededVoidFuture()

View File

@ -33,13 +33,16 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
return options
}
set {
assert({
if case .udp = protocolOptions {
return true
} else {
return false
}
}(), "The protocol options of this channel were not configured as UDP")
assert(
{
if case .udp = protocolOptions {
return true
} else {
return false
}
}(),
"The protocol options of this channel were not configured as UDP"
)
protocolOptions = .udp(newValue)
}
@ -55,13 +58,16 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
return options
}
set {
assert({
if case .udp = childProtocolOptions {
return true
} else {
return false
}
}(), "The protocol options of child channelss were not configured as UDP")
assert(
{
if case .udp = childProtocolOptions {
return true
} else {
return false
}
}(),
"The protocol options of child channelss were not configured as UDP"
)
childProtocolOptions = .udp(newValue)
}
@ -70,14 +76,16 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
/// Create a `NIOTSDatagramListenerChannel` on a given `NIOTSEventLoop`.
///
/// Note that `NIOTSDatagramListenerChannel` objects cannot be created on arbitrary loops types.
internal convenience init(eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childUDPOptions: NWProtocolUDP.Options,
childTLSOptions: NWProtocolTLS.Options?) {
internal convenience init(
eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childUDPOptions: NWProtocolUDP.Options,
childTLSOptions: NWProtocolTLS.Options?
) {
self.init(
eventLoop: eventLoop,
protocolOptions: .udp(udpOptions),
@ -90,15 +98,17 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
}
/// Create a `NIOTSDatagramListenerChannel` with an already-established `NWListener`.
internal convenience init(wrapping listener: NWListener,
on eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childUDPOptions: NWProtocolUDP.Options,
childTLSOptions: NWProtocolTLS.Options?) {
internal convenience init(
wrapping listener: NWListener,
on eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
udpOptions: NWProtocolUDP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childUDPOptions: NWProtocolUDP.Options,
childTLSOptions: NWProtocolTLS.Options?
) {
self.init(
wrapping: listener,
eventLoop: eventLoop,
@ -122,7 +132,8 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
on: self.childLoopGroup.next() as! NIOTSEventLoop,
parent: self,
udpOptions: self.childUDPOptions,
tlsOptions: self.childTLSOptions)
tlsOptions: self.childTLSOptions
)
self.pipeline.fireChannelRead(NIOAny(newChannel))
self.pipeline.fireChannelReadComplete()
@ -140,12 +151,12 @@ internal final class NIOTSDatagramListenerChannel: StateManagedListenerChannel<N
}
public func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
return try self.channel.getOption0(option: option)
try self.channel.getOption0(option: option)
}
}
public override var syncOptions: NIOSynchronousChannelOptions? {
return SynchronousOptions(channel: self)
SynchronousOptions(channel: self)
}
}

View File

@ -14,7 +14,6 @@
import NIOCore
/// A `ChannelHandler` that checks for outbound writes of zero length, which are then dropped. This is
/// due to a bug in `Network Framework`, where zero byte TCP writes lead to stalled connections.
/// Write promises are confirmed in the correct order.
@ -31,16 +30,16 @@ public final class NIOFilterEmptyWritesHandler: ChannelDuplexHandler {
case closedFromRemote
case error
}
private var state: ChannelState = .notActiveYet
private var prefixEmptyWritePromise: Optional<EventLoopPromise<Void>>
private var lastWritePromise: Optional<EventLoopPromise<Void>>
public init() {
self.prefixEmptyWritePromise = nil
self.lastWritePromise = nil
}
public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
switch self.state {
case .open:
@ -49,13 +48,11 @@ public final class NIOFilterEmptyWritesHandler: ChannelDuplexHandler {
self.lastWritePromise = promise ?? context.eventLoop.makePromise()
context.write(data, promise: self.lastWritePromise)
} else {
/*
Empty writes need to be handled individually depending on:
A) Empty write occurring before any non-empty write needs a
separate promise to cascade from (prefix)
B) Non-empty writes carry a promise, that subsequent empty
writes can cascade from
*/
// Empty writes need to be handled individually depending on:
// A) Empty write occurring before any non-empty write needs a
// separate promise to cascade from (prefix)
// B) Non-empty writes carry a promise, that subsequent empty
// writes can cascade from
switch (self.prefixEmptyWritePromise, self.lastWritePromise, promise) {
case (_, _, .none): ()
case (.none, .none, .some(let promise)):
@ -73,7 +70,7 @@ public final class NIOFilterEmptyWritesHandler: ChannelDuplexHandler {
preconditionFailure()
}
}
public func flush(context: ChannelHandlerContext) {
self.lastWritePromise = nil
if let prefixEmptyWritePromise = self.prefixEmptyWritePromise {
@ -102,7 +99,7 @@ extension NIOFilterEmptyWritesHandler {
preconditionFailure()
}
}
public func channelInactive(context: ChannelHandlerContext) {
let save = self.prefixEmptyWritePromise
self.prefixEmptyWritePromise = nil
@ -127,19 +124,19 @@ extension NIOFilterEmptyWritesHandler {
switch (mode, self.state) {
case (.all, .open),
(.output, .open),
(.output, .open),
// We allow closure in .notActiveYet because it is possible to close before the channelActive fires.
(.all, .notActiveYet),
(.output, .notActiveYet):
// We allow closure in .notActiveYet because it is possible to close before the channelActive fires.
(.all, .notActiveYet),
(.output, .notActiveYet):
self.state = .closedFromLocal
save?.fail(ChannelError.outputClosed)
case (.all, .closedFromLocal),
(.output, .closedFromLocal),
(.all, .closedFromRemote),
(.output, .closedFromRemote),
(.all, .error),
(.output, .error):
(.output, .closedFromLocal),
(.all, .closedFromRemote),
(.output, .closedFromRemote),
(.all, .error),
(.output, .error):
assert(save == nil)
case (.input, _):
save?.fail(ChannelError.operationUnsupported)
@ -162,7 +159,7 @@ extension NIOFilterEmptyWritesHandler {
case .notActiveYet:
preconditionFailure()
}
context.fireErrorCaught(error)
}

View File

@ -19,7 +19,7 @@ import NIOCore
internal enum NIOTSBootstraps {
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal static func isCompatible(group: EventLoopGroup) -> Bool {
return group is NIOTSEventLoop || group is NIOTSEventLoopGroup
group is NIOTSEventLoop || group is NIOTSEventLoopGroup
}
}

View File

@ -23,7 +23,7 @@ public struct NIOTSChannelOptions {
/// See: ``Types/NIOTSEnablePeerToPeerOption``.
public static let enablePeerToPeer = NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption()
/// See: ``Types/NIOTSAllowLocalEndpointReuse``.
public static let allowLocalEndpointReuse = NIOTSChannelOptions.Types.NIOTSAllowLocalEndpointReuse()
@ -31,7 +31,8 @@ public struct NIOTSChannelOptions {
public static let currentPath = NIOTSChannelOptions.Types.NIOTSCurrentPathOption()
/// See: ``Types/NIOTSMetadataOption``
public static let metadata = { (definition: NWProtocolDefinition) -> NIOTSChannelOptions.Types.NIOTSMetadataOption in
public static let metadata = {
(definition: NWProtocolDefinition) -> NIOTSChannelOptions.Types.NIOTSMetadataOption in
.init(definition: definition)
}
@ -55,13 +56,13 @@ public struct NIOTSChannelOptions {
public static let listener = NIOTSChannelOptions.Types.NIOTSListenerOption()
/// See: ``Types/NIOTSMinimumIncompleteReceiveLengthOption``.
public static let minimumIncompleteReceiveLength = NIOTSChannelOptions.Types.NIOTSMinimumIncompleteReceiveLengthOption()
public static let minimumIncompleteReceiveLength = NIOTSChannelOptions.Types
.NIOTSMinimumIncompleteReceiveLengthOption()
/// See: ``Types/NIOTSMaximumReceiveLengthOption``.
public static let maximumReceiveLength = NIOTSChannelOptions.Types.NIOTSMaximumReceiveLengthOption()
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSChannelOptions {
/// A namespace for ``NIOTSChannelOptions`` datastructures.
@ -92,7 +93,7 @@ extension NIOTSChannelOptions {
public init() {}
}
/// ``NIOTSAllowLocalEndpointReuse`` controls whether the `Channel` can reuse a TCP address recently used.
/// Setting this to true is the equivalent of setting at least one of `REUSEADDR` and `REUSEPORT` to
/// `true`. By default this option is set to `false`.
@ -101,7 +102,7 @@ extension NIOTSChannelOptions {
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public struct NIOTSAllowLocalEndpointReuse: ChannelOption, Equatable {
public typealias Value = Bool
public init() {}
}
@ -111,19 +112,19 @@ extension NIOTSChannelOptions {
@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 6.0, *)
public struct NIOTSMetadataOption: ChannelOption, Equatable {
public typealias Value = NWProtocolMetadata
let definition: NWProtocolDefinition
public init(definition: NWProtocolDefinition) {
self.definition = definition
}
@ -135,7 +136,7 @@ extension NIOTSChannelOptions {
@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() {}
}
@ -145,7 +146,7 @@ extension NIOTSChannelOptions {
@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() {}
}
@ -213,11 +214,9 @@ extension NIOTSChannelOptions {
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public typealias NIOTSWaitForActivityOption = NIOTSChannelOptions.Types.NIOTSWaitForActivityOption
/// See: ``NIOTSChannelOptions/Types/NIOTSEnablePeerToPeerOption``
@available(*, deprecated, renamed: "NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption")
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public typealias NIOTSEnablePeerToPeerOption = NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption
#endif

View File

@ -59,7 +59,7 @@ public final class NIOTSConnectionBootstrap {
private var qos: DispatchQoS?
private var tcpOptions: NWProtocolTCP.Options = .init()
private var tlsOptions: NWProtocolTLS.Options?
private var protocolHandlers: Optional<() -> [ChannelHandler]> = nil
private var protocolHandlers: (() -> [ChannelHandler])? = nil
/// Create a `NIOTSConnectionBootstrap` on the `EventLoopGroup` `group`.
///
@ -72,8 +72,10 @@ public final class NIOTSConnectionBootstrap {
/// - group: The `EventLoopGroup` to use.
public convenience init(group: EventLoopGroup) {
guard NIOTSBootstraps.isCompatible(group: group) else {
preconditionFailure("NIOTSConnectionBootstrap is only compatible with NIOTSEventLoopGroup and " +
"NIOTSEventLoop. You tried constructing one with \(group) which is incompatible.")
preconditionFailure(
"NIOTSConnectionBootstrap is only compatible with NIOTSEventLoopGroup and "
+ "NIOTSEventLoop. You tried constructing one with \(group) which is incompatible."
)
}
self.init(validatingGroup: group)!
@ -84,7 +86,7 @@ public final class NIOTSConnectionBootstrap {
/// - parameters:
/// - group: The ``NIOTSEventLoopGroup`` to use.
public convenience init(group: NIOTSEventLoopGroup) {
self.init(group: group as EventLoopGroup)
self.init(group: group as EventLoopGroup)
}
/// Create a `NIOTSConnectionBootstrap` on the ``NIOTSEventLoopGroup`` `group`, validating
@ -184,7 +186,7 @@ public final class NIOTSConnectionBootstrap {
/// - address: The address to connect to.
/// - returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
public func connect(to address: SocketAddress) -> EventLoopFuture<Channel> {
return self.connect(shouldRegister: true) { channel, promise in
self.connect(shouldRegister: true) { channel, promise in
channel.connect(to: address, promise: promise)
}
}
@ -205,9 +207,11 @@ public final class NIOTSConnectionBootstrap {
/// Specify the `endpoint` to connect to for the TCP `Channel` that will be established.
public func connect(endpoint: NWEndpoint) -> EventLoopFuture<Channel> {
return self.connect(shouldRegister: true) { channel, promise in
channel.triggerUserOutboundEvent(NIOTSNetworkEvents.ConnectToNWEndpoint(endpoint: endpoint),
promise: promise)
self.connect(shouldRegister: true) { channel, promise in
channel.triggerUserOutboundEvent(
NIOTSNetworkEvents.ConnectToNWEndpoint(endpoint: endpoint),
promise: promise
)
}
}
@ -217,29 +221,37 @@ public final class NIOTSConnectionBootstrap {
/// - connection: The NWConnection to wrap.
/// - returns: An `EventLoopFuture<Channel>` to deliver the `Channel` when connected.
public func withExistingNWConnection(_ connection: NWConnection) -> EventLoopFuture<Channel> {
return self.connect(existingNWConnection: connection, shouldRegister: false) { channel, promise in
self.connect(existingNWConnection: connection, shouldRegister: false) { channel, promise in
channel.registerAlreadyConfigured0(promise: promise)
}
}
private func connect(existingNWConnection: NWConnection? = nil, shouldRegister: Bool, _ connectAction: @escaping (NIOTSConnectionChannel, EventLoopPromise<Void>) -> Void) -> EventLoopFuture<Channel> {
private func connect(
existingNWConnection: NWConnection? = nil,
shouldRegister: Bool,
_ connectAction: @escaping (NIOTSConnectionChannel, EventLoopPromise<Void>) -> Void
) -> EventLoopFuture<Channel> {
let conn: NIOTSConnectionChannel
if let newConnection = existingNWConnection {
conn = NIOTSConnectionChannel(wrapping: newConnection,
on: self.group.next() as! NIOTSEventLoop,
tcpOptions: self.tcpOptions,
tlsOptions: self.tlsOptions)
conn = NIOTSConnectionChannel(
wrapping: newConnection,
on: self.group.next() as! NIOTSEventLoop,
tcpOptions: self.tcpOptions,
tlsOptions: self.tlsOptions
)
} else {
conn = NIOTSConnectionChannel(eventLoop: self.group.next() as! NIOTSEventLoop,
qos: self.qos,
tcpOptions: self.tcpOptions,
tlsOptions: self.tlsOptions)
conn = NIOTSConnectionChannel(
eventLoop: self.group.next() as! NIOTSEventLoop,
qos: self.qos,
tcpOptions: self.tcpOptions,
tlsOptions: self.tlsOptions
)
}
let initializer = self.channelInitializer
let channelOptions = self.channelOptions
return conn.eventLoop.flatSubmit {
return channelOptions.applyAllChannelOptions(to: conn).flatMap {
channelOptions.applyAllChannelOptions(to: conn).flatMap {
initializer(conn)
}.flatMap {
conn.eventLoop.assertInEventLoop()
@ -435,7 +447,7 @@ extension NIOTSConnectionBootstrap {
let channelOptions = self.channelOptions
return connectionChannel.eventLoop.flatSubmit {
return channelOptions.applyAllChannelOptions(to: connectionChannel).flatMap {
channelOptions.applyAllChannelOptions(to: connectionChannel).flatMap {
channelInitializer(connectionChannel)
}.flatMap { result -> EventLoopFuture<ChannelInitializerResult> in
let connectPromise: EventLoopPromise<Void> = connectionChannel.eventLoop.makePromise()

View File

@ -39,8 +39,8 @@ struct TransportServicesChannelOptions {
internal struct AddressCache {
// deliberately lets because they must always be updated together (so forcing `init` is useful).
let local: Optional<SocketAddress>
let remote: Optional<SocketAddress>
let local: SocketAddress?
let remote: SocketAddress?
init(local: SocketAddress?, remote: SocketAddress?) {
self.local = local
@ -48,7 +48,6 @@ internal struct AddressCache {
}
}
/// A structure that manages backpressure signaling on this channel.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal struct BackpressureManager {
@ -79,7 +78,7 @@ internal struct BackpressureManager {
/// - returns: Whether the state changed.
mutating func writabilityChanges(whenQueueingBytes newBytes: Int) -> Bool {
self.outstandingBytes += newBytes
if self.outstandingBytes > self.waterMarks.high && self.writable.load(ordering: .relaxed) {
if self.outstandingBytes > self.waterMarks.high && self.writable.load(ordering: .relaxed) {
self.writable.store(false, ordering: .relaxed)
return true
}
@ -109,7 +108,9 @@ internal struct BackpressureManager {
/// - parameters:
/// - waterMarks: The new waterMarks to use.
/// - returns: Whether the state changed.
mutating func writabilityChanges(whenUpdatingWaterMarks waterMarks: ChannelOptions.Types.WriteBufferWaterMark) -> Bool {
mutating func writabilityChanges(
whenUpdatingWaterMarks waterMarks: ChannelOptions.Types.WriteBufferWaterMark
) -> Bool {
let writable = self.writable.load(ordering: .relaxed)
self.waterMarks = waterMarks
@ -125,7 +126,6 @@ internal struct BackpressureManager {
}
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
/// The `ByteBufferAllocator` for this `Channel`.
@ -133,7 +133,7 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
/// An `EventLoopFuture` that will complete when this channel is finally closed.
public var closeFuture: EventLoopFuture<Void> {
return self.closePromise.futureResult
self.closePromise.futureResult
}
/// The parent `Channel` for this one, if any.
@ -142,7 +142,9 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
/// The `EventLoop` this `Channel` belongs to.
internal let tsEventLoop: NIOTSEventLoop
private(set) var _pipeline: ChannelPipeline! = nil // this is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`. Do not change as this needs to accessed from arbitrary threads.
// This is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`.
// *Do not change* as this needs to accessed from arbitrary threads.
private(set) var _pipeline: ChannelPipeline! = nil
internal let closePromise: EventLoopPromise<Void>
@ -201,7 +203,7 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
/// The value of SO_REUSEPORT.
internal var reusePort = false
/// The value of the allowLocalEndpointReuse option.
internal var allowLocalEndpointReuse = false
@ -216,8 +218,8 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
internal var addressCache: AddressCache {
get {
return self._addressCacheLock.withLock {
return self._addressCache
self._addressCacheLock.withLock {
self._addressCache
}
}
set {
@ -233,13 +235,15 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
/// Create a `NIOTSConnectionChannel` on a given `NIOTSEventLoop`.
///
/// Note that `NIOTSConnectionChannel` objects cannot be created on arbitrary loops types.
internal init(eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?) {
internal init(
eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?
) {
self.tsEventLoop = eventLoop
self.closePromise = eventLoop.makePromise()
self.parent = parent
@ -254,30 +258,33 @@ internal final class NIOTSConnectionChannel: StateManagedNWConnectionChannel {
}
/// Create a `NIOTSConnectionChannel` with an already-established `NWConnection`.
internal convenience init(wrapping connection: NWConnection,
on eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?) {
self.init(eventLoop: eventLoop,
parent: parent,
qos: qos,
minimumIncompleteReceiveLength: minimumIncompleteReceiveLength,
maximumReceiveLength: maximumReceiveLength,
tcpOptions: tcpOptions,
tlsOptions: tlsOptions)
internal convenience init(
wrapping connection: NWConnection,
on eventLoop: NIOTSEventLoop,
parent: Channel? = nil,
qos: DispatchQoS? = nil,
minimumIncompleteReceiveLength: Int = 1,
maximumReceiveLength: Int = 8192,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?
) {
self.init(
eventLoop: eventLoop,
parent: parent,
qos: qos,
minimumIncompleteReceiveLength: minimumIncompleteReceiveLength,
maximumReceiveLength: maximumReceiveLength,
tcpOptions: tcpOptions,
tlsOptions: tlsOptions
)
self.connection = connection
}
}
// MARK:- NIOTSConnectionChannel implementation of Channel
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel: Channel {
func getChannelSpecificOption0<Option>(option: Option) throws -> Option.Value where Option : ChannelOption {
func getChannelSpecificOption0<Option>(option: Option) throws -> Option.Value where Option: ChannelOption {
if #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) {
switch option {
case is NIOTSChannelOptions.Types.NIOTSConnectionOption:
@ -312,7 +319,6 @@ extension NIOTSConnectionChannel: Channel {
}
}
// MARK:- NIOTSConnectionChannel implementation of StateManagedChannel.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel: StateManagedChannel {
@ -370,7 +376,6 @@ extension NIOTSConnectionChannel: StateManagedChannel {
}
}
// MARK:- Implementations of the callbacks passed to NWConnection.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel {
@ -380,7 +385,12 @@ extension NIOTSConnectionChannel {
/// and call channelReadComplete. This may be nil, in which case we expect either `isComplete` to be `true` or `error`
/// to be non-nil. `isComplete` indicates half-closure on the read side of a connection. `error` is set if the receive
/// did not complete due to an error, though there may still be some data.
private func dataReceivedHandler(content: Data?, context: NWConnection.ContentContext?, isComplete: Bool, error: NWError?) {
private func dataReceivedHandler(
content: Data?,
context: NWConnection.ContentContext?,
isComplete: Bool,
error: NWError?
) {
precondition(self.outstandingRead)
self.outstandingRead = false
@ -423,7 +433,7 @@ extension NIOTSConnectionChannel {
self.pipeline.fireUserInboundEventTriggered(NIOTSNetworkEvents.BetterPathUnavailable())
}
}
/// Called by the underlying `NWConnection` when a path becomes viable or non-viable
///
/// Notifies the channel pipeline of the new viability.
@ -439,7 +449,6 @@ extension NIOTSConnectionChannel {
}
}
// MARK:- Implementations of state management for the channel.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel {
@ -481,7 +490,6 @@ extension NIOTSConnectionChannel {
}
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension NIOTSConnectionChannel {
internal struct SynchronousOptions: NIOSynchronousChannelOptions {
@ -496,16 +504,15 @@ extension NIOTSConnectionChannel {
}
public func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
return try self.channel.getOption0(option: option)
try self.channel.getOption0(option: option)
}
}
public var syncOptions: NIOSynchronousChannelOptions? {
return SynchronousOptions(channel: self)
SynchronousOptions(channel: self)
}
}
public struct NIOTSConnectionNotInitialized: Error, Hashable {
public init() {}
}
@ -543,7 +550,7 @@ extension Channel {
}
}
}
/// 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

View File

@ -18,37 +18,37 @@ import NIOCore
///
/// Users are strongly encouraged not to conform their own types to this protocol.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public protocol NIOTSError: Error, Equatable { }
public protocol NIOTSError: Error, Equatable {}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public enum NIOTSErrors {
/// ``InvalidChannelStateTransition`` is thrown when a channel has been asked to do something
/// that is incompatible with its current channel state: e.g. attempting to register an
/// already registered channel.
public struct InvalidChannelStateTransition: NIOTSError { }
public struct InvalidChannelStateTransition: NIOTSError {}
/// ``NotPreConfigured`` is thrown when a channel has had `registerAlreadyConfigured`
/// called on it, but has not had the appropriate underlying network object provided.
public struct NotPreConfigured: NIOTSError { }
public struct NotPreConfigured: NIOTSError {}
/// ``UnsupportedSocketOption`` is thrown when an attempt is made to configure a socket option that
/// is not supported by Network.framework.
public struct UnsupportedSocketOption: NIOTSError {
public let optionValue: ChannelOptions.Types.SocketOption
public static func ==(lhs: UnsupportedSocketOption, rhs: UnsupportedSocketOption) -> Bool {
return lhs.optionValue == rhs.optionValue
public static func == (lhs: UnsupportedSocketOption, rhs: UnsupportedSocketOption) -> Bool {
lhs.optionValue == rhs.optionValue
}
}
/// ``NoCurrentPath`` is thrown when an attempt is made to request path details from a channel and
/// that channel has no path available. This can manifest, for example, when asking for remote
/// or local addresses.
public struct NoCurrentPath: NIOTSError { }
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 { }
public struct NoCurrentConnection: NIOTSError {}
/// ``InvalidPort`` is thrown when the port passed to a method is not valid.
public struct InvalidPort: NIOTSError {
@ -58,7 +58,7 @@ public enum NIOTSErrors {
/// ``UnableToResolveEndpoint`` is thrown when an attempt is made to resolve a local endpoint, but
/// insufficient information is available to create it.
public struct UnableToResolveEndpoint: NIOTSError { }
public struct UnableToResolveEndpoint: NIOTSError {}
/// ``BindTimeout`` is thrown when a timeout set for a `NWListenerBootstrap.bind` call has been exceeded
/// without successfully binding the address.
@ -72,7 +72,7 @@ public enum NIOTSErrors {
/// ``InvalidHostname`` is thrown when attempting to connect to an invalid host.
public struct InvalidHostname: NIOTSError {
public init() { }
public init() {}
}
}
#endif

View File

@ -28,14 +28,13 @@ import NIOConcurrencyHelpers
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public protocol QoSEventLoop: EventLoop {
/// Submit a given task to be executed by the `EventLoop` at a given `qos`.
func execute(qos: DispatchQoS, _ task: @escaping () -> Void) -> Void
func execute(qos: DispatchQoS, _ task: @escaping () -> Void)
/// Schedule a `task` that is executed by this `NIOTSEventLoop` after the given amount of time at the
/// given `qos`.
func scheduleTask<T>(in time: TimeAmount, qos: DispatchQoS, _ task: @escaping () throws -> T) -> Scheduled<T>
}
/// The lifecycle state of a given event loop.
///
/// Event loops have the ability to be shut down, and not restarted. When a loop is active it will accept
@ -44,7 +43,7 @@ public protocol QoSEventLoop: EventLoop {
/// will accept neither new registrations nor new scheduled work items, but it will continue to process
/// the queue until it has drained.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
fileprivate enum LifecycleState {
private enum LifecycleState {
case active
case closing
case closed
@ -72,7 +71,7 @@ internal class NIOTSEventLoop: QoSEventLoop {
/// Whether this event loop is accepting new channels.
private var open: Bool {
return self.state == .active
self.state == .active
}
/// Returns whether the currently executing code is on the event loop.
@ -94,7 +93,7 @@ internal class NIOTSEventLoop: QoSEventLoop {
/// callers ever use synchronous dispatch (which is impossible to enforce), or to hope that a future version of
/// libdispatch will provide a solution.
public var inEventLoop: Bool {
return DispatchQueue.getSpecific(key: self.inQueueKey) == self.loopID
DispatchQueue.getSpecific(key: self.inQueueKey) == self.loopID
}
public convenience init(qos: DispatchQoS) {
@ -102,7 +101,11 @@ internal class NIOTSEventLoop: QoSEventLoop {
}
internal init(qos: DispatchQoS, canBeShutDownIndividually: Bool) {
self.loop = DispatchQueue(label: "nio.transportservices.eventloop.loop", qos: qos, autoreleaseFrequency: .workItem)
self.loop = DispatchQueue(
label: "nio.transportservices.eventloop.loop",
qos: qos,
autoreleaseFrequency: .workItem
)
self.taskQueue = DispatchQueue(label: "nio.transportservices.eventloop.taskqueue", target: self.loop)
self.loopID = UUID()
self.inQueueKey = DispatchSpecificKey()
@ -121,10 +124,14 @@ internal class NIOTSEventLoop: QoSEventLoop {
}
public func scheduleTask<T>(deadline: NIODeadline, _ task: @escaping () throws -> T) -> Scheduled<T> {
return self.scheduleTask(deadline: deadline, qos: self.defaultQoS, task)
self.scheduleTask(deadline: deadline, qos: self.defaultQoS, task)
}
public func scheduleTask<T>(deadline: NIODeadline, qos: DispatchQoS, _ task: @escaping () throws -> T) -> Scheduled<T> {
public func scheduleTask<T>(
deadline: NIODeadline,
qos: DispatchQoS,
_ task: @escaping () throws -> T
) -> Scheduled<T> {
let p: EventLoopPromise<T> = self.makePromise()
// Dispatch support for cancellation exists at the work-item level, so we explicitly create one here.
@ -150,17 +157,21 @@ internal class NIOTSEventLoop: QoSEventLoop {
timerSource.cancel()
}
return Scheduled(promise: p, cancellationTask: {
timerSource.cancel()
})
return Scheduled(
promise: p,
cancellationTask: {
timerSource.cancel()
}
)
}
public func scheduleTask<T>(in time: TimeAmount, _ task: @escaping () throws -> T) -> Scheduled<T> {
return self.scheduleTask(in: time, qos: self.defaultQoS, task)
self.scheduleTask(in: time, qos: self.defaultQoS, task)
}
public func scheduleTask<T>(in time: TimeAmount, qos: DispatchQoS, _ task: @escaping () throws -> T) -> Scheduled<T> {
return self.scheduleTask(deadline: NIODeadline.now() + time, qos: qos, task)
public func scheduleTask<T>(in time: TimeAmount, qos: DispatchQoS, _ task: @escaping () throws -> T) -> Scheduled<T>
{
self.scheduleTask(deadline: NIODeadline.now() + time, qos: qos, task)
}
public func shutdownGracefully(queue: DispatchQueue, _ callback: @escaping (Error?) -> Void) {

View File

@ -20,7 +20,6 @@ import Dispatch
import Network
import Atomics
/// An `EventLoopGroup` containing `EventLoop`s specifically designed for use with
/// Network.framework's post-sockets networking API.
///
@ -79,7 +78,7 @@ public final class NIOTSEventLoopGroup: EventLoopGroup {
}
public func next() -> EventLoop {
return self.eventLoops[abs(index.loadThenWrappingIncrement(ordering: .relaxed) % self.eventLoops.count)]
self.eventLoops[abs(index.loadThenWrappingIncrement(ordering: .relaxed) % self.eventLoops.count)]
}
/// Shuts down all of the event loops, rendering them unable to perform further work.
@ -109,7 +108,7 @@ public final class NIOTSEventLoopGroup: EventLoopGroup {
}
public func makeIterator() -> EventLoopIterator {
return EventLoopIterator(self.eventLoops)
EventLoopIterator(self.eventLoops)
}
}
@ -139,7 +138,7 @@ public struct NIOTSClientTLSProvider: NIOClientTLSProvider {
/// Enable TLS on the bootstrap. This is not a function you will typically call as a user, it is called by
/// `NIOClientTCPBootstrap`.
public func enableTLS(_ bootstrap: NIOTSConnectionBootstrap) -> NIOTSConnectionBootstrap {
return bootstrap.tlsOptions(self.tlsOptions)
bootstrap.tlsOptions(self.tlsOptions)
}
}

View File

@ -107,9 +107,11 @@ public final class NIOTSListenerBootstrap {
/// - childGroup: The `EventLoopGroup` to run the accepted `NIOTSConnectionChannel`s on.
public convenience init(group: EventLoopGroup, childGroup: EventLoopGroup) {
guard NIOTSBootstraps.isCompatible(group: group) && NIOTSBootstraps.isCompatible(group: childGroup) else {
preconditionFailure("NIOTSListenerBootstrap is only compatible with NIOTSEventLoopGroup and " +
"NIOTSEventLoop. You tried constructing one with group: \(group) and " +
"childGroup: \(childGroup) at least one of which is incompatible.")
preconditionFailure(
"NIOTSListenerBootstrap is only compatible with NIOTSEventLoopGroup and "
+ "NIOTSEventLoop. You tried constructing one with group: \(group) and "
+ "childGroup: \(childGroup) at least one of which is incompatible."
)
}
self.init(validatingGroup: group, childGroup: childGroup)!
@ -213,7 +215,6 @@ public final class NIOTSListenerBootstrap {
return self
}
/// Specifies a QoS to use for the child connections created from the server channel,
/// instead of the default QoS for the event loop.
///
@ -274,7 +275,7 @@ public final class NIOTSListenerBootstrap {
/// - parameters:
/// - address: The `SocketAddress` to bind on.
public func bind(to address: SocketAddress) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
channel.bind(to: address, promise: promise)
}
}
@ -284,7 +285,7 @@ public final class NIOTSListenerBootstrap {
/// - parameters:
/// - unixDomainSocketPath: The _Unix domain socket_ path to bind to. `unixDomainSocketPath` must not exist, it will be created by the system.
public func bind(unixDomainSocketPath: String) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
do {
let address = try SocketAddress(unixDomainSocketPath: unixDomainSocketPath)
channel.bind(to: address, promise: promise)
@ -299,7 +300,7 @@ public final class NIOTSListenerBootstrap {
/// - parameters:
/// - endpoint: The `NWEndpoint` to bind this channel to.
public func bind(endpoint: NWEndpoint) -> EventLoopFuture<Channel> {
return self.bind0(shouldRegister: true) { (channel, promise) in
self.bind0(shouldRegister: true) { (channel, promise) in
channel.triggerUserOutboundEvent(NIOTSNetworkEvents.BindToNWEndpoint(endpoint: endpoint), promise: promise)
}
}
@ -308,13 +309,17 @@ public final class NIOTSListenerBootstrap {
///
/// - parameters:
/// - listener: The NWListener to wrap.
public func withNWListener(_ listener:NWListener) -> EventLoopFuture<Channel>{
return self.bind0(existingNWListener: listener,shouldRegister: false) { channel, promise in
public func withNWListener(_ listener: NWListener) -> EventLoopFuture<Channel> {
self.bind0(existingNWListener: listener, shouldRegister: false) { channel, promise in
channel.registerAlreadyConfigured0(promise: promise)
}
}
private func bind0(existingNWListener: NWListener? = nil, shouldRegister: Bool, _ binder: @escaping (NIOTSListenerChannel, EventLoopPromise<Void>) -> Void) -> EventLoopFuture<Channel> {
private func bind0(
existingNWListener: NWListener? = nil,
shouldRegister: Bool,
_ binder: @escaping (NIOTSListenerChannel, EventLoopPromise<Void>) -> Void
) -> EventLoopFuture<Channel> {
let eventLoop = self.group.next() as! NIOTSEventLoop
let serverChannelInit = self.serverChannelInit ?? { _ in eventLoop.makeSucceededFuture(()) }
let childChannelInit = self.childChannelInit
@ -348,15 +353,19 @@ public final class NIOTSListenerBootstrap {
}
return eventLoop.submit {
return serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelOptions.applyAllChannelOptions(to: serverChannel).flatMap {
serverChannelInit(serverChannel)
}.flatMap {
eventLoop.assertInEventLoop()
return serverChannel.pipeline.addHandler(AcceptHandler<NIOTSConnectionChannel>(childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions))
return serverChannel.pipeline.addHandler(
AcceptHandler<NIOTSConnectionChannel>(
childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions
)
)
}.flatMap {
if shouldRegister{
return serverChannel.register()
if shouldRegister {
return serverChannel.register()
} else {
return eventLoop.makeSucceededVoidFuture()
}
@ -450,7 +459,7 @@ extension NIOTSListenerBootstrap {
serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
) async throws -> NIOAsyncChannel<Output, Never> {
return try await self.bind0(
try await self.bind0(
serverBackPressureStrategy: serverBackPressureStrategy,
childChannelInitializer: childChannelInitializer,
registration: { (serverChannel, promise) in
@ -480,14 +489,17 @@ extension NIOTSListenerBootstrap {
serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
) async throws -> NIOAsyncChannel<Output, Never> {
return try await self.bind0(
try await self.bind0(
serverBackPressureStrategy: serverBackPressureStrategy,
childChannelInitializer: childChannelInitializer,
registration: { (serverChannel, promise) in
serverChannel.register().whenComplete { result in
switch result {
case .success:
serverChannel.triggerUserOutboundEvent(NIOTSNetworkEvents.BindToNWEndpoint(endpoint: endpoint), promise: promise)
serverChannel.triggerUserOutboundEvent(
NIOTSNetworkEvents.BindToNWEndpoint(endpoint: endpoint),
promise: promise
)
case .failure(let error):
promise.fail(error)
}
@ -510,7 +522,7 @@ extension NIOTSListenerBootstrap {
serverBackPressureStrategy: NIOAsyncSequenceProducerBackPressureStrategies.HighLowWatermark? = nil,
childChannelInitializer: @escaping @Sendable (Channel) -> EventLoopFuture<Output>
) async throws -> NIOAsyncChannel<Output, Never> {
return try await self.bind0(
try await self.bind0(
existingNWListener: listener,
serverBackPressureStrategy: serverBackPressureStrategy,
childChannelInitializer: childChannelInitializer,
@ -565,7 +577,10 @@ extension NIOTSListenerBootstrap {
}.flatMap { (_) -> EventLoopFuture<NIOAsyncChannel<ChannelInitializerResult, Never>> in
do {
try serverChannel.pipeline.syncOperations.addHandler(
AcceptHandler<NIOTSConnectionChannel>(childChannelInitializer: childChannelInit, childChannelOptions: childChannelOptions),
AcceptHandler<NIOTSConnectionChannel>(
childChannelInitializer: childChannelInit,
childChannelOptions: childChannelOptions
),
name: "AcceptHandler"
)
let asyncChannel = try NIOAsyncChannel<ChannelInitializerResult, Never>
@ -598,7 +613,7 @@ extension NIOTSListenerBootstrap {
return bindPromise.futureResult
.map { (_) -> NIOAsyncChannel<ChannelInitializerResult, Never> in asyncChannel
}
}
} catch {
return eventLoop.makeFailedFuture(error)
}

View File

@ -33,13 +33,16 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
return options
}
set {
assert({
if case .tcp = protocolOptions {
return true
} else {
return false
}
}(), "The protocol options of this channel were not configured as TCP")
assert(
{
if case .tcp = protocolOptions {
return true
} else {
return false
}
}(),
"The protocol options of this channel were not configured as TCP"
)
protocolOptions = .tcp(newValue)
}
@ -55,13 +58,16 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
return options
}
set {
assert({
if case .tcp = childProtocolOptions {
return true
} else {
return false
}
}(), "The protocol options of child channels were not configured as TCP")
assert(
{
if case .tcp = childProtocolOptions {
return true
} else {
return false
}
}(),
"The protocol options of child channels were not configured as TCP"
)
childProtocolOptions = .tcp(newValue)
}
@ -70,14 +76,16 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
/// Create a `NIOTSListenerChannel` on a given `NIOTSEventLoop`.
///
/// Note that `NIOTSListenerChannel` objects cannot be created on arbitrary loops types.
internal convenience init(eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childTCPOptions: NWProtocolTCP.Options,
childTLSOptions: NWProtocolTLS.Options?) {
internal convenience init(
eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childTCPOptions: NWProtocolTCP.Options,
childTLSOptions: NWProtocolTLS.Options?
) {
self.init(
eventLoop: eventLoop,
protocolOptions: .tcp(tcpOptions),
@ -90,15 +98,17 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
}
/// Create a `NIOTSListenerChannel` with an already-established `NWListener`.
internal convenience init(wrapping listener: NWListener,
on eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childTCPOptions: NWProtocolTCP.Options,
childTLSOptions: NWProtocolTLS.Options?) {
internal convenience init(
wrapping listener: NWListener,
on eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
tcpOptions: NWProtocolTCP.Options,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childTCPOptions: NWProtocolTCP.Options,
childTLSOptions: NWProtocolTLS.Options?
) {
self.init(
wrapping: listener,
eventLoop: eventLoop,
@ -124,7 +134,8 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
parent: self,
qos: self.childChannelQoS,
tcpOptions: self.childTCPOptions,
tlsOptions: self.childTLSOptions)
tlsOptions: self.childTLSOptions
)
self.pipeline.fireChannelRead(NIOAny(newChannel))
self.pipeline.fireChannelReadComplete()
@ -142,12 +153,12 @@ internal final class NIOTSListenerChannel: StateManagedListenerChannel<NIOTSConn
}
public func getOption<Option: ChannelOption>(_ option: Option) throws -> Option.Value {
return try self.channel.getOption0(option: option)
try self.channel.getOption0(option: option)
}
}
public override var syncOptions: NIOSynchronousChannelOptions? {
return SynchronousOptions(channel: self)
SynchronousOptions(channel: self)
}
}

View File

@ -20,7 +20,7 @@ import NIOCore
///
/// Users are strongly encouraged not to conform their own types to this protocol.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public protocol NIOTSNetworkEvent: Equatable, _NIOPreconcurrencySendable { }
public protocol NIOTSNetworkEvent: Equatable, _NIOPreconcurrencySendable {}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public enum NIOTSNetworkEvents {
@ -33,7 +33,7 @@ public enum NIOTSNetworkEvents {
/// transfer your work to that connection before closing this one.
public struct BetterPathAvailable: NIOTSNetworkEvent {
/// Create a new ``NIOTSNetworkEvents/BetterPathAvailable`` event.
public init(){ }
public init() {}
}
/// ``BetterPathUnavailable`` is fired when the OS has informed NIO that no better path to the
@ -41,16 +41,16 @@ public enum NIOTSNetworkEvents {
/// currently available.
public struct BetterPathUnavailable: NIOTSNetworkEvent {
/// Create a new ``NIOTSNetworkEvents/BetterPathUnavailable`` event.
public init(){ }
public init() {}
}
/// ``ViabilityUpdate`` is triggered when the OS informs NIO that communication
/// with the remote endpoint is possible, indicating that the connection is viable.
public struct ViabilityUpdate: NIOTSNetworkEvent {
/// The current viability for the connection
public var isViable: Bool
/// Create a new ``NIOTSNetworkEvents/ViabilityUpdate`` event.
public init(isViable: Bool) {
self.isViable = isViable
@ -74,7 +74,7 @@ public enum NIOTSNetworkEvents {
public struct ConnectToNWEndpoint: NIOTSNetworkEvent {
/// The endpoint to which we want to connect.
public let endpoint: NWEndpoint
/// Create a new ``NIOTSNetworkEvents/ConnectToNWEndpoint`` event.
public init(endpoint: NWEndpoint) {
self.endpoint = endpoint
@ -86,7 +86,7 @@ public enum NIOTSNetworkEvents {
public struct BindToNWEndpoint: NIOTSNetworkEvent {
/// The endpoint to which we want to bind.
public let endpoint: NWEndpoint
/// Create a new ``NIOTSNetworkEvents/BindToNWEndpoint`` event.
public init(endpoint: NWEndpoint) {
self.endpoint = endpoint
@ -104,7 +104,7 @@ public enum NIOTSNetworkEvents {
/// Note that these reasons are _not fatal_: applications are strongly advised not to treat them
/// as fatal, and instead to use them as information to inform UI decisions.
public var transientError: NWError
/// Create a new ``NIOTSNetworkEvents/WaitingForConnectivity`` event.
public init(transientError: NWError) {
self.transientError = transientError

View File

@ -34,17 +34,18 @@ extension NIOTSEventLoopGroup {
/// `NIOSingletons.singletonsEnabledSuggestion` to `false` which will lead to a forced crash
/// if any code attempts to use the global singletons.
public static var singleton: NIOTSEventLoopGroup {
return NIOSingletons.transportServicesEventLoopGroup
NIOSingletons.transportServicesEventLoopGroup
}
}
// swift-format-ignore: DontRepeatTypeInStaticProperties
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension EventLoopGroup where Self == NIOTSEventLoopGroup {
/// A globally shared, lazily initialized, singleton ``NIOTSEventLoopGroup``.
///
/// This provides the same object as ``NIOTSEventLoopGroup/singleton``.
public static var singletonNIOTSEventLoopGroup: Self {
return NIOTSEventLoopGroup.singleton
NIOTSEventLoopGroup.singleton
}
}
@ -67,22 +68,26 @@ extension NIOSingletons {
/// if any code attempts to use the global singletons.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
public static var transportServicesEventLoopGroup: NIOTSEventLoopGroup {
return globalTransportServicesEventLoopGroup
globalTransportServicesEventLoopGroup
}
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
private let globalTransportServicesEventLoopGroup: NIOTSEventLoopGroup = {
guard NIOSingletons.singletonsEnabledSuggestion else {
fatalError("""
Cannot create global singleton NIOThreadPool because the global singletons have been \
disabled by setting `NIOSingletons.singletonsEnabledSuggestion = false`
""")
fatalError(
"""
Cannot create global singleton NIOThreadPool because the global singletons have been \
disabled by setting `NIOSingletons.singletonsEnabledSuggestion = false`
"""
)
}
let group = NIOTSEventLoopGroup._makePerpetualGroup(loopCount: NIOSingletons.groupLoopCountSuggestion,
defaultQoS: .default)
_ = Unmanaged.passUnretained(group).retain() // Never gonna give you up, never gonna let you down.
let group = NIOTSEventLoopGroup._makePerpetualGroup(
loopCount: NIOSingletons.groupLoopCountSuggestion,
defaultQoS: .default
)
_ = Unmanaged.passUnretained(group).retain() // Never gonna give you up, never gonna let you down.
return group
}()
#endif

View File

@ -110,7 +110,7 @@ extension SocketAddress {
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal extension SocketAddress {
extension SocketAddress {
/// Change the port on this `SocketAddress` to a new value.
mutating func newPort(_ port: UInt16) {
switch self {

View File

@ -21,7 +21,6 @@ import Dispatch
import Network
import Atomics
/// An object that conforms to this protocol represents the substate of a channel in the
/// active state. This can be used to provide more fine-grained tracking of states
/// within the active state of a channel. Example uses include for tracking TCP half-closure
@ -32,7 +31,6 @@ internal protocol ActiveChannelSubstate {
init()
}
/// A state machine enum that tracks the state of the connection channel.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal enum ChannelState<ActiveSubstate: ActiveChannelSubstate> {
@ -82,7 +80,6 @@ internal enum ChannelState<ActiveSubstate: ActiveChannelSubstate> {
}
}
/// The kinds of activation that a channel may support.
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
internal enum ActivationType {
@ -90,7 +87,6 @@ internal enum ActivationType {
case bind
}
/// A protocol for `Channel` implementations with a simple Network.framework
/// state management layer.
///
@ -111,28 +107,28 @@ internal protocol StateManagedChannel: Channel, ChannelCore {
var supportedActivationType: ActivationType { get }
func beginActivating0(to: NWEndpoint, promise: EventLoopPromise<Void>?) -> Void
func beginActivating0(to: NWEndpoint, promise: EventLoopPromise<Void>?)
func becomeActive0(promise: EventLoopPromise<Void>?) -> Void
func becomeActive0(promise: EventLoopPromise<Void>?)
func alreadyConfigured0(promise: EventLoopPromise<Void>?) -> Void
func alreadyConfigured0(promise: EventLoopPromise<Void>?)
func doClose0(error: Error) -> Void
func doClose0(error: Error)
func doHalfClose0(error: Error, promise: EventLoopPromise<Void>?) -> Void
func doHalfClose0(error: Error, promise: EventLoopPromise<Void>?)
func readIfNeeded0() -> Void
func readIfNeeded0()
}
@available(OSX 10.14, iOS 12.0, tvOS 12.0, watchOS 6.0, *)
extension StateManagedChannel {
public var eventLoop: EventLoop {
return self.tsEventLoop
self.tsEventLoop
}
/// Whether this channel is currently active.
public var isActive: Bool {
return self.isActive0.load(ordering: .relaxed)
self.isActive0.load(ordering: .relaxed)
}
/// Whether this channel is currently closed. This is not necessary for the public

View File

@ -45,7 +45,7 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
/// An `EventLoopFuture` that will complete when this channel is finally closed.
public var closeFuture: EventLoopFuture<Void> {
return self.closePromise.futureResult
self.closePromise.futureResult
}
/// The parent `Channel` for this one, if any.
@ -54,7 +54,9 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
/// The `EventLoop` this `Channel` belongs to.
internal let tsEventLoop: NIOTSEventLoop
internal var _pipeline: ChannelPipeline! = nil // this is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`. Do not change as this needs to accessed from arbitrary threads.
// This is really a constant (set in .init) but needs `self` to be constructed and therefore a `var`.
// *Do not change* as this needs to accessed from arbitrary threads.
internal var _pipeline: ChannelPipeline! = nil
internal let closePromise: EventLoopPromise<Void>
@ -123,14 +125,16 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
/// The protocol level options to use for child channels.
var childProtocolOptions: ProtocolOptions
internal init(eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
protocolOptions: ProtocolOptions,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childProtocolOptions: ProtocolOptions,
childTLSOptions: NWProtocolTLS.Options?) {
internal init(
eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
protocolOptions: ProtocolOptions,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childProtocolOptions: ProtocolOptions,
childTLSOptions: NWProtocolTLS.Options?
) {
self.tsEventLoop = eventLoop
self.closePromise = eventLoop.makePromise()
self.connectionQueue = eventLoop.channelQueue(label: "nio.transportservices.listenerchannel", qos: qos)
@ -145,15 +149,17 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
self._pipeline = ChannelPipeline(channel: self)
}
internal convenience init(wrapping listener: NWListener,
eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
protocolOptions: ProtocolOptions,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childProtocolOptions: ProtocolOptions,
childTLSOptions: NWProtocolTLS.Options?) {
internal convenience init(
wrapping listener: NWListener,
eventLoop: NIOTSEventLoop,
qos: DispatchQoS? = nil,
protocolOptions: ProtocolOptions,
tlsOptions: NWProtocolTLS.Options?,
childLoopGroup: EventLoopGroup,
childChannelQoS: DispatchQoS?,
childProtocolOptions: ProtocolOptions,
childTLSOptions: NWProtocolTLS.Options?
) {
self.init(
eventLoop: eventLoop,
qos: qos,
@ -174,7 +180,7 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
// This needs to be declared here to make sure the child classes can override
// the behaviour.
internal var syncOptions: NIOSynchronousChannelOptions? {
return nil
nil
}
}
@ -183,27 +189,27 @@ internal class StateManagedListenerChannel<ChildChannel: StateManagedChannel>: S
extension StateManagedListenerChannel {
/// The `ChannelPipeline` for this `Channel`.
public var pipeline: ChannelPipeline {
return self._pipeline
self._pipeline
}
/// The local address for this channel.
public var localAddress: SocketAddress? {
return self.addressCache.local
self.addressCache.local
}
/// The remote address for this channel.
public var remoteAddress: SocketAddress? {
return self.addressCache.remote
self.addressCache.remote
}
/// Whether this channel is currently writable.
public var isWritable: Bool {
// TODO: implement
return true
true
}
public var _channelCore: ChannelCore {
return self
self
}
public func setOption<Option: ChannelOption>(_ option: Option, value: Option.Value) -> EventLoopFuture<Void> {

View File

@ -45,7 +45,7 @@ internal protocol StateManagedNWConnectionChannel: StateManagedChannel where Act
var parameters: NWParameters { get }
var nwOptions: NWOptions { get }
var connection: NWConnection? { get set }
var minimumIncompleteReceiveLength: Int { get set }
@ -69,7 +69,7 @@ internal protocol StateManagedNWConnectionChannel: StateManagedChannel where Act
var reusePort: Bool { get set }
var enablePeerToPeer: Bool { get set }
var _inboundStreamOpen: Bool { get }
var _pipeline: ChannelPipeline! { get }
@ -94,26 +94,26 @@ extension StateManagedNWConnectionChannel {
}
public var _channelCore: ChannelCore {
return self
self
}
/// The local address for this channel.
public var localAddress: SocketAddress? {
return self._addressCacheLock.withLock {
return self._addressCache.local
self._addressCacheLock.withLock {
self._addressCache.local
}
}
/// The remote address for this channel.
public var remoteAddress: SocketAddress? {
return self._addressCacheLock.withLock {
return self._addressCache.remote
self._addressCacheLock.withLock {
self._addressCache.remote
}
}
/// Whether this channel is currently writable.
public var isWritable: Bool {
return self._backpressureManager.writable.load(ordering: .relaxed)
self._backpressureManager.writable.load(ordering: .relaxed)
}
internal func beginActivating0(to target: NWEndpoint, promise: EventLoopPromise<Void>?) {
@ -166,7 +166,6 @@ extension StateManagedNWConnectionChannel {
let data = self.unwrapData(data, as: ByteBuffer.self)
self.pendingWrites.append((data, promise))
/// This may cause our writability state to change.
if self._backpressureManager.writabilityChanges(whenQueueingBytes: data.readableBytes) {
self.pipeline.fireChannelWritabilityChanged()
@ -183,7 +182,7 @@ extension StateManagedNWConnectionChannel {
}
func completionCallback(promise: EventLoopPromise<Void>?, sentBytes: Int) -> ((NWError?) -> Void) {
return { error in
{ error in
if let error = error {
promise?.fail(error)
} else {
@ -201,11 +200,16 @@ extension StateManagedNWConnectionChannel {
let write = self.pendingWrites.removeFirst()
let buffer = write.data
let content = buffer.getData(at: buffer.readerIndex, length: buffer.readableBytes)
conn.send(content: content, completion: .contentProcessed(completionCallback(promise: write.promise, sentBytes: buffer.readableBytes)))
conn.send(
content: content,
completion: .contentProcessed(
completionCallback(promise: write.promise, sentBytes: buffer.readableBytes)
)
)
}
}
}
public func localAddress0() throws -> SocketAddress {
guard let localEndpoint = self.connection?.currentPath?.localEndpoint else {
throw NIOTSErrors.NoCurrentPath()
@ -310,7 +314,7 @@ extension StateManagedNWConnectionChannel {
}
func completionCallback(for promise: EventLoopPromise<Void>?) -> ((NWError?) -> Void) {
return { error in
{ error in
if let error = error {
promise?.fail(error)
} else {
@ -323,7 +327,11 @@ extension StateManagedNWConnectionChannel {
assert(self.connectPromise == nil)
// Step 1 is to tell the network stack we're done.
conn.send(content: nil, contentContext: .finalMessage, completion: .contentProcessed(completionCallback(for: promise)))
conn.send(
content: nil,
contentContext: .finalMessage,
completion: .contentProcessed(completionCallback(for: promise))
)
// Step 2 is to fail all outstanding writes.
self.dropOutstandingWrites(error: error)
@ -354,7 +362,7 @@ extension StateManagedNWConnectionChannel {
self.pipeline.read()
}
}
/// Called by the underlying `NWConnection` when its internal state has changed.
private func stateUpdateHandler(newState: NWConnection.State) {
switch newState {
@ -365,7 +373,9 @@ extension StateManagedNWConnectionChannel {
// This means the connection cannot currently be completed. We should notify the pipeline
// here, or support this with a channel option or something, but for now for the sake of
// demos we will just allow ourselves into this stage.tage.
self.pipeline.fireUserInboundEventTriggered(NIOTSNetworkEvents.WaitingForConnectivity(transientError: err))
self.pipeline.fireUserInboundEventTriggered(
NIOTSNetworkEvents.WaitingForConnectivity(transientError: err)
)
break
}
@ -400,7 +410,12 @@ extension StateManagedNWConnectionChannel {
/// and call channelReadComplete. This may be nil, in which case we expect either `isComplete` to be `true` or `error`
/// to be non-nil. `isComplete` indicates half-closure on the read side of a connection. `error` is set if the receive
/// did not complete due to an error, though there may still be some data.
private func dataReceivedHandler(content: Data?, context: NWConnection.ContentContext?, isComplete: Bool, error: NWError?) {
private func dataReceivedHandler(
content: Data?,
context: NWConnection.ContentContext?,
isComplete: Bool,
error: NWError?
) {
precondition(self.outstandingRead)
self.outstandingRead = false
@ -443,7 +458,7 @@ extension StateManagedNWConnectionChannel {
self.pipeline.fireUserInboundEventTriggered(NIOTSNetworkEvents.BetterPathUnavailable())
}
}
/// Called by the underlying `NWConnection` when a path becomes viable or non-viable
///
/// Notifies the channel pipeline of the new viability.
@ -501,7 +516,9 @@ extension StateManagedNWConnectionChannel {
}
}
self.pipeline.fireUserInboundEventTriggered(TLSUserEvent.handshakeCompleted(negotiatedProtocol: negotiatedProtocol))
self.pipeline.fireUserInboundEventTriggered(
TLSUserEvent.handshakeCompleted(negotiatedProtocol: negotiatedProtocol)
)
}
}
@ -545,7 +562,9 @@ extension StateManagedNWConnectionChannel {
try self.nwOptions.applyChannelOption(option: optionValue, value: value as! SocketOptionValue)
}
case _ as ChannelOptions.Types.WriteBufferWaterMarkOption:
if self._backpressureManager.writabilityChanges(whenUpdatingWaterMarks: value as! ChannelOptions.Types.WriteBufferWaterMark) {
if self._backpressureManager.writabilityChanges(
whenUpdatingWaterMarks: value as! ChannelOptions.Types.WriteBufferWaterMark
) {
self.pipeline.fireChannelWritabilityChanged()
}
case is NIOTSChannelOptions.Types.NIOTSEnablePeerToPeerOption:
@ -563,7 +582,8 @@ extension StateManagedNWConnectionChannel {
case is NIOTSChannelOptions.Types.NIOTSAllowLocalEndpointReuse:
self.allowLocalEndpointReuse = value as! NIOTSChannelOptions.Types.NIOTSAllowLocalEndpointReuse.Value
case is NIOTSChannelOptions.Types.NIOTSMinimumIncompleteReceiveLengthOption:
self.minimumIncompleteReceiveLength = value as! NIOTSChannelOptions.Types.NIOTSMinimumIncompleteReceiveLengthOption.Value
self.minimumIncompleteReceiveLength =
value as! NIOTSChannelOptions.Types.NIOTSMinimumIncompleteReceiveLengthOption.Value
case is NIOTSChannelOptions.Types.NIOTSMaximumReceiveLengthOption:
self.maximumReceiveLength = value as! NIOTSChannelOptions.Types.NIOTSMaximumReceiveLengthOption.Value
default:

View File

@ -18,7 +18,6 @@ import NIOCore
import NIOEmbedded
import NIOTransportServices
class NIOFilterEmptyWritesHandlerTests: XCTestCase {
var allocator: ByteBufferAllocator!
var channel: EmbeddedChannel!
@ -52,36 +51,42 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
XCTAssertNil(try channel.readOutbound(as: ByteBuffer.self))
)
}
func testEmptyWritesNoWriteThrough() {
class OutboundTestHandler: ChannelOutboundHandler {
typealias OutboundIn = ByteBuffer
typealias OutboundOut = ByteBuffer
func write(context: ChannelHandlerContext,
data: NIOAny,
promise: EventLoopPromise<Void>?) {
func write(
context: ChannelHandlerContext,
data: NIOAny,
promise: EventLoopPromise<Void>?
) {
XCTFail()
context.write(data, promise: promise)
}
}
XCTAssertNoThrow(
try self.channel.pipeline.addHandler(OutboundTestHandler(),
position: .first).wait()
try self.channel.pipeline.addHandler(
OutboundTestHandler(),
position: .first
).wait()
)
let emptyWrite = self.allocator.buffer(capacity: 0)
let thenEmptyWrite = self.allocator.buffer(capacity: 0)
let thenEmptyWritePromise = self.eventLoop.makePromise(of: Void.self)
self.channel.write(NIOAny(emptyWrite), promise: nil)
self.channel.write(NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise)
self.channel.write(
NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise
)
self.channel.flush()
XCTAssertNoThrow(try thenEmptyWritePromise.futureResult.wait())
XCTAssertNoThrow(
XCTAssertNil(try self.channel.readOutbound(as: ByteBuffer.self))
)
}
func testSomeWriteThenEmptyWritePromiseCascade() {
let someWrite = self.allocator.bufferFor(string: "non empty")
let someWritePromise = self.eventLoop.makePromise(of: Void.self)
@ -101,10 +106,14 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
XCTAssertEqual(checkOrder, .someWrite)
checkOrder = .thenEmptyWrite
}
self.channel.write(NIOAny(someWrite),
promise: someWritePromise)
self.channel.write(NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise)
self.channel.write(
NIOAny(someWrite),
promise: someWritePromise
)
self.channel.write(
NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise
)
self.channel.flush()
XCTAssertNoThrow(try thenEmptyWritePromise.futureResult.wait())
XCTAssertNoThrow(
@ -115,7 +124,7 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
)
XCTAssertEqual(checkOrder, .thenEmptyWrite)
}
func testEmptyWriteTwicePromiseCascade() {
let emptyWrite = self.allocator.buffer(capacity: 0)
let emptyWritePromise = self.eventLoop.makePromise(of: Void.self)
@ -135,10 +144,14 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
XCTAssertEqual(checkOrder, .emptyWrite)
checkOrder = .thenEmptyWrite
}
self.channel.write(NIOAny(emptyWrite),
promise: emptyWritePromise)
self.channel.write(NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise)
self.channel.write(
NIOAny(emptyWrite),
promise: emptyWritePromise
)
self.channel.write(
NIOAny(thenEmptyWrite),
promise: thenEmptyWritePromise
)
self.channel.flush()
XCTAssertNoThrow(try thenEmptyWritePromise.futureResult.wait())
XCTAssertNoThrow(
@ -146,7 +159,7 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
)
XCTAssertEqual(checkOrder, .thenEmptyWrite)
}
func testEmptyWriteThenSomeWriteThenEmptyWritePromiseCascade() {
let emptyWrite = self.allocator.buffer(capacity: 0)
let emptyWritePromise = self.eventLoop.makePromise(of: Void.self)
@ -186,7 +199,7 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
)
XCTAssertEqual(checkOrder, .thenEmptyWrite)
}
func testSomeWriteWithNilPromiseThenEmptyWriteWithNilPromiseThenSomeWrite() {
let someWrite = self.allocator.bufferFor(string: "non empty")
let thenEmptyWrite = self.allocator.buffer(capacity: 0)
@ -211,7 +224,7 @@ class NIOFilterEmptyWritesHandlerTests: XCTestCase {
XCTAssertNil(try self.channel.readOutbound(as: ByteBuffer.self))
)
}
func testSomeWriteAndFlushThenSomeWriteAndFlush() {
let someWrite = self.allocator.bufferFor(string: "non empty")
var someWritePromise: EventLoopPromise<Void>! = self.eventLoop.makePromise()

View File

@ -87,7 +87,9 @@ private final class TLSUserEventHandler: ChannelInboundHandler, RemovableChannel
context.fireUserInboundEventTriggered(TLSUserEvent.handshakeCompleted(negotiatedProtocol: alpn))
context.pipeline.removeHandler(self, promise: nil)
} else if string.hasPrefix("alpn:") {
context.fireUserInboundEventTriggered(TLSUserEvent.handshakeCompleted(negotiatedProtocol: String(string.dropFirst(5))))
context.fireUserInboundEventTriggered(
TLSUserEvent.handshakeCompleted(negotiatedProtocol: String(string.dropFirst(5)))
)
context.pipeline.removeHandler(self, promise: nil)
} else {
context.fireChannelRead(data)
@ -152,7 +154,10 @@ private final class AddressedEnvelopingHandler: ChannelDuplexHandler {
func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise<Void>?) {
let buffer = self.unwrapOutboundIn(data)
if let remoteAddress = self.remoteAddress {
context.write(self.wrapOutboundOut(AddressedEnvelope(remoteAddress: remoteAddress, data: buffer)), promise: promise)
context.write(
self.wrapOutboundOut(AddressedEnvelope(remoteAddress: remoteAddress, data: buffer)),
promise: promise
)
return
}
@ -219,7 +224,10 @@ final class AsyncChannelBootstrapTests: XCTestCase {
}
}
let stringChannel = try await self.makeClientChannel(eventLoopGroup: eventLoopGroup, port: channel.channel.localAddress!.port!)
let stringChannel = try await self.makeClientChannel(
eventLoopGroup: eventLoopGroup,
port: channel.channel.localAddress!.port!
)
try await stringChannel.executeThenClose { _, outbound in
try await outbound.write("hello")
await XCTAsyncAssertEqual(await iterator.next(), .string("hello"))
@ -455,22 +463,24 @@ final class AsyncChannelBootstrapTests: XCTestCase {
}
let channels = NIOLockedValueBox<[Channel]>([Channel]())
let channel: NIOAsyncChannel<EventLoopFuture<NegotiationResult>, Never> = try await NIOTSListenerBootstrap(group: eventLoopGroup)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(CollectingHandler(channels: channels))
}
let channel: NIOAsyncChannel<EventLoopFuture<NegotiationResult>, Never> = try await NIOTSListenerBootstrap(
group: eventLoopGroup
)
.serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
.serverChannelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(CollectingHandler(channels: channels))
}
.childChannelOption(ChannelOptions.autoRead, value: true)
.bind(
host: "127.0.0.1",
port: 0
) { channel in
channel.eventLoop.makeCompletedFuture {
try self.configureProtocolNegotiationHandlers(channel: channel).protocolNegotiationResult
}
}
.childChannelOption(ChannelOptions.autoRead, value: true)
.bind(
host: "127.0.0.1",
port: 0
) { channel in
channel.eventLoop.makeCompletedFuture {
try self.configureProtocolNegotiationHandlers(channel: channel).protocolNegotiationResult
}
}
try await withThrowingTaskGroup(of: Void.self) { group in
let (stream, continuation) = AsyncStream<StringOrByte>.makeStream()
@ -541,11 +551,14 @@ final class AsyncChannelBootstrapTests: XCTestCase {
// MARK: - Test Helpers
private func makeClientChannel(eventLoopGroup: EventLoopGroup, port: Int) async throws -> NIOAsyncChannel<String, String> {
return try await NIOTSConnectionBootstrap(group: eventLoopGroup)
private func makeClientChannel(
eventLoopGroup: EventLoopGroup,
port: Int
) async throws -> NIOAsyncChannel<String, String> {
try await NIOTSConnectionBootstrap(group: eventLoopGroup)
.connect(
to: .init(ipAddress: "127.0.0.1", port: port)
) { channel in
) { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(AddressedEnvelopingHandler())
try channel.pipeline.syncOperations.addHandler(ByteToMessageHandler(LineDelimiterCoder()))
@ -567,12 +580,13 @@ final class AsyncChannelBootstrapTests: XCTestCase {
port: Int,
proposedALPN: TLSUserEventHandler.ALPN
) async throws -> EventLoopFuture<NegotiationResult> {
return try await NIOTSConnectionBootstrap(group: eventLoopGroup)
try await NIOTSConnectionBootstrap(group: eventLoopGroup)
.connect(
to: .init(ipAddress: "127.0.0.1", port: port)
) { channel in
return channel.eventLoop.makeCompletedFuture {
return try self.configureProtocolNegotiationHandlers(channel: channel, proposedALPN: proposedALPN).protocolNegotiationResult
channel.eventLoop.makeCompletedFuture {
try self.configureProtocolNegotiationHandlers(channel: channel, proposedALPN: proposedALPN)
.protocolNegotiationResult
}
}
}
@ -583,11 +597,11 @@ final class AsyncChannelBootstrapTests: XCTestCase {
proposedOuterALPN: TLSUserEventHandler.ALPN,
proposedInnerALPN: TLSUserEventHandler.ALPN
) async throws -> EventLoopFuture<EventLoopFuture<NegotiationResult>> {
return try await NIOTSConnectionBootstrap(group: eventLoopGroup)
try await NIOTSConnectionBootstrap(group: eventLoopGroup)
.connect(
to: .init(ipAddress: "127.0.0.1", port: port)
) { channel in
return channel.eventLoop.makeCompletedFuture {
channel.eventLoop.makeCompletedFuture {
try self.configureNestedProtocolNegotiationHandlers(
channel: channel,
proposedOuterALPN: proposedOuterALPN,
@ -617,20 +631,26 @@ final class AsyncChannelBootstrapTests: XCTestCase {
try channel.pipeline.syncOperations.addHandler(ByteToMessageHandler(LineDelimiterCoder()))
try channel.pipeline.syncOperations.addHandler(MessageToByteHandler(LineDelimiterCoder()))
try channel.pipeline.syncOperations.addHandler(TLSUserEventHandler(proposedALPN: proposedOuterALPN))
let negotiationHandler = NIOTypedApplicationProtocolNegotiationHandler<EventLoopFuture<NegotiationResult>> { alpnResult, channel in
let negotiationHandler = NIOTypedApplicationProtocolNegotiationHandler<EventLoopFuture<NegotiationResult>> {
alpnResult,
channel in
switch alpnResult {
case .negotiated(let alpn):
switch alpn {
case "string":
return channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(TLSUserEventHandler(proposedALPN: proposedInnerALPN))
try channel.pipeline.syncOperations.addHandler(
TLSUserEventHandler(proposedALPN: proposedInnerALPN)
)
let negotiationFuture = try self.addTypedApplicationProtocolNegotiationHandler(to: channel)
return negotiationFuture.protocolNegotiationResult
}
case "byte":
return channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(TLSUserEventHandler(proposedALPN: proposedInnerALPN))
try channel.pipeline.syncOperations.addHandler(
TLSUserEventHandler(proposedALPN: proposedInnerALPN)
)
let negotiationHandler = try self.addTypedApplicationProtocolNegotiationHandler(to: channel)
return negotiationHandler.protocolNegotiationResult
@ -647,8 +667,12 @@ final class AsyncChannelBootstrapTests: XCTestCase {
}
@discardableResult
private func addTypedApplicationProtocolNegotiationHandler(to channel: Channel) throws -> NIOTypedApplicationProtocolNegotiationHandler<NegotiationResult> {
let negotiationHandler = NIOTypedApplicationProtocolNegotiationHandler<NegotiationResult> { alpnResult, channel in
private func addTypedApplicationProtocolNegotiationHandler(
to channel: Channel
) throws -> NIOTypedApplicationProtocolNegotiationHandler<NegotiationResult> {
let negotiationHandler = NIOTypedApplicationProtocolNegotiationHandler<NegotiationResult> {
alpnResult,
channel in
switch alpnResult {
case .negotiated(let alpn):
switch alpn {
@ -697,7 +721,12 @@ extension AsyncStream {
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
private func XCTAsyncAssertEqual<Element: Equatable>(_ lhs: @autoclosure () async throws -> Element, _ rhs: @autoclosure () async throws -> Element, file: StaticString = #filePath, line: UInt = #line) async rethrows {
private func XCTAsyncAssertEqual<Element: Equatable>(
_ lhs: @autoclosure () async throws -> Element,
_ rhs: @autoclosure () async throws -> Element,
file: StaticString = #filePath,
line: UInt = #line
) async rethrows {
let lhsResult = try await lhs()
let rhsResult = try await rhs()
XCTAssertEqual(lhsResult, rhsResult, file: file, line: line)

View File

@ -24,7 +24,7 @@ import Foundation
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
final class NIOTSBootstrapTests: XCTestCase {
var groupBag: [NIOTSEventLoopGroup]? = nil // protected by `self.lock`
var groupBag: [NIOTSEventLoopGroup]? = nil // protected by `self.lock`
let lock = NIOLock()
override func setUp() {
@ -35,16 +35,19 @@ final class NIOTSBootstrapTests: XCTestCase {
}
override func tearDown() {
XCTAssertNoThrow(try self.lock.withLock {
guard let groupBag = self.groupBag else {
XCTFail()
return
XCTAssertNoThrow(
try self.lock.withLock {
guard let groupBag = self.groupBag else {
XCTFail()
return
}
for group in groupBag {
XCTAssertNoThrow(try group.syncShutdownGracefully())
}
self.groupBag = nil
}
XCTAssertNoThrow(try groupBag.forEach {
XCTAssertNoThrow(try $0.syncShutdownGracefully())
})
self.groupBag = nil
})
)
}
func freshEventLoop() -> EventLoop {
@ -58,34 +61,38 @@ final class NIOTSBootstrapTests: XCTestCase {
func testBootstrapsTolerateFuturesFromDifferentEventLoopsReturnedInInitializers() throws {
let childChannelDone = self.freshEventLoop().makePromise(of: Void.self)
let serverChannelDone = self.freshEventLoop().makePromise(of: Void.self)
let serverChannel = try assertNoThrowWithValue(NIOTSListenerBootstrap(group: self.freshEventLoop())
.childChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
childChannelDone.succeed(())
let serverChannel = try assertNoThrowWithValue(
NIOTSListenerBootstrap(group: self.freshEventLoop())
.childChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
childChannelDone.succeed(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
.serverChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
serverChannelDone.succeed(())
.serverChannelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
defer {
serverChannelDone.succeed(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
return self.freshEventLoop().makeSucceededFuture(())
}
.bind(host: "127.0.0.1", port: 0)
.wait())
.bind(host: "127.0.0.1", port: 0)
.wait()
)
defer {
XCTAssertNoThrow(try serverChannel.close().wait())
}
let client = try assertNoThrowWithValue(NIOTSConnectionBootstrap(group: self.freshEventLoop())
.channelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
return self.freshEventLoop().makeSucceededFuture(())
}
.connect(to: serverChannel.localAddress!)
.wait())
let client = try assertNoThrowWithValue(
NIOTSConnectionBootstrap(group: self.freshEventLoop())
.channelInitializer { channel in
channel.eventLoop.preconditionInEventLoop()
return self.freshEventLoop().makeSucceededFuture(())
}
.connect(to: serverChannel.localAddress!)
.wait()
)
defer {
XCTAssertNoThrow(try client.syncCloseAcceptingAlreadyClosed())
}
@ -115,7 +122,7 @@ final class NIOTSBootstrapTests: XCTestCase {
}
switch self.buffer!.readBytes(length: 2) {
case .some([0x16, 0x03]): // TLS ClientHello always starts with 0x16, 0x03
case .some([0x16, 0x03]): // TLS ClientHello always starts with 0x16, 0x03
self.isTLS.succeed(true)
context.channel.close(promise: nil)
case .some(_):
@ -134,9 +141,9 @@ final class NIOTSBootstrapTests: XCTestCase {
.childChannelInitializer { channel in
XCTAssertEqual(0, numberOfConnections.loadThenWrappingIncrement(ordering: .relaxed))
return channel.pipeline.addHandler(TellMeIfConnectionIsTLSHandler(isTLS: isTLS))
}
.bind(host: "127.0.0.1", port: 0)
.wait()
}
.bind(host: "127.0.0.1", port: 0)
.wait()
}
let isTLSConnection1 = group.next().makePromise(of: Bool.self)
@ -158,11 +165,15 @@ final class NIOTSBootstrapTests: XCTestCase {
}
let tlsOptions = NWProtocolTLS.Options()
let bootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: group),
tls: NIOTSClientTLSProvider(tlsOptions: tlsOptions))
let tlsBootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: group),
tls: NIOTSClientTLSProvider())
.enableTLS()
let bootstrap = NIOClientTCPBootstrap(
NIOTSConnectionBootstrap(group: group),
tls: NIOTSClientTLSProvider(tlsOptions: tlsOptions)
)
let tlsBootstrap = NIOClientTCPBootstrap(
NIOTSConnectionBootstrap(group: group),
tls: NIOTSClientTLSProvider()
)
.enableTLS()
var buffer = server1.allocator.buffer(capacity: 2)
buffer.writeString("NO")
@ -249,44 +260,55 @@ final class NIOTSBootstrapTests: XCTestCase {
XCTAssertNil(NIOTSListenerBootstrap(validatingGroup: wrongELG, childGroup: correctELG))
XCTAssertNil(NIOTSListenerBootstrap(validatingGroup: wrongEL, childGroup: correctEL))
}
func testEndpointReuseShortcutOption() throws {
let group = NIOTSEventLoopGroup()
let listenerChannel = try NIOTSListenerBootstrap(group: group)
.bind(host: "127.0.0.1", port: 0)
.wait()
let bootstrap = NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: group),
tls: NIOInsecureNoTLS())
.channelConvenienceOptions([.allowLocalEndpointReuse])
let bootstrap = NIOClientTCPBootstrap(
NIOTSConnectionBootstrap(group: group),
tls: NIOInsecureNoTLS()
)
.channelConvenienceOptions([.allowLocalEndpointReuse])
let client = try bootstrap.connect(to: listenerChannel.localAddress!).wait()
let optionValue = try client.getOption(NIOTSChannelOptions.allowLocalEndpointReuse).wait()
try client.close().wait()
XCTAssertEqual(optionValue, true)
}
func testShorthandOptionsAreEquivalent() throws {
func setAndGetOption<Option>(option: Option,
_ applyOptions : (NIOClientTCPBootstrap) -> NIOClientTCPBootstrap)
throws -> Option.Value where Option : ChannelOption {
func setAndGetOption<Option>(
option: Option,
_ applyOptions: (NIOClientTCPBootstrap) -> NIOClientTCPBootstrap
)
throws -> Option.Value where Option: ChannelOption
{
let group = NIOTSEventLoopGroup()
let listenerChannel = try NIOTSListenerBootstrap(group: group)
.bind(host: "127.0.0.1", port: 0)
.wait()
let bootstrap = applyOptions(NIOClientTCPBootstrap(NIOTSConnectionBootstrap(group: group),
tls: NIOInsecureNoTLS()))
let bootstrap = applyOptions(
NIOClientTCPBootstrap(
NIOTSConnectionBootstrap(group: group),
tls: NIOInsecureNoTLS()
)
)
let client = try bootstrap.connect(to: listenerChannel.localAddress!).wait()
let optionRead = try client.getOption(option).wait()
try client.close().wait()
return optionRead
}
func checkOptionEquivalence<Option>(longOption: Option, setValue: Option.Value,
shortOption: ChannelOptions.TCPConvenienceOption) throws
where Option : ChannelOption, Option.Value : Equatable {
func checkOptionEquivalence<Option>(
longOption: Option,
setValue: Option.Value,
shortOption: ChannelOptions.TCPConvenienceOption
) throws
where Option: ChannelOption, Option.Value: Equatable {
let longSetValue = try setAndGetOption(option: longOption) { bs in
bs.channelOption(longOption, value: setValue)
}
@ -294,20 +316,26 @@ final class NIOTSBootstrapTests: XCTestCase {
bs.channelConvenienceOptions([shortOption])
}
let unsetValue = try setAndGetOption(option: longOption) { $0 }
XCTAssertEqual(longSetValue, shortSetValue)
XCTAssertNotEqual(longSetValue, unsetValue)
}
try checkOptionEquivalence(longOption: NIOTSChannelOptions.allowLocalEndpointReuse,
setValue: true,
shortOption: .allowLocalEndpointReuse)
try checkOptionEquivalence(longOption: ChannelOptions.allowRemoteHalfClosure,
setValue: true,
shortOption: .allowRemoteHalfClosure)
try checkOptionEquivalence(longOption: ChannelOptions.autoRead,
setValue: false,
shortOption: .disableAutoRead)
try checkOptionEquivalence(
longOption: NIOTSChannelOptions.allowLocalEndpointReuse,
setValue: true,
shortOption: .allowLocalEndpointReuse
)
try checkOptionEquivalence(
longOption: ChannelOptions.allowRemoteHalfClosure,
setValue: true,
shortOption: .allowRemoteHalfClosure
)
try checkOptionEquivalence(
longOption: ChannelOptions.autoRead,
setValue: false,
shortOption: .disableAutoRead
)
}
func testBootstrapsErrorGracefullyOnOutOfBandPorts() throws {
@ -325,10 +353,14 @@ final class NIOTSBootstrapTests: XCTestCase {
var listenerChannel: Channel?
var connectionChannel: Channel?
XCTAssertThrowsError(listenerChannel = try listenerBootstrap.bind(host: "localhost", port: invalidPort).wait()) { error in
XCTAssertThrowsError(
listenerChannel = try listenerBootstrap.bind(host: "localhost", port: invalidPort).wait()
) { error in
XCTAssertNotNil(error as? NIOTSErrors.InvalidPort)
}
XCTAssertThrowsError(connectionChannel = try connectionBootstrap.connect(host: "localhost", port: invalidPort).wait()) { error in
XCTAssertThrowsError(
connectionChannel = try connectionBootstrap.connect(host: "localhost", port: invalidPort).wait()
) { error in
XCTAssertNotNil(error as? NIOTSErrors.InvalidPort)
}
@ -348,7 +380,7 @@ final class NIOTSBootstrapTests: XCTestCase {
let listenerChannel: Channel = try listenerBootstrap.bind(host: "localhost", port: 0).wait()
let connectionChannel: Channel = try connectionBootstrap.connect(to: listenerChannel.localAddress!).wait()
defer{
defer {
try? listenerChannel.close().wait()
try? connectionChannel.close().wait()
}
@ -362,7 +394,7 @@ extension Channel {
do {
try self.close().wait()
} catch ChannelError.alreadyClosed {
/* we're happy with this one */
// we're happy with this one
} catch let e {
throw e
}

View File

@ -26,7 +26,7 @@ final class NIOTSChannelMetadataTests: XCTestCase {
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)")
}
@ -35,12 +35,16 @@ final class NIOTSChannelMetadataTests: XCTestCase {
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())
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)")
}

View File

@ -30,7 +30,7 @@ class NIOTSChannelOptionsTests: XCTestCase {
override func tearDown() {
XCTAssertNoThrow(try self.group.syncShutdownGracefully())
}
func testCurrentPath() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.bind(host: "localhost", port: 0).wait()
@ -44,11 +44,11 @@ class NIOTSChannelOptionsTests: XCTestCase {
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()
@ -62,8 +62,10 @@ class NIOTSChannelOptionsTests: XCTestCase {
defer {
XCTAssertNoThrow(try connection.close().wait())
}
let metadata = try connection.getOption(NIOTSChannelOptions.metadata(NWProtocolTCP.definition)).wait() as! NWProtocolTCP.Metadata
let metadata =
try connection.getOption(NIOTSChannelOptions.metadata(NWProtocolTCP.definition)).wait()
as! NWProtocolTCP.Metadata
XCTAssertEqual(metadata.availableReceiveBuffer, 0)
}
@ -81,13 +83,13 @@ class NIOTSChannelOptionsTests: XCTestCase {
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")
@ -105,9 +107,9 @@ class NIOTSChannelOptionsTests: XCTestCase {
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)
@ -133,8 +135,12 @@ class NIOTSChannelOptionsTests: XCTestCase {
XCTAssertNoThrow(try connection.close().wait())
}
let listenerValue = try assertNoThrowWithValue(listener.getOption(NIOTSChannelOptions.multipathServiceType).wait())
let connectionValue = try assertNoThrowWithValue(connection.getOption(NIOTSChannelOptions.multipathServiceType).wait())
let listenerValue = try assertNoThrowWithValue(
listener.getOption(NIOTSChannelOptions.multipathServiceType).wait()
)
let connectionValue = try assertNoThrowWithValue(
connection.getOption(NIOTSChannelOptions.multipathServiceType).wait()
)
XCTAssertEqual(listenerValue, .handover)
XCTAssertEqual(connectionValue, .interactive)
@ -155,7 +161,9 @@ class NIOTSChannelOptionsTests: XCTestCase {
XCTAssertNoThrow(try connection.close().wait())
}
let connectionValue = try assertNoThrowWithValue(connection.getOption(NIOTSChannelOptions.minimumIncompleteReceiveLength).wait())
let connectionValue = try assertNoThrowWithValue(
connection.getOption(NIOTSChannelOptions.minimumIncompleteReceiveLength).wait()
)
XCTAssertEqual(connectionValue, 1)
}
@ -175,7 +183,9 @@ class NIOTSChannelOptionsTests: XCTestCase {
XCTAssertNoThrow(try connection.close().wait())
}
let connectionValue = try assertNoThrowWithValue(connection.getOption(NIOTSChannelOptions.maximumReceiveLength).wait())
let connectionValue = try assertNoThrowWithValue(
connection.getOption(NIOTSChannelOptions.maximumReceiveLength).wait()
)
XCTAssertEqual(connectionValue, 8192)
}

View File

@ -19,7 +19,6 @@ import NIOCore
import NIOTransportServices
import Foundation
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
final class ConnectRecordingHandler: ChannelOutboundHandler {
typealias OutboundIn = Any
@ -44,7 +43,6 @@ final class ConnectRecordingHandler: ChannelOutboundHandler {
}
}
final class FailOnReadHandler: ChannelInboundHandler {
typealias InboundIn = Any
@ -54,7 +52,6 @@ final class FailOnReadHandler: ChannelInboundHandler {
}
}
final class WritabilityChangedHandler: ChannelInboundHandler {
typealias InboundIn = Any
@ -69,7 +66,6 @@ final class WritabilityChangedHandler: ChannelInboundHandler {
}
}
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
final class DisableWaitingAfterConnect: ChannelOutboundHandler {
typealias OutboundIn = Any
@ -99,7 +95,6 @@ final class EnableWaitingAfterWaiting: ChannelInboundHandler {
}
}
final class PromiseOnActiveHandler: ChannelInboundHandler {
typealias InboundIn = Any
typealias InboundOut = Any
@ -136,7 +131,6 @@ final class EventWaiter<Event>: ChannelInboundHandler {
}
}
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
class NIOTSConnectionChannelTests: XCTestCase {
private var group: NIOTSEventLoopGroup!
@ -193,7 +187,15 @@ class NIOTSConnectionChannelTests: XCTestCase {
try connection.eventLoop.submit {
XCTAssertEqual(connectRecordingHandler.connectTargets, [])
XCTAssertEqual(connectRecordingHandler.endpointTargets, [NWEndpoint.hostPort(host: "localhost", port: NWEndpoint.Port(rawValue: UInt16(listener.localAddress!.port!))!)])
XCTAssertEqual(
connectRecordingHandler.endpointTargets,
[
NWEndpoint.hostPort(
host: "localhost",
port: NWEndpoint.Port(rawValue: UInt16(listener.localAddress!.port!))!
)
]
)
}.wait()
}
@ -210,7 +212,10 @@ class NIOTSConnectionChannelTests: XCTestCase {
XCTAssertEqual(connectRecordingHandler.connectTargets, [])
XCTAssertEqual(connectRecordingHandler.endpointTargets, [])
let target = NWEndpoint.hostPort(host: "localhost", port: NWEndpoint.Port(rawValue: UInt16(listener.localAddress!.port!))!)
let target = NWEndpoint.hostPort(
host: "localhost",
port: NWEndpoint.Port(rawValue: UInt16(listener.localAddress!.port!))!
)
let connection = try connectBootstrap.connect(endpoint: target).wait()
defer {
@ -225,7 +230,7 @@ class NIOTSConnectionChannelTests: XCTestCase {
func testZeroLengthWritesHaveSatisfiedPromises() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(FailOnReadHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(FailOnReadHandler()) }
.bind(host: "localhost", port: 0).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -244,7 +249,6 @@ class NIOTSConnectionChannelTests: XCTestCase {
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.disableAckStretching = true
let listener = try NIOTSListenerBootstrap(group: self.group)
.tcpOptions(tcpOptions)
.serverChannelInitializer { channel in
@ -296,7 +300,10 @@ class NIOTSConnectionChannelTests: XCTestCase {
XCTAssertEqual(option.high, 64 * 1024)
XCTAssertEqual(option.low, 32 * 1024)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 1, high: 101))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 1, high: 101)
)
}.flatMap {
connection.getOption(ChannelOptions.writeBufferWaterMark)
}.map {
@ -323,7 +330,12 @@ class NIOTSConnectionChannelTests: XCTestCase {
.wait()
// We're going to set some helpful watermarks, and allocate a big buffer.
XCTAssertNoThrow(try connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 2, high: 2048)).wait())
XCTAssertNoThrow(
try connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 2, high: 2048)
).wait()
)
var buffer = connection.allocator.buffer(capacity: 2048)
buffer.writeBytes(repeatElement(UInt8(4), count: 2048))
@ -459,36 +471,54 @@ class NIOTSConnectionChannelTests: XCTestCase {
XCTAssertTrue(connection.isWritable)
}.wait()
try connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 256)).flatMap {
try connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 256)
).flatMap {
// High to 256, low to 128. No writability change.
XCTAssertEqual(writabilities, [])
XCTAssertTrue(connection.isWritable)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 255))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 255)
)
}.flatMap {
// High to 255, low to 127. Channel becomes not writable.
XCTAssertEqual(writabilities, [false])
XCTAssertFalse(connection.isWritable)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 256))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 128, high: 256)
)
}.flatMap {
// High back to 256, low to 128. No writability change.
XCTAssertEqual(writabilities, [false])
XCTAssertFalse(connection.isWritable)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 256, high: 1024))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 256, high: 1024)
)
}.flatMap {
// High to 1024, low to 128. No writability change.
XCTAssertEqual(writabilities, [false])
XCTAssertFalse(connection.isWritable)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 257, high: 1024))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 257, high: 1024)
)
}.flatMap {
// Low to 257, channel becomes writable again.
XCTAssertEqual(writabilities, [false, true])
XCTAssertTrue(connection.isWritable)
return connection.setOption(ChannelOptions.writeBufferWaterMark, value: ChannelOptions.Types.WriteBufferWaterMark(low: 256, high: 1024))
return connection.setOption(
ChannelOptions.writeBufferWaterMark,
value: ChannelOptions.Types.WriteBufferWaterMark(low: 256, high: 1024)
)
}.map {
// Low back to 256, no writability change.
XCTAssertEqual(writabilities, [false, true])
@ -535,7 +565,7 @@ class NIOTSConnectionChannelTests: XCTestCase {
}
func testErrorsInChannelSetupAreFine() throws {
struct MyError: Error { }
struct MyError: Error {}
let listener = try NIOTSListenerBootstrap(group: self.group)
.bind(host: "localhost", port: 0).wait()
@ -630,7 +660,7 @@ class NIOTSConnectionChannelTests: XCTestCase {
let connectFuture = NIOTSConnectionBootstrap(group: self.group)
.channelInitializer { channel in
return channel.getOption(NIOTSChannelOptions.waitForActivity).map { value in
channel.getOption(NIOTSChannelOptions.waitForActivity).map { value in
XCTAssertTrue(value)
}.flatMap {
channel.setOption(NIOTSChannelOptions.waitForActivity, value: false)
@ -654,11 +684,11 @@ class NIOTSConnectionChannelTests: XCTestCase {
let connectFuture = NIOTSConnectionBootstrap(group: self.group)
.channelInitializer { channel in
return channel.getOption(NIOTSChannelOptions.enablePeerToPeer).map { value in
channel.getOption(NIOTSChannelOptions.enablePeerToPeer).map { value in
XCTAssertFalse(value)
}.flatMap {
channel.setOption(NIOTSChannelOptions.enablePeerToPeer, value: true)
}.flatMap {
}.flatMap {
channel.setOption(NIOTSChannelOptions.enablePeerToPeer, value: true)
}.flatMap {
channel.getOption(NIOTSChannelOptions.enablePeerToPeer)
}.map { value in
XCTAssertTrue(value)
@ -742,9 +772,11 @@ class NIOTSConnectionChannelTests: XCTestCase {
let testCompletePromise = self.group.next().makePromise(of: Void.self)
let testHandler = TestHandler(testCompletePromise: testCompletePromise)
let listener = try assertNoThrowWithValue(NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(host: "localhost", port: 0).wait())
let listener = try assertNoThrowWithValue(
NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(host: "localhost", port: 0).wait()
)
defer {
XCTAssertNoThrow(try listener.close().wait())
}
@ -769,9 +801,11 @@ class NIOTSConnectionChannelTests: XCTestCase {
//
// Thus, once the test has completed we can enter the event loop and check the read count.
// We expect 2.
XCTAssertNoThrow(try connection.eventLoop.submit {
XCTAssertEqual(testHandler.readCount, 2)
}.wait())
XCTAssertNoThrow(
try connection.eventLoop.submit {
XCTAssertEqual(testHandler.readCount, 2)
}.wait()
)
}
func testLoadingAddressesInMultipleQueues() throws {
@ -783,7 +817,8 @@ class NIOTSConnectionChannelTests: XCTestCase {
let ourSyncQueue = DispatchQueue(label: "ourSyncQueue")
let workFuture = NIOTSConnectionBootstrap(group: self.group).connect(to: listener.localAddress!).map { channel -> Channel in
let workFuture = NIOTSConnectionBootstrap(group: self.group).connect(to: listener.localAddress!).map {
channel -> Channel in
XCTAssertTrue(channel.eventLoop.inEventLoop)
ourSyncQueue.sync {
@ -805,9 +840,10 @@ class NIOTSConnectionChannelTests: XCTestCase {
let eventPromise = loop.makePromise(of: NIOTSNetworkEvents.WaitingForConnectivity.self)
let eventRecordingHandler = EventWaiter<NIOTSNetworkEvents.WaitingForConnectivity>(eventPromise)
// 5s is the worst-case test time: normally it'll be faster as we don't wait for this.
let connectBootstrap = NIOTSConnectionBootstrap(group: loop)
.channelInitializer { channel in channel.pipeline.addHandler(eventRecordingHandler) }
.connectTimeout(.seconds(5)) // This is the worst-case test time: normally it'll be faster as we don't wait for this.
.connectTimeout(.seconds(5))
// We choose 443 here to avoid triggering Private Relay, which can do all kinds of weird stuff to this test.
let target = NWEndpoint.hostPort(host: "example.invalid", port: 443)
@ -870,15 +906,15 @@ class NIOTSConnectionChannelTests: XCTestCase {
final class ForwardErrorHandler: ChannelDuplexHandler {
typealias OutboundIn = ByteBuffer
typealias InboundIn = ByteBuffer
private let testCompletePromise: EventLoopPromise<Error>
let listenerChannel: Channel
init(testCompletePromise: EventLoopPromise<Error>, listenerChannel: Channel) {
self.testCompletePromise = testCompletePromise
self.listenerChannel = listenerChannel
}
func channelActive(context: ChannelHandlerContext) {
listenerChannel
.close()
@ -886,7 +922,7 @@ class NIOTSConnectionChannelTests: XCTestCase {
_ = context.channel.write(ByteBuffer(data: Data()))
}
}
func errorCaught(context: ChannelHandlerContext, error: Error) {
let error = error as? ChannelError
XCTAssertNotEqual(error, ChannelError.eof)
@ -895,14 +931,14 @@ class NIOTSConnectionChannelTests: XCTestCase {
testCompletePromise.succeed(error!)
}
}
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in
return channel.eventLoop.makeSucceededVoidFuture()
channel.eventLoop.makeSucceededVoidFuture()
}
.bind(host: "localhost", port: 0)
.wait()
let testCompletePromise = self.group.next().makePromise(of: Error.self)
let connection = try NIOTSConnectionBootstrap(group: self.group)
.channelInitializer { channel in

View File

@ -21,7 +21,7 @@ import Foundation
extension Channel {
func wait<T>(for type: T.Type, count: Int) throws -> [T] {
return try self.pipeline.context(name: "ByteReadRecorder").flatMap { context in
try self.pipeline.context(name: "ByteReadRecorder").flatMap { context in
if let future = (context.handler as? ReadRecorder<T>)?.notifyForDatagrams(count) {
return future
}
@ -36,15 +36,18 @@ extension Channel {
}
func readCompleteCount() throws -> Int {
return try self.pipeline.context(name: "ByteReadRecorder").map { context in
return (context.handler as! ReadRecorder<ByteBuffer>).readCompleteCount
try self.pipeline.context(name: "ByteReadRecorder").map { context in
(context.handler as! ReadRecorder<ByteBuffer>).readCompleteCount
}.wait()
}
func configureForRecvMmsg(messageCount: Int) throws {
let totalBufferSize = messageCount * 2048
try self.setOption(ChannelOptions.recvAllocator, value: FixedSizeRecvByteBufferAllocator(capacity: totalBufferSize)).flatMap {
try self.setOption(
ChannelOptions.recvAllocator,
value: FixedSizeRecvByteBufferAllocator(capacity: totalBufferSize)
).flatMap {
self.setOption(ChannelOptions.datagramVectorReadMessageCount, value: messageCount)
}.wait()
}
@ -113,8 +116,13 @@ final class ReadRecorder<DataType>: ChannelInboundHandler {
final class NIOTSDatagramConnectionChannelTests: XCTestCase {
private var group: NIOTSEventLoopGroup!
private func buildServerChannel(group: NIOTSEventLoopGroup, host: String = "127.0.0.1", port: Int = 0, onConnect: @escaping (Channel) -> ()) throws -> Channel {
return try NIOTSDatagramListenerBootstrap(group: group)
private func buildServerChannel(
group: NIOTSEventLoopGroup,
host: String = "127.0.0.1",
port: Int = 0,
onConnect: @escaping (Channel) -> Void
) throws -> Channel {
try NIOTSDatagramListenerBootstrap(group: group)
.childChannelInitializer { childChannel in
onConnect(childChannel)
return childChannel.pipeline.addHandler(ReadRecorder<ByteBuffer>(), name: "ByteReadRecorder")
@ -123,8 +131,9 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase {
.wait()
}
private func buildClientChannel(group: NIOTSEventLoopGroup, host: String = "127.0.0.1", port: Int) throws -> Channel {
return try NIOTSDatagramBootstrap(group: group)
private func buildClientChannel(group: NIOTSEventLoopGroup, host: String = "127.0.0.1", port: Int) throws -> Channel
{
try NIOTSDatagramBootstrap(group: group)
.channelInitializer { channel in
channel.pipeline.addHandler(ReadRecorder<ByteBuffer>(), name: "ByteReadRecorder")
}
@ -234,7 +243,6 @@ final class NIOTSDatagramConnectionChannelTests: XCTestCase {
}.wait()
}
func testCanExtractTheListener() throws {
guard #available(macOS 14.0, iOS 17.0, watchOS 10.0, tvOS 17.0, *) else {
throw XCTSkip("Option not available")

View File

@ -19,9 +19,13 @@ import NIOTransportServices
import Foundation
import Network
func assertNoThrowWithValue<T>(_ body: @autoclosure () throws -> T, defaultValue: T? = nil, message: String? = nil,
file: StaticString = #filePath, line: UInt = #line) throws -> T {
func assertNoThrowWithValue<T>(
_ body: @autoclosure () throws -> T,
defaultValue: T? = nil,
message: String? = nil,
file: StaticString = #filePath,
line: UInt = #line
) throws -> T {
do {
return try body()
} catch {
@ -34,7 +38,6 @@ func assertNoThrowWithValue<T>(_ body: @autoclosure () throws -> T, defaultValue
}
}
final class EchoHandler: ChannelInboundHandler {
typealias InboundIn = Any
typealias OutboundOut = Any
@ -48,18 +51,17 @@ final class EchoHandler: ChannelInboundHandler {
}
}
final class ReadExpecter: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
struct DidNotReadError: Error { }
struct DidNotReadError: Error {}
private var readPromise: EventLoopPromise<Void>?
private var cumulationBuffer: ByteBuffer?
private let expectedRead: ByteBuffer
var readFuture: EventLoopFuture<Void>? {
return self.readPromise?.futureResult
self.readPromise?.futureResult
}
init(expecting: ByteBuffer) {
@ -95,7 +97,6 @@ final class ReadExpecter: ChannelInboundHandler {
}
}
final class CloseOnActiveHandler: ChannelInboundHandler {
typealias InboundIn = Never
typealias OutboundOut = Never
@ -105,7 +106,6 @@ final class CloseOnActiveHandler: ChannelInboundHandler {
}
}
final class HalfCloseHandler: ChannelInboundHandler {
typealias InboundIn = Never
typealias InboundOut = Never
@ -141,7 +141,6 @@ final class HalfCloseHandler: ChannelInboundHandler {
}
}
final class FailOnHalfCloseHandler: ChannelInboundHandler {
typealias InboundIn = Any
@ -158,7 +157,6 @@ final class FailOnHalfCloseHandler: ChannelInboundHandler {
}
}
final class WaitForActiveHandler: ChannelInboundHandler {
typealias InboundIn = Any
@ -179,13 +177,12 @@ final class WaitForActiveHandler: ChannelInboundHandler {
}
}
extension Channel {
/// Expect that the given bytes will be received.
func expectRead(_ bytes: ByteBuffer) -> EventLoopFuture<Void> {
let expecter = ReadExpecter(expecting: bytes)
return self.pipeline.addHandler(expecter).flatMap {
return expecter.readFuture!
expecter.readFuture!
}
}
}
@ -212,7 +209,7 @@ class NIOTSEndToEndTests: XCTestCase {
func testSimpleListener() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(host: "localhost", port: 0).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -232,9 +229,10 @@ class NIOTSEndToEndTests: XCTestCase {
func testNWExistingListener() throws {
let nwListenerTest = try NWListener(
using: NWParameters(tls: nil),
on: NWEndpoint.Port(rawValue: 0)!)
on: NWEndpoint.Port(rawValue: 0)!
)
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.withNWListener(nwListenerTest).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -243,7 +241,8 @@ class NIOTSEndToEndTests: XCTestCase {
let nwConnectionTest = NWConnection(
host: NWEndpoint.Host("localhost"),
port: nwListenerTest.port!,
using: NWParameters(tls: nil))
using: NWParameters(tls: nil)
)
let connection = try NIOTSConnectionBootstrap(group: self.group)
.withExistingNWConnection(nwConnectionTest).wait()
@ -260,7 +259,7 @@ class NIOTSEndToEndTests: XCTestCase {
func testMultipleConnectionsOneListener() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(host: "localhost", port: 0).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -269,7 +268,7 @@ class NIOTSEndToEndTests: XCTestCase {
let bootstrap = NIOTSConnectionBootstrap(group: self.group)
let completeFutures: [EventLoopFuture<Void>] = (0..<10).map { _ in
return bootstrap.connect(to: listener.localAddress!).flatMap { channel -> EventLoopFuture<Void> in
bootstrap.connect(to: listener.localAddress!).flatMap { channel -> EventLoopFuture<Void> in
let buffer = channel.allocator.bufferFor(string: "hello, world!")
let completeFuture = channel.expectRead(buffer)
channel.writeAndFlush(buffer, promise: nil)
@ -283,7 +282,7 @@ class NIOTSEndToEndTests: XCTestCase {
func testBasicConnectionTeardown() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(CloseOnActiveHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(CloseOnActiveHandler()) }
.bind(host: "localhost", port: 0).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -327,7 +326,8 @@ class NIOTSEndToEndTests: XCTestCase {
for _ in (0..<10) {
// Each connection attempt needs to enter the group twice: each end will leave it once
// for us.
closeFutureGroup.enter(); closeFutureGroup.enter()
closeFutureGroup.enter()
closeFutureGroup.enter()
bootstrap.connect(to: listener.localAddress!).whenSuccess { channel in
closeFutureSyncQueue.sync {
closeFutures.append(channel.closeFuture)
@ -338,7 +338,7 @@ class NIOTSEndToEndTests: XCTestCase {
closeFutureGroup.wait()
let allClosed = closeFutureSyncQueue.sync {
return EventLoopFuture<Void>.andAllComplete(closeFutures, on: self.group.next())
EventLoopFuture<Void>.andAllComplete(closeFutures, on: self.group.next())
}
XCTAssertNoThrow(try allClosed.wait())
}
@ -347,9 +347,9 @@ class NIOTSEndToEndTests: XCTestCase {
let serverSideConnectionPromise: EventLoopPromise<Channel> = self.group.next().makePromise()
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in
return channel.pipeline.addHandlers([
channel.pipeline.addHandlers([
WaitForActiveHandler(serverSideConnectionPromise),
EchoHandler()
EchoHandler(),
])
}
.bind(host: "localhost", port: 0).wait()
@ -483,7 +483,7 @@ class NIOTSEndToEndTests: XCTestCase {
let udsPath = "/tmp/\(UUID().uuidString)_testBasicUnixSockets.sock"
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(unixDomainSocketPath: udsPath).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -513,7 +513,7 @@ class NIOTSEndToEndTests: XCTestCase {
let serviceEndpoint = NWEndpoint.service(name: name, type: "_niots._tcp", domain: "local", interface: nil)
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(EchoHandler()) }
.bind(endpoint: serviceEndpoint).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -541,7 +541,7 @@ class NIOTSEndToEndTests: XCTestCase {
let listener = try NIOTSListenerBootstrap(group: self.group)
.serverChannelOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR), value: 0)
.serverChannelOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEPORT), value: 0)
.childChannelInitializer { channel in channel.pipeline.addHandler(CloseOnActiveHandler())}
.childChannelInitializer { channel in channel.pipeline.addHandler(CloseOnActiveHandler()) }
.bind(host: "localhost", port: 0).wait()
let address = listener.localAddress!
@ -550,20 +550,22 @@ class NIOTSEndToEndTests: XCTestCase {
XCTAssertNoThrow(try listener.close().wait())
// this should now definitely time out.
XCTAssertThrowsError(try NIOTSConnectionBootstrap(group: self.group)
.connectTimeout(.milliseconds(10))
.connect(to: address)
.wait()) { error in
print(error)
XCTAssertThrowsError(
try NIOTSConnectionBootstrap(group: self.group)
.connectTimeout(.milliseconds(10))
.connect(to: address)
.wait()
) { error in
print(error)
}
}
func testViabilityUpdate() throws {
final class ViabilityHandler: ChannelInboundHandler {
typealias InboundIn = ByteBuffer
private let testCompletePromise: EventLoopPromise<Bool>
init(testCompletePromise: EventLoopPromise<Bool>) {
self.testCompletePromise = testCompletePromise
}
@ -574,14 +576,14 @@ class NIOTSEndToEndTests: XCTestCase {
}
}
}
let listener = try NIOTSListenerBootstrap(group: self.group)
.childChannelInitializer { channel in
return channel.eventLoop.makeSucceededVoidFuture()
channel.eventLoop.makeSucceededVoidFuture()
}
.bind(host: "localhost", port: 0)
.wait()
let testCompletePromise = self.group.next().makePromise(of: Bool.self)
let connection = try NIOTSConnectionBootstrap(group: self.group)
.channelInitializer { channel in
@ -593,7 +595,7 @@ class NIOTSEndToEndTests: XCTestCase {
}
.connect(to: listener.localAddress!)
.wait()
do {
let result = try testCompletePromise.futureResult.wait()
XCTAssertEqual(result, true)

View File

@ -19,7 +19,6 @@ import NIOCore
import NIOConcurrencyHelpers
import NIOTransportServices
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
class NIOTSEventLoopTest: XCTestCase {
func testIsInEventLoopWorks() throws {
@ -38,8 +37,10 @@ class NIOTSEventLoopTest: XCTestCase {
try loop.scheduleTask(in: .milliseconds(100)) {
let newNow = DispatchTime.now()
XCTAssertGreaterThan(newNow.uptimeNanoseconds - now.uptimeNanoseconds,
100 * 1000 * 1000)
XCTAssertGreaterThan(
newNow.uptimeNanoseconds - now.uptimeNanoseconds,
100 * 1000 * 1000
)
}.futureResult.wait()
}
@ -54,11 +55,13 @@ class NIOTSEventLoopTest: XCTestCase {
let secondTask = loop.scheduleTask(in: .milliseconds(10)) {
firstTask.cancel()
}
let thirdTask = loop.scheduleTask(in: .milliseconds(50)) { }
let thirdTask = loop.scheduleTask(in: .milliseconds(50)) {}
firstTask.futureResult.whenComplete { (_: Result<Void, Error>) in
let newNow = DispatchTime.now()
XCTAssertLessThan(newNow.uptimeNanoseconds - now.uptimeNanoseconds,
300 * 1000 * 1000)
XCTAssertLessThan(
newNow.uptimeNanoseconds - now.uptimeNanoseconds,
300 * 1000 * 1000
)
}
XCTAssertNoThrow(try secondTask.futureResult.wait())
@ -88,7 +91,8 @@ class NIOTSEventLoopTest: XCTestCase {
XCTAssertFalse(firstLoop.inEventLoop)
XCTAssertTrue(secondLoop.inEventLoop)
}
try EventLoopFuture<Void>.andAllComplete([firstTask.futureResult, secondTask.futureResult], on: firstLoop).wait()
try EventLoopFuture<Void>.andAllComplete([firstTask.futureResult, secondTask.futureResult], on: firstLoop)
.wait()
}
func testWeDontHoldELOrELGReferencesImmeditelyFollowingAConnect() {
@ -112,20 +116,24 @@ class NIOTSEventLoopTest: XCTestCase {
}
.bind(host: "127.0.0.1", port: 0).wait()
// leave this "localhost" so we need to resolve it (involving happy eyeballs)
let client = try NIOTSConnectionBootstrap(group: group).connect(host: "localhost",
port: server.localAddress!.port!).wait()
let client = try NIOTSConnectionBootstrap(group: group).connect(
host: "localhost",
port: server.localAddress!.port!
).wait()
XCTAssertNoThrow(try client.close().wait())
XCTAssertNoThrow(try acceptedChan.futureResult.wait().close().flatMapErrorThrowing { error in
if let error = error as? ChannelError, error == .alreadyClosed {
// this is okay because we previously closed the other end
} else {
throw error
XCTAssertNoThrow(
try acceptedChan.futureResult.wait().close().flatMapErrorThrowing { error in
if let error = error as? ChannelError, error == .alreadyClosed {
// this is okay because we previously closed the other end
} else {
throw error
}
}
})
)
XCTAssertNoThrow(try server.close().wait())
}
XCTAssertNoThrow(try make())
usleep(100_000) // to give the other thread chance to deallocate everything
usleep(100_000) // to give the other thread chance to deallocate everything
XCTAssertNil(weakELG)
XCTAssertNil(weakEL)
}

View File

@ -18,7 +18,6 @@ import Network
import NIOCore
import NIOTransportServices
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
final class BindRecordingHandler: ChannelOutboundHandler {
typealias OutboundIn = Any
@ -43,7 +42,6 @@ final class BindRecordingHandler: ChannelOutboundHandler {
}
}
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
class NIOTSListenerChannelTests: XCTestCase {
private var group: NIOTSEventLoopGroup!
@ -60,7 +58,7 @@ class NIOTSListenerChannelTests: XCTestCase {
let bindRecordingHandler = BindRecordingHandler()
let target = try SocketAddress.makeAddressResolvingHost("localhost", port: 0)
let bindBootstrap = NIOTSListenerBootstrap(group: self.group)
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler)}
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler) }
XCTAssertEqual(bindRecordingHandler.bindTargets, [])
XCTAssertEqual(bindRecordingHandler.endpointTargets, [])
@ -79,7 +77,7 @@ class NIOTSListenerChannelTests: XCTestCase {
func testConnectingToHostPortTraversesPipeline() throws {
let bindRecordingHandler = BindRecordingHandler()
let bindBootstrap = NIOTSListenerBootstrap(group: self.group)
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler)}
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler) }
XCTAssertEqual(bindRecordingHandler.bindTargets, [])
XCTAssertEqual(bindRecordingHandler.endpointTargets, [])
@ -90,7 +88,10 @@ class NIOTSListenerChannelTests: XCTestCase {
}
try self.group.next().submit {
XCTAssertEqual(bindRecordingHandler.bindTargets, [try SocketAddress.makeAddressResolvingHost("localhost", port: 0)])
XCTAssertEqual(
bindRecordingHandler.bindTargets,
[try SocketAddress.makeAddressResolvingHost("localhost", port: 0)]
)
XCTAssertEqual(bindRecordingHandler.endpointTargets, [])
}.wait()
}
@ -99,7 +100,7 @@ class NIOTSListenerChannelTests: XCTestCase {
let endpoint = NWEndpoint.hostPort(host: .ipv4(.loopback), port: .any)
let bindRecordingHandler = BindRecordingHandler()
let bindBootstrap = NIOTSListenerBootstrap(group: self.group)
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler)}
.serverChannelInitializer { channel in channel.pipeline.addHandler(bindRecordingHandler) }
XCTAssertEqual(bindRecordingHandler.bindTargets, [])
XCTAssertEqual(bindRecordingHandler.endpointTargets, [])
@ -142,7 +143,7 @@ class NIOTSListenerChannelTests: XCTestCase {
}
func testErrorsInChannelSetupAreFine() throws {
struct MyError: Error { }
struct MyError: Error {}
let listenerFuture = NIOTSListenerBootstrap(group: self.group)
.serverChannelInitializer { channel in channel.eventLoop.makeFailedFuture(MyError()) }
@ -205,12 +206,12 @@ class NIOTSListenerChannelTests: XCTestCase {
func testCanObserveValueOfEnablePeerToPeer() throws {
let listener = try NIOTSListenerBootstrap(group: self.group)
.serverChannelInitializer { channel in
return channel.getOption(NIOTSChannelOptions.enablePeerToPeer).map { value in
channel.getOption(NIOTSChannelOptions.enablePeerToPeer).map { value in
XCTAssertFalse(value)
}.flatMap {
channel.setOption(NIOTSChannelOptions.enablePeerToPeer, value: true)
}.flatMap {
channel.getOption(NIOTSChannelOptions.enablePeerToPeer)
channel.getOption(NIOTSChannelOptions.enablePeerToPeer)
}.map { value in
XCTAssertTrue(value)
}
@ -277,7 +278,11 @@ class NIOTSListenerChannelTests: XCTestCase {
XCTAssertNoThrow(try channel.close().wait())
XCTFail("Did not throw")
} catch {
XCTAssertEqual(error as? NIOTSErrors.BindTimeout, NIOTSErrors.BindTimeout(timeout: .nanoseconds(0)), "unexpected error: \(error)")
XCTAssertEqual(
error as? NIOTSErrors.BindTimeout,
NIOTSErrors.BindTimeout(timeout: .nanoseconds(0)),
"unexpected error: \(error)"
)
}
}
@ -290,7 +295,8 @@ class NIOTSListenerChannelTests: XCTestCase {
let ourSyncQueue = DispatchQueue(label: "ourSyncQueue")
let workFuture = NIOTSConnectionBootstrap(group: self.group).connect(to: listener.localAddress!).map { channel -> Channel in
let workFuture = NIOTSConnectionBootstrap(group: self.group).connect(to: listener.localAddress!).map {
channel -> Channel in
XCTAssertTrue(listener.eventLoop.inEventLoop)
ourSyncQueue.sync {

View File

@ -18,7 +18,6 @@ import NIOCore
import Network
@testable import NIOTransportServices
@available(macOS 10.14, iOS 12.0, tvOS 12.0, watchOS 6, *)
class NIOTSSocketOptionTests: XCTestCase {
private var options: NWProtocolTCP.Options!
@ -31,12 +30,14 @@ class NIOTSSocketOptionTests: XCTestCase {
self.options = nil
}
private func assertProperty<T: Equatable>(called path: KeyPath<NWProtocolTCP.Options, T>,
correspondsTo socketOption: ChannelOptions.Types.SocketOption,
defaultsTo defaultValue: T,
and defaultSocketOptionValue: SocketOptionValue,
canBeSetTo unusualValue: SocketOptionValue,
whichLeadsTo newInnerValue: T) throws {
private func assertProperty<T: Equatable>(
called path: KeyPath<NWProtocolTCP.Options, T>,
correspondsTo socketOption: ChannelOptions.Types.SocketOption,
defaultsTo defaultValue: T,
and defaultSocketOptionValue: SocketOptionValue,
canBeSetTo unusualValue: SocketOptionValue,
whichLeadsTo newInnerValue: T
) throws {
// Confirm the default is right.
let actualDefaultSocketOptionValue = try self.options.valueFor(socketOption: socketOption)
XCTAssertEqual(self.options[keyPath: path], defaultValue)
@ -54,87 +55,135 @@ class NIOTSSocketOptionTests: XCTestCase {
}
func testReadingAndSettingNoDelay() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.noDelay,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NODELAY),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.noDelay,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NODELAY),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testReadingAndSettingNoPush() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.noPush,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOPUSH),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.noPush,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOPUSH),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testReadingAndSettingNoOpt() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.noOptions,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOOPT),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.noOptions,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOOPT),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testReadingAndSettingKeepaliveCount() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.keepaliveCount,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPCNT),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.keepaliveCount,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPCNT),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingKeepaliveIdle() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.keepaliveIdle,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPALIVE),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.keepaliveIdle,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPALIVE),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingKeepaliveInterval() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.keepaliveInterval,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPINTVL),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.keepaliveInterval,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPINTVL),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingMaxSeg() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.maximumSegmentSize,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_MAXSEG),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.maximumSegmentSize,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_MAXSEG),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingConnectTimeout() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.connectionTimeout,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_CONNECTIONTIMEOUT),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.connectionTimeout,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_CONNECTIONTIMEOUT),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingConnectDropTime() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.connectionDropTime,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_CONNDROPTIME),
defaultsTo: 0, and: 0,
canBeSetTo: 5, whichLeadsTo: 5)
try self.assertProperty(
called: \NWProtocolTCP.Options.connectionDropTime,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_CONNDROPTIME),
defaultsTo: 0,
and: 0,
canBeSetTo: 5,
whichLeadsTo: 5
)
}
func testReadingAndSettingFinDrop() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.retransmitFinDrop,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_FINDROP),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.retransmitFinDrop,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_FINDROP),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testReadingAndSettingAckStretching() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.disableAckStretching,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_SENDMOREACKS),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.disableAckStretching,
correspondsTo: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_SENDMOREACKS),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testReadingAndSettingKeepalive() throws {
try self.assertProperty(called: \NWProtocolTCP.Options.enableKeepalive,
correspondsTo: ChannelOptions.Types.SocketOption(level: SOL_SOCKET, name: SO_KEEPALIVE),
defaultsTo: false, and: 0,
canBeSetTo: 1, whichLeadsTo: true)
try self.assertProperty(
called: \NWProtocolTCP.Options.enableKeepalive,
correspondsTo: ChannelOptions.Types.SocketOption(level: SOL_SOCKET, name: SO_KEEPALIVE),
defaultsTo: false,
and: 0,
canBeSetTo: 1,
whichLeadsTo: true
)
}
func testWritingNonexistentSocketOption() {

View File

@ -18,20 +18,26 @@ import NIOCore
import Network
@testable import NIOTransportServices
private extension Channel {
extension Channel {
private func getSocketOption(_ option: ChannelOptions.Types.SocketOption) -> EventLoopFuture<SocketOptionValue> {
return self.getOption(option)
self.getOption(option)
}
private func setSocketOption(_ option: ChannelOptions.Types.SocketOption, to value: SocketOptionValue) -> EventLoopFuture<Void> {
return self.setOption(option, value: value)
private func setSocketOption(
_ option: ChannelOptions.Types.SocketOption,
to value: SocketOptionValue
) -> EventLoopFuture<Void> {
self.setOption(option, value: value)
}
/// Asserts that a given socket option has a default value, that its value can be changed to a new value, and that it can then be
/// switched back.
func assertOptionRoundTrips(option: ChannelOptions.Types.SocketOption, initialValue: SocketOptionValue, testAlternativeValue: SocketOptionValue) -> EventLoopFuture<Void> {
return self.getSocketOption(option).flatMap { actualInitialValue in
fileprivate func assertOptionRoundTrips(
option: ChannelOptions.Types.SocketOption,
initialValue: SocketOptionValue,
testAlternativeValue: SocketOptionValue
) -> EventLoopFuture<Void> {
self.getSocketOption(option).flatMap { actualInitialValue in
XCTAssertEqual(actualInitialValue, initialValue)
return self.setSocketOption(option, to: testAlternativeValue)
}.flatMap {
@ -59,7 +65,11 @@ class NIOTSSocketOptionsOnChannelTests: XCTestCase {
XCTAssertNoThrow(try self.group.syncShutdownGracefully())
}
func assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption, initialValue: SocketOptionValue, testAlternativeValue: SocketOptionValue) throws {
func assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption,
initialValue: SocketOptionValue,
testAlternativeValue: SocketOptionValue
) throws {
let listener = try NIOTSListenerBootstrap(group: group).bind(host: "127.0.0.1", port: 0).wait()
defer {
XCTAssertNoThrow(try listener.close().wait())
@ -69,56 +79,116 @@ class NIOTSSocketOptionsOnChannelTests: XCTestCase {
XCTAssertNoThrow(try connector.close().wait())
}
XCTAssertNoThrow(try listener.assertOptionRoundTrips(option: option, initialValue: initialValue, testAlternativeValue: testAlternativeValue).wait())
XCTAssertNoThrow(try connector.assertOptionRoundTrips(option: option, initialValue: initialValue, testAlternativeValue: testAlternativeValue).wait())
XCTAssertNoThrow(
try listener.assertOptionRoundTrips(
option: option,
initialValue: initialValue,
testAlternativeValue: testAlternativeValue
).wait()
)
XCTAssertNoThrow(
try connector.assertOptionRoundTrips(
option: option,
initialValue: initialValue,
testAlternativeValue: testAlternativeValue
).wait()
)
}
func testNODELAY() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NODELAY), initialValue: 1, testAlternativeValue: 0)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NODELAY),
initialValue: 1,
testAlternativeValue: 0
)
}
func testNOPUSH() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOPUSH), initialValue: 0, testAlternativeValue: 1)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOPUSH),
initialValue: 0,
testAlternativeValue: 1
)
}
func testNOOPT() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOOPT), initialValue: 0, testAlternativeValue: 1)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_NOOPT),
initialValue: 0,
testAlternativeValue: 1
)
}
func testKEEPCNT() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPCNT), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPCNT),
initialValue: 0,
testAlternativeValue: 5
)
}
func testKEEPALIVE() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPALIVE), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPALIVE),
initialValue: 0,
testAlternativeValue: 5
)
}
func testKEEPINTVL() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPINTVL), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_KEEPINTVL),
initialValue: 0,
testAlternativeValue: 5
)
}
func testMAXSEG() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_MAXSEG), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_MAXSEG),
initialValue: 0,
testAlternativeValue: 5
)
}
func testCONNECTIONTIMEOUT() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_CONNECTIONTIMEOUT), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_CONNECTIONTIMEOUT),
initialValue: 0,
testAlternativeValue: 5
)
}
func testRXT_CONNDROPTIME() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_CONNDROPTIME), initialValue: 0, testAlternativeValue: 5)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_CONNDROPTIME),
initialValue: 0,
testAlternativeValue: 5
)
}
func testRXT_FINDROP() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_FINDROP), initialValue: 0, testAlternativeValue: 1)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_RXT_FINDROP),
initialValue: 0,
testAlternativeValue: 1
)
}
func testSENDMOREACKS() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_SENDMOREACKS), initialValue: 0, testAlternativeValue: 1)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: IPPROTO_TCP, name: TCP_SENDMOREACKS),
initialValue: 0,
testAlternativeValue: 1
)
}
func testSO_KEEPALIVE() throws {
try self.assertChannelOptionAfterCreation(option: ChannelOptions.Types.SocketOption(level: SOL_SOCKET, name: SO_KEEPALIVE), initialValue: 0, testAlternativeValue: 1)
try self.assertChannelOptionAfterCreation(
option: ChannelOptions.Types.SocketOption(level: SOL_SOCKET, name: SO_KEEPALIVE),
initialValue: 0,
testAlternativeValue: 1
)
}
func testMultipleSocketOptions() throws {
@ -137,10 +207,18 @@ class NIOTSSocketOptionsOnChannelTests: XCTestCase {
XCTAssertNoThrow(try connector.close().wait())
}
XCTAssertNoThrow(XCTAssertEqual(1, try listener.getOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait()))
XCTAssertNoThrow(XCTAssertEqual(1, try listener.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait()))
XCTAssertNoThrow(XCTAssertEqual(1, try connector.getOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait()))
XCTAssertNoThrow(XCTAssertEqual(1, try connector.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait()))
XCTAssertNoThrow(
XCTAssertEqual(1, try listener.getOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait())
)
XCTAssertNoThrow(
XCTAssertEqual(1, try listener.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait())
)
XCTAssertNoThrow(
XCTAssertEqual(1, try connector.getOption(ChannelOptions.socket(SOL_SOCKET, SO_REUSEADDR)).wait())
)
XCTAssertNoThrow(
XCTAssertEqual(1, try connector.getOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY)).wait())
)
}
}
#endif

View File

@ -1,22 +0,0 @@
ARG swift_version=5.7
ARG ubuntu_version=focal
ARG base_image=swift:$swift_version-$ubuntu_version
FROM $base_image
# needed to do again after FROM due to docker limitation
ARG swift_version
ARG ubuntu_version
# set as UTF-8
RUN apt-get update && apt-get install -y locales locales-all
ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US.UTF-8
# tools
RUN mkdir -p $HOME/.tools
RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile
ARG swiftformat_version=0.40.12
RUN git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format
RUN cd $HOME/.tools/swift-format && swift build -c release
RUN ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat

View File

@ -1,19 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-nio-transport-services:22.04-5.10
build:
args:
ubuntu_version: "jammy"
swift_version: "5.10"
documentation-check:
image: swift-nio-transport-services:22.04-5.10
test:
image: swift-nio-transport-services:22.04-5.10
shell:
image: swift-nio-transport-services:22.04-5.10

View File

@ -1,19 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-nio-transport-services:22.04-5.8
build:
args:
ubuntu_version: "jammy"
swift_version: "5.8"
documentation-check:
image: swift-nio-transport-services:22.04-5.8
test:
image: swift-nio-transport-services:22.04-5.8
shell:
image: swift-nio-transport-services:22.04-5.8

View File

@ -1,19 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-nio-transport-services:22.04-5.9
build:
args:
ubuntu_version: "jammy"
swift_version: "5.9"
documentation-check:
image: swift-nio-transport-services:22.04-5.9
test:
image: swift-nio-transport-services:22.04-5.9
shell:
image: swift-nio-transport-services:22.04-5.9

View File

@ -1,19 +0,0 @@
version: "3"
services:
runtime-setup:
image: swift-nio-transport-services:22.04-main
build:
args:
ubuntu_version: "jammy"
base_image: "swiftlang/swift:nightly-main-jammy"
documentation-check:
image: swift-nio-transport-services:22.04-main
test:
image: swift-nio-transport-services:22.04-main
shell:
image: swift-nio-transport-services:22.04-main

View File

@ -1,41 +0,0 @@
# this file is not designed to be run directly
# instead, use the docker-compose.<os>.<swift> files
# eg docker-compose -f docker/docker-compose.yaml -f docker/docker-compose.1804.50.yaml run test
version: "3"
services:
runtime-setup:
image: swift-nio-transport-services:default
build:
context: .
dockerfile: Dockerfile
common: &common
image: swift-nio-transport-services:default
depends_on: [runtime-setup]
volumes:
- ~/.ssh:/root/.ssh
- ..:/code:z
working_dir: /code
cap_drop:
- CAP_NET_RAW
- CAP_NET_BIND_SERVICE
soundness:
<<: *common
command: /bin/bash -xcl "./scripts/soundness.sh"
documentation-check:
<<: *common
command: /bin/bash -xcl "./scripts/check-docs.sh"
test:
<<: *common
command: /bin/bash -xcl "swift test --enable-test-discovery -Xswiftc -warnings-as-errors $${SANITIZER_ARG-}"
# util
shell:
<<: *common
entrypoint: /bin/bash

View File

@ -1,26 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2023 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
##
##===----------------------------------------------------------------------===##
set -eu
raw_targets=$(sed -E -n -e 's/^.* - documentation_targets: \[(.*)\].*$/\1/p' .spi.yml)
targets=(${raw_targets//,/ })
# Add the DocC plugin; the doc checking CI runs on 5.8 so we can't use "swift package add-dependency".
echo 'package.dependencies.append(.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.0.0"))' >> Package.swift
for target in "${targets[@]}"; do
swift package plugin generate-documentation --target "$target" --warnings-as-errors --analyze --level detailed
done

View File

@ -1,142 +0,0 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2017-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
##
##===----------------------------------------------------------------------===##
set -eu
here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
function replace_acceptable_years() {
# this needs to replace all acceptable forms with 'YEARS'
sed -e 's/20[12][7890123]-20[12][890123]/YEARS/' -e 's/20[12][890123]/YEARS/'
}
# This checks for the umbrella NIO module.
printf "=> Checking for imports of umbrella NIO module... "
if git grep --color=never -i "^[ \t]*import \+NIO[ \t]*$" > /dev/null; then
printf "\033[0;31mUmbrella imports found.\033[0m\n"
git grep -i "^[ \t]*import \+NIO[ \t]*$"
exit 1
fi
printf "\033[0;32mokay.\033[0m\n"
printf "=> Checking license headers... "
tmp=$(mktemp /tmp/.swift-nio-sanity_XXXXXX)
for language in swift-or-c bash dtrace python; do
declare -a matching_files
declare -a exceptions
expections=( )
matching_files=( -name '*' )
case "$language" in
swift-or-c)
exceptions=( -name LinuxMain.swift -o -name Package.swift -o -name 'Package@swift*.swift' )
matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' )
cat > "$tmp" <<"EOF"
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) YEARS 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
//
//===----------------------------------------------------------------------===//
EOF
;;
bash)
matching_files=( -name '*.sh' )
cat > "$tmp" <<"EOF"
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) YEARS 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
##
##===----------------------------------------------------------------------===##
EOF
;;
python)
matching_files=( -name '*.py' )
cat > "$tmp" <<"EOF"
#!/usr/bin/env python
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) YEARS 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
##
##===----------------------------------------------------------------------===##
EOF
;;
dtrace)
matching_files=( -name '*.d' )
cat > "$tmp" <<"EOF"
#!/usr/sbin/dtrace -q -s
/*===----------------------------------------------------------------------===*
*
* This source file is part of the SwiftNIO open source project
*
* Copyright (c) YEARS 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
*
*===----------------------------------------------------------------------===*/
EOF
;;
*)
echo >&2 "ERROR: unknown language '$language'"
;;
esac
expected_lines=$(cat "$tmp" | wc -l)
expected_sha=$(cat "$tmp" | shasum)
(
cd "$here/.."
find . \
\( \! -path './.build/*' -a \
\( "${matching_files[@]}" \) -a \
\( \! \( "${exceptions[@]}" \) \) \) | while read line; do
if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then
printf "\033[0;31mmissing headers in file '$line'!\033[0m\n"
diff -u <(cat "$line" | replace_acceptable_years | head -n $expected_lines) "$tmp"
exit 1
fi
done
printf "\033[0;32mokay.\033[0m\n"
)
done
rm "$tmp"