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:
parent
bbd5e63cf9
commit
c73112efc0
|
|
@ -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"
|
||||
|
|
@ -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.
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
]),
|
||||
]
|
||||
),
|
||||
]
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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!)")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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"
|
||||
Loading…
Reference in New Issue