From 8eea84ec6144167354387ef9244b0939f5852dc8 Mon Sep 17 00:00:00 2001 From: Fabian Fett Date: Fri, 22 Apr 2022 08:57:02 +0200 Subject: [PATCH 01/22] Fix warnings, that appeared after requiring Swift 5.4 (#159) --- Package.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Package.swift b/Package.swift index d49419fc..66ef39d0 100644 --- a/Package.swift +++ b/Package.swift @@ -30,7 +30,7 @@ var targets: [PackageDescription.Target] = [ .product(name: "NIOCore", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), ]), - .target( + .executableTarget( name: "HTTPServerWithQuiescingDemo", dependencies: [ "NIOExtras", @@ -38,7 +38,7 @@ var targets: [PackageDescription.Target] = [ .product(name: "NIOPosix", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), ]), - .target( + .executableTarget( name: "NIOWritePCAPDemo", dependencies: [ "NIOExtras", @@ -46,7 +46,7 @@ var targets: [PackageDescription.Target] = [ .product(name: "NIOPosix", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), ]), - .target( + .executableTarget( name: "NIOWritePartialPCAPDemo", dependencies: [ "NIOExtras", @@ -54,7 +54,7 @@ var targets: [PackageDescription.Target] = [ .product(name: "NIOPosix", package: "swift-nio"), .product(name: "NIOHTTP1", package: "swift-nio"), ]), - .target( + .executableTarget( name: "NIOExtrasPerformanceTester", dependencies: [ "NIOExtras", @@ -69,7 +69,7 @@ var targets: [PackageDescription.Target] = [ .product(name: "NIO", package: "swift-nio"), .product(name: "NIOCore", package: "swift-nio"), ]), - .target( + .executableTarget( name: "NIOSOCKSClient", dependencies: [ .product(name: "NIOCore", package: "swift-nio"), From ba05378671d37bcef669a83df155053bcf85bdb3 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Tue, 3 May 2022 16:31:04 +0100 Subject: [PATCH 02/22] Update podspec generation (#161) Motivation: Pods need to include all transitive dependencies in order to work around https://github.com/apple/swift-nio/issues/2073. We also ship a second library from this repo: NIOSOCKS which doesn't yet have a pod published. Modifications: - Update the pod generation script Result: Pods include all transitive dependencies --- scripts/build_podspec.sh | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/scripts/build_podspec.sh b/scripts/build_podspec.sh index acea3d2d..cea35368 100755 --- a/scripts/build_podspec.sh +++ b/scripts/build_podspec.sh @@ -42,7 +42,6 @@ if [[ $# -lt 2 ]]; then fi version=$1 -name="SwiftNIOExtras" # Current SwiftNIO Version to add as dependency in the .podspec nio_version=$2 @@ -60,9 +59,14 @@ here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" tmpdir=$(mktemp -d /tmp/.build_podspecsXXXXXX) echo "Building podspec in $tmpdir" -cat > "${tmpdir}/${name}.podspec" <<- EOF +# Right now this is only valid because the transitive dependencies of NIOExtras +# and NIOSOCKS are the same. +names=("NIOExtras" "NIOSOCKS") +for name in "${names[@]}"; do + podname="Swift${name}" +cat > "${tmpdir}/${podname}.podspec" <<- EOF Pod::Spec.new do |s| - s.name = '$name' + s.name = '$podname' s.version = '$version' s.license = { :type => 'Apache 2.0', :file => 'LICENSE.txt' } s.summary = 'Useful code around SwiftNIO.' @@ -79,16 +83,26 @@ Pod::Spec.new do |s| s.tvos.deployment_target = '10.0' s.watchos.deployment_target = '6.0' + s.dependency 'CNIOAtomics', '>= $nio_version', '< $next_major_version' + s.dependency 'CNIODarwin', '>= $nio_version', '< $next_major_version' + s.dependency 'CNIOLinux', '>= $nio_version', '< $next_major_version' + s.dependency 'CNIOWindows', '>= $nio_version', '< $next_major_version' s.dependency 'SwiftNIO', '>= $nio_version', '< $next_major_version' + s.dependency 'SwiftNIOConcurrencyHelpers', '>= $nio_version', '< $next_major_version' + s.dependency 'SwiftNIOCore', '>= $nio_version', '< $next_major_version' + s.dependency 'SwiftNIOEmbedded', '>= $nio_version', '< $next_major_version' + s.dependency 'SwiftNIOPosix', '>= $nio_version', '< $next_major_version' + s.dependency '_NIODataStructures', '>= $nio_version', '< $next_major_version' - s.source_files = 'Sources/NIOExtras/**/*.swift' + s.source_files = 'Sources/$name/**/*.swift' end EOF -if $upload; then - echo "Uploading ${tmpdir}/${name}.podspec" - pod trunk push --synchronous "${tmpdir}/${name}.podspec" -else - echo "Generated podspec available at ${tmpdir}/${name}.podspec" -fi + if $upload; then + echo "Uploading ${tmpdir}/${podname}.podspec" + pod trunk push --synchronous "${tmpdir}/${podname}.podspec" + else + echo "Generated podspec available at ${tmpdir}/${podname}.podspec" + fi +done From 79586313bd1cdd7fb9da3be37b3c55e3a491ead6 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Wed, 4 May 2022 11:02:26 +0100 Subject: [PATCH 03/22] Remove build_podspec.sh (#162) Motivation: We no longer support Cocoapods. Modifications: - Remove build_podspec.sh Result: Less unused code. --- scripts/build_podspec.sh | 108 --------------------------------------- 1 file changed, 108 deletions(-) delete mode 100755 scripts/build_podspec.sh diff --git a/scripts/build_podspec.sh b/scripts/build_podspec.sh deleted file mode 100755 index cea35368..00000000 --- a/scripts/build_podspec.sh +++ /dev/null @@ -1,108 +0,0 @@ -#!/bin/bash -##===----------------------------------------------------------------------===## -## -## This source file is part of the SwiftNIO open source project -## -## Copyright (c) 2017-2019 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 - -function usage() { - echo "$0 [-u] version nio_version" - echo - echo "OPTIONS:" - echo " -u: Additionally upload the podspec" -} - -upload=false -while getopts ":u" opt; do - case $opt in - u) - upload=true - ;; - \?) - usage - exit 1 - ;; - esac -done -shift "$((OPTIND-1))" - -if [[ $# -lt 2 ]]; then - usage - exit 1 -fi - -version=$1 - -# Current SwiftNIO Version to add as dependency in the .podspec -nio_version=$2 -if [[ $nio_version =~ ^([0-9]+)\. ]]; then - # Extract and incremenet the major version to use an upper bound on the - # version requirement (we can't use '~>' as it means 'up to the next - # major' if you specify x.y and 'up to the next minor' if you specify x.y.z). - next_major_version=$((${BASH_REMATCH[1]} + 1)) -else - echo "Invalid NIO version '$nio_version'" - exit 1 -fi - -here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -tmpdir=$(mktemp -d /tmp/.build_podspecsXXXXXX) -echo "Building podspec in $tmpdir" - -# Right now this is only valid because the transitive dependencies of NIOExtras -# and NIOSOCKS are the same. -names=("NIOExtras" "NIOSOCKS") -for name in "${names[@]}"; do - podname="Swift${name}" -cat > "${tmpdir}/${podname}.podspec" <<- EOF -Pod::Spec.new do |s| - s.name = '$podname' - s.version = '$version' - s.license = { :type => 'Apache 2.0', :file => 'LICENSE.txt' } - s.summary = 'Useful code around SwiftNIO.' - s.homepage = 'https://github.com/apple/swift-nio-extras' - s.author = 'Apple Inc.' - s.source = { :git => 'https://github.com/apple/swift-nio-extras.git', :tag => s.version.to_s } - s.documentation_url = 'https://github.com/apple/swift-nio-extras' - s.module_name = 'NIOExtras' - - s.swift_version = '5.0' - s.cocoapods_version = '>=1.6.0' - s.ios.deployment_target = '10.0' - s.osx.deployment_target = '10.12' - s.tvos.deployment_target = '10.0' - s.watchos.deployment_target = '6.0' - - s.dependency 'CNIOAtomics', '>= $nio_version', '< $next_major_version' - s.dependency 'CNIODarwin', '>= $nio_version', '< $next_major_version' - s.dependency 'CNIOLinux', '>= $nio_version', '< $next_major_version' - s.dependency 'CNIOWindows', '>= $nio_version', '< $next_major_version' - s.dependency 'SwiftNIO', '>= $nio_version', '< $next_major_version' - s.dependency 'SwiftNIOConcurrencyHelpers', '>= $nio_version', '< $next_major_version' - s.dependency 'SwiftNIOCore', '>= $nio_version', '< $next_major_version' - s.dependency 'SwiftNIOEmbedded', '>= $nio_version', '< $next_major_version' - s.dependency 'SwiftNIOPosix', '>= $nio_version', '< $next_major_version' - s.dependency '_NIODataStructures', '>= $nio_version', '< $next_major_version' - - s.source_files = 'Sources/$name/**/*.swift' -end -EOF - - if $upload; then - echo "Uploading ${tmpdir}/${podname}.podspec" - pod trunk push --synchronous "${tmpdir}/${podname}.podspec" - else - echo "Generated podspec available at ${tmpdir}/${podname}.podspec" - fi -done - From e4dc3c8d1d1213298b9883f95a9ded5e6032c939 Mon Sep 17 00:00:00 2001 From: David Evans Date: Wed, 4 May 2022 11:46:21 +0100 Subject: [PATCH 04/22] Implement Sendable (#160) Make every non-channel class conform to Sendable (or @unchecked Sendable) to prepare for NIO. --- Sources/NIOExtras/PCAPRingBuffer.swift | 2 +- Sources/NIOExtras/QuiescingHelper.swift | 11 +++++++++++ Sources/NIOExtras/WritePCAPHandler.swift | 8 +++++++- .../HTTP1PerformanceTestFramework.swift | 6 +++--- .../PCAPPerformanceTest.swift | 8 +++----- .../RollingPCAPPerformanceTest.swift | 2 +- 6 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Sources/NIOExtras/PCAPRingBuffer.swift b/Sources/NIOExtras/PCAPRingBuffer.swift index 01da4787..3100f2c8 100644 --- a/Sources/NIOExtras/PCAPRingBuffer.swift +++ b/Sources/NIOExtras/PCAPRingBuffer.swift @@ -76,7 +76,7 @@ public class NIOPCAPRingBuffer { self.popFirst() } precondition(self.pcapFragments.count < self.maximumFragments) - + // Add the new fragment self.append(buffer) diff --git a/Sources/NIOExtras/QuiescingHelper.swift b/Sources/NIOExtras/QuiescingHelper.swift index c1f0f984..193d394b 100644 --- a/Sources/NIOExtras/QuiescingHelper.swift +++ b/Sources/NIOExtras/QuiescingHelper.swift @@ -27,6 +27,7 @@ private final class ChannelCollector { case shuttingDown case shutdownCompleted } + private var openChannels: [ObjectIdentifier: Channel] = [:] private let serverChannel: Channel private var fullyShutdownPromise: EventLoopPromise? = nil @@ -143,6 +144,12 @@ private final class ChannelCollector { } } +#if swift(>=5.5) && canImport(_Concurrency) +extension ChannelCollector: @unchecked Sendable { + +} +#endif + /// A `ChannelHandler` that adds all channels that it receives through the `ChannelPipeline` to a `ChannelCollector`. /// /// - note: This is only useful to be added to a server `Channel` in `ServerBootstrap.serverChannelInitializer`. @@ -250,3 +257,7 @@ public final class ServerQuiescingHelper { } } } + +extension ServerQuiescingHelper: NIOSendable { + +} diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index c3723a95..b457a231 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -595,7 +595,7 @@ extension NIOWritePCAPHandler { /// A `SynchronizedFileSink` is thread-safe so can be used from any thread/`EventLoop`. After use, you /// _must_ call `syncClose` on the `SynchronizedFileSink` to shut it and all the associated resources down. Failing /// to do so triggers undefined behaviour. - public class SynchronizedFileSink { + public final class SynchronizedFileSink { private let fileHandle: NIOFileHandle private let workQueue: DispatchQueue private let writesGroup = DispatchGroup() @@ -715,3 +715,9 @@ extension NIOWritePCAPHandler { } } } + +#if swift(>=5.5) && canImport(_Concurrency) +extension NIOWritePCAPHandler.SynchronizedFileSink: @unchecked Sendable { + +} +#endif diff --git a/Sources/NIOExtrasPerformanceTester/HTTP1PerformanceTestFramework.swift b/Sources/NIOExtrasPerformanceTester/HTTP1PerformanceTestFramework.swift index 8e20f514..46d1e8c0 100644 --- a/Sources/NIOExtrasPerformanceTester/HTTP1PerformanceTestFramework.swift +++ b/Sources/NIOExtrasPerformanceTester/HTTP1PerformanceTestFramework.swift @@ -128,6 +128,9 @@ class HTTP1ThreadedPerformanceTest: Benchmark { let head: HTTPRequestHead + var group: MultiThreadedEventLoopGroup! + var serverChannel: Channel! + init(numberOfRepeats: Int, numberOfClients: Int, requestsPerClient: Int, @@ -142,9 +145,6 @@ class HTTP1ThreadedPerformanceTest: Benchmark { self.head = head } - var group: MultiThreadedEventLoopGroup! - var serverChannel: Channel! - func setUp() throws { self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) self.serverChannel = try ServerBootstrap(group: self.group) diff --git a/Sources/NIOExtrasPerformanceTester/PCAPPerformanceTest.swift b/Sources/NIOExtrasPerformanceTester/PCAPPerformanceTest.swift index ca3eb1b8..19fc5ead 100644 --- a/Sources/NIOExtrasPerformanceTester/PCAPPerformanceTest.swift +++ b/Sources/NIOExtrasPerformanceTester/PCAPPerformanceTest.swift @@ -17,19 +17,17 @@ import NIOEmbedded import NIOExtras import Foundation -class PCAPPerformanceTest: Benchmark { +final class PCAPPerformanceTest: Benchmark { let numberOfRepeats: Int - let byteBuffer = ByteBuffer(repeating: 0x65, count: 1000) + let outputFile = NSTemporaryDirectory() + "/" + UUID().uuidString init(numberOfRepeats: Int) { self.numberOfRepeats = numberOfRepeats } - var outputFile: String! - func setUp() throws { - self.outputFile = NSTemporaryDirectory() + "/" + UUID().uuidString + } func tearDown() { diff --git a/Sources/NIOExtrasPerformanceTester/RollingPCAPPerformanceTest.swift b/Sources/NIOExtrasPerformanceTester/RollingPCAPPerformanceTest.swift index f2fb1005..0eaf6e6d 100644 --- a/Sources/NIOExtrasPerformanceTester/RollingPCAPPerformanceTest.swift +++ b/Sources/NIOExtrasPerformanceTester/RollingPCAPPerformanceTest.swift @@ -16,7 +16,7 @@ import NIOCore import NIOEmbedded import NIOExtras -class RollingPCAPPerformanceTest: Benchmark { +final class RollingPCAPPerformanceTest: Benchmark { let numberOfRepeats: Int let byteBuffer = ByteBuffer(repeating: 0x65, count: 1000) From 8e4f5cfdec5a64c80101c7c4aca32aa562c1b3ca Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Mon, 13 Jun 2022 12:17:06 +0100 Subject: [PATCH 05/22] Use 5.7 nightlies (#164) --- docker/docker-compose.2004.57.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.2004.57.yaml b/docker/docker-compose.2004.57.yaml index f1d25f39..37a62ef9 100644 --- a/docker/docker-compose.2004.57.yaml +++ b/docker/docker-compose.2004.57.yaml @@ -6,7 +6,7 @@ services: image: swift-nio-extras:20.04-5.7 build: args: - base_image: "swiftlang/swift:nightly-main-focal" + base_image: "swiftlang/swift:nightly-5.7-focal" test: image: swift-nio-extras:20.04-5.7 From a75e92bde3683241c15df3dd905b7a6dcac4d551 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Tue, 14 Jun 2022 11:59:18 +0100 Subject: [PATCH 06/22] Bump the lowest NIO version to 2.34.0 (#165) --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index 66ef39d0..69332c62 100644 --- a/Package.swift +++ b/Package.swift @@ -119,7 +119,7 @@ let package = Package( .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.32.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), ], targets: targets ) From f4359b987d8346c52344db06f3778d342404b9d9 Mon Sep 17 00:00:00 2001 From: Peter Adams <63288215+PeterAdams-A@users.noreply.github.com> Date: Mon, 1 Aug 2022 14:21:15 +0100 Subject: [PATCH 07/22] Prepare for docc (#167) Motivation: Publishing docs is a good thing. Modifications: Update Package.swift to allow docc documentation to be generated. Result: It is possible to generate docc docs. --- Package.swift | 5 +- Package@swift-5.4.swift | 125 +++++++++++++++++++++++++++++ Package@swift-5.5.swift | 125 +++++++++++++++++++++++++++++ docker/docker-compose.1804.54.yaml | 1 + docker/docker-compose.yaml | 2 +- scripts/soundness.sh | 2 +- 6 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 Package@swift-5.4.swift create mode 100644 Package@swift-5.5.swift diff --git a/Package.swift b/Package.swift index 69332c62..e97f6c3a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,9 +1,9 @@ -// swift-tools-version:5.4 +// swift-tools-version:5.6 //===----------------------------------------------------------------------===// // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2021 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2017-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -120,6 +120,7 @@ let package = Package( ], dependencies: [ .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), + .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), ], targets: targets ) diff --git a/Package@swift-5.4.swift b/Package@swift-5.4.swift new file mode 100644 index 00000000..b7dd215f --- /dev/null +++ b/Package@swift-5.4.swift @@ -0,0 +1,125 @@ +// swift-tools-version:5.4 +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import PackageDescription + +var targets: [PackageDescription.Target] = [ + .target( + name: "NIOExtras", + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + ]), + .target( + name: "NIOHTTPCompression", + dependencies: [ + "CNIOExtrasZlib", + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "HTTPServerWithQuiescingDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOWritePCAPDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOWritePartialPCAPDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOExtrasPerformanceTester", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .target( + name: "NIOSOCKS", + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOSOCKSClient", + dependencies: [ + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + "NIOSOCKS" + ]), + .target( + name: "CNIOExtrasZlib", + dependencies: [], + linkerSettings: [ + .linkedLibrary("z") + ]), + .testTarget( + name: "NIOExtrasTests", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOTestUtils", package: "swift-nio"), + .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), + ]), + .testTarget( + name: "NIOHTTPCompressionTests", + dependencies: [ + "CNIOExtrasZlib", + "NIOHTTPCompression", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), + ]), + .testTarget( + name: "NIOSOCKSTests", + dependencies: [ + "NIOSOCKS", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + ]) +] + +let package = Package( + name: "swift-nio-extras", + products: [ + .library(name: "NIOExtras", targets: ["NIOExtras"]), + .library(name: "NIOSOCKS", targets: ["NIOSOCKS"]), + .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), + ], + targets: targets +) diff --git a/Package@swift-5.5.swift b/Package@swift-5.5.swift new file mode 100644 index 00000000..57538037 --- /dev/null +++ b/Package@swift-5.5.swift @@ -0,0 +1,125 @@ +// swift-tools-version:5.5 +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +import PackageDescription + +var targets: [PackageDescription.Target] = [ + .target( + name: "NIOExtras", + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + ]), + .target( + name: "NIOHTTPCompression", + dependencies: [ + "CNIOExtrasZlib", + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "HTTPServerWithQuiescingDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOWritePCAPDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOWritePartialPCAPDemo", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOExtrasPerformanceTester", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + ]), + .target( + name: "NIOSOCKS", + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "NIOCore", package: "swift-nio"), + ]), + .executableTarget( + name: "NIOSOCKSClient", + dependencies: [ + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + "NIOSOCKS" + ]), + .target( + name: "CNIOExtrasZlib", + dependencies: [], + linkerSettings: [ + .linkedLibrary("z") + ]), + .testTarget( + name: "NIOExtrasTests", + dependencies: [ + "NIOExtras", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOPosix", package: "swift-nio"), + .product(name: "NIOTestUtils", package: "swift-nio"), + .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), + ]), + .testTarget( + name: "NIOHTTPCompressionTests", + dependencies: [ + "CNIOExtrasZlib", + "NIOHTTPCompression", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + .product(name: "NIOHTTP1", package: "swift-nio"), + .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), + ]), + .testTarget( + name: "NIOSOCKSTests", + dependencies: [ + "NIOSOCKS", + .product(name: "NIOCore", package: "swift-nio"), + .product(name: "NIOEmbedded", package: "swift-nio"), + ]) +] + +let package = Package( + name: "swift-nio-extras", + products: [ + .library(name: "NIOExtras", targets: ["NIOExtras"]), + .library(name: "NIOSOCKS", targets: ["NIOSOCKS"]), + .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), + ], + dependencies: [ + .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), + ], + targets: targets +) diff --git a/docker/docker-compose.1804.54.yaml b/docker/docker-compose.1804.54.yaml index d607fb6e..eb9c80a8 100644 --- a/docker/docker-compose.1804.54.yaml +++ b/docker/docker-compose.1804.54.yaml @@ -12,6 +12,7 @@ services: test: image: swift-nio-extras:18.04-5.4 + command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors $${SANITIZER_ARG-}" shell: image: swift-nio-extras:18.04-5.4 diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d4813831..06b042be 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -28,7 +28,7 @@ services: test: <<: *common - command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors $${SANITIZER_ARG-}" + command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors --enable-test-discovery $${SANITIZER_ARG-}" # util diff --git a/scripts/soundness.sh b/scripts/soundness.sh index 0b242496..96136a6f 100755 --- a/scripts/soundness.sh +++ b/scripts/soundness.sh @@ -71,7 +71,7 @@ for language in swift-or-c bash dtrace; do matching_files=( -name '*' ) case "$language" in swift-or-c) - exceptions=( -name c_nio_http_parser.c -o -name c_nio_http_parser.h -o -name cpp_magic.h -o -name Package.swift -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h) + exceptions=( -name c_nio_http_parser.c -o -name c_nio_http_parser.h -o -name cpp_magic.h -o -name Package.swift -o -name CNIOSHA1.h -o -name c_nio_sha1.c -o -name ifaddrs-android.c -o -name ifaddrs-android.h -o -name 'Package@swift*.swift') matching_files=( -name '*.swift' -o -name '*.c' -o -name '*.h' ) cat > "$tmp" <<"EOF" //===----------------------------------------------------------------------===// From ca22c1252803cd09f9b8e0e680ae21f061cf32c3 Mon Sep 17 00:00:00 2001 From: Peter Adams <63288215+PeterAdams-A@users.noreply.github.com> Date: Tue, 2 Aug 2022 09:46:57 +0100 Subject: [PATCH 08/22] Deprecate cumulationBuffer (#168) Motivation: It is no longer used. Modifications: Deprecate the 3 cumulationBuffers Result: Less confusion and accidental usage. --- Sources/NIOExtras/FixedLengthFrameDecoder.swift | 1 + Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift | 3 ++- Sources/NIOExtras/LineBasedFrameDecoder.swift | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Sources/NIOExtras/FixedLengthFrameDecoder.swift b/Sources/NIOExtras/FixedLengthFrameDecoder.swift index 2929d6d2..bf3249cd 100644 --- a/Sources/NIOExtras/FixedLengthFrameDecoder.swift +++ b/Sources/NIOExtras/FixedLengthFrameDecoder.swift @@ -32,6 +32,7 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { public typealias InboundIn = ByteBuffer public typealias InboundOut = ByteBuffer + @available(*, deprecated, message: "No longer used") public var cumulationBuffer: ByteBuffer? private let frameLength: Int diff --git a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift index 81dbaad2..f461bce8 100644 --- a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift @@ -105,7 +105,8 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { public typealias InboundIn = ByteBuffer public typealias InboundOut = ByteBuffer - + + @available(*, deprecated, message: "No longer used") public var cumulationBuffer: ByteBuffer? private var readState: DecoderReadState = .waitingForHeader diff --git a/Sources/NIOExtras/LineBasedFrameDecoder.swift b/Sources/NIOExtras/LineBasedFrameDecoder.swift index b27ab15d..d0bb8544 100644 --- a/Sources/NIOExtras/LineBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LineBasedFrameDecoder.swift @@ -33,6 +33,8 @@ import NIOCore public class LineBasedFrameDecoder: ByteToMessageDecoder { public typealias InboundIn = ByteBuffer public typealias InboundOut = ByteBuffer + + @available(*, deprecated, message: "No longer used") public var cumulationBuffer: ByteBuffer? // keep track of the last scan offset from the buffer's reader index (if we didn't find the delimiter) private var lastScanOffset = 0 From da7c04777b2997543eea13cb531acde8021bce19 Mon Sep 17 00:00:00 2001 From: Peter Adams <63288215+PeterAdams-A@users.noreply.github.com> Date: Wed, 3 Aug 2022 09:34:45 +0100 Subject: [PATCH 09/22] Docnioextras (#169) * Improve documentation for NIOExtras Motivation: Docs will help users do things correctly. Modifications: Add missing comments, improve links. Result: Better docc documentation * Docc in NIOHTTPCompression * NIOSOCKS docc * Correct bad symbol * Minor typo Co-authored-by: Cory Benfield --- .../NIOExtras/DebugInboundEventsHandler.swift | 90 +++++++++++++++---- .../DebugOutboundEventsHandler.swift | 86 +++++++++++++++--- Sources/NIOExtras/Docs.docc/Article.md | 13 +++ .../NIOExtras/FixedLengthFrameDecoder.swift | 16 +++- .../JSONRPCFraming+ContentLengthHeader.swift | 41 ++++++--- Sources/NIOExtras/JSONRPCFraming.swift | 1 + .../LengthFieldBasedFrameDecoder.swift | 53 ++++++----- Sources/NIOExtras/LengthFieldPrepender.swift | 21 +++-- Sources/NIOExtras/LineBasedFrameDecoder.swift | 20 ++++- Sources/NIOExtras/NIOExtrasError.swift | 1 + .../NIOExtras/NIOLengthFieldBitLength.swift | 14 ++- Sources/NIOExtras/PCAPRingBuffer.swift | 2 +- Sources/NIOExtras/QuiescingHelper.swift | 7 +- .../NIOExtras/RequestResponseHandler.swift | 16 ++-- Sources/NIOExtras/WritePCAPHandler.swift | 23 ++--- .../NIOHTTPCompression/HTTPCompression.swift | 16 +++- .../HTTPDecompression.swift | 4 + .../HTTPRequestCompressor.swift | 12 ++- .../HTTPRequestDecompressor.swift | 7 ++ .../HTTPResponseCompressor.swift | 16 +++- .../HTTPResponseDecompressor.swift | 7 ++ .../Channel Handlers/SOCKSClientHandler.swift | 11 ++- .../SOCKSServerHandshakeHandler.swift | 15 +++- .../NIOSOCKS/Messages/ClientGreeting.swift | 2 +- Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 11 +-- Sources/NIOSOCKS/Messages/SOCKSResponse.swift | 2 +- .../SelectedAuthenticationMethod.swift | 2 +- 27 files changed, 393 insertions(+), 116 deletions(-) create mode 100644 Sources/NIOExtras/Docs.docc/Article.md diff --git a/Sources/NIOExtras/DebugInboundEventsHandler.swift b/Sources/NIOExtras/DebugInboundEventsHandler.swift index eb53d243..10c2e5b9 100644 --- a/Sources/NIOExtras/DebugInboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugInboundEventsHandler.swift @@ -19,76 +19,135 @@ import Glibc import NIOCore -/// ChannelInboundHandler that prints all inbound events that pass through the pipeline by default, -/// overridable by providing your own closure for custom logging. See DebugOutboundEventsHandler for outbound events. +/// `ChannelInboundHandler` that prints all inbound events that pass through the pipeline by default, +/// overridable by providing your own closure for custom logging. See ``DebugOutboundEventsHandler`` for outbound events. public class DebugInboundEventsHandler: ChannelInboundHandler { - + /// The type of the inbound data which is wrapped in `NIOAny`. public typealias InboundIn = Any + /// The type of the inbound data which will be forwarded to the next `ChannelInboundHandler` in the `ChannelPipeline`. public typealias InboudOut = Any - + + /// Enumeration of possible `ChannelHandler` events which can occur. public enum Event { + /// Channel was registered. case registered + /// Channel was unregistered. case unregistered + /// Channel became active. case active + /// Channel became inactive. case inactive + /// Data was received. case read(data: NIOAny) + /// Current read loop finished. case readComplete + /// Writability state of the channel changed. case writabilityChanged(isWritable: Bool) + /// A user inbound event was received. case userInboundEventTriggered(event: Any) + /// An error was caught. case errorCaught(Error) } var logger: (Event, ChannelHandlerContext) -> () - + + /// Initialiser. + /// - Parameter logger: Method for logging events which occur. public init(logger: @escaping (Event, ChannelHandlerContext) -> () = DebugInboundEventsHandler.defaultPrint) { self.logger = logger } - + + /// Logs ``Event.registered`` to ``logger`` + /// Called when the `Channel` has successfully registered with its `EventLoop` to handle I/O. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelRegistered(context: ChannelHandlerContext) { logger(.registered, context) context.fireChannelRegistered() } - + + /// Logs ``Event.unregistered`` to ``logger`` + /// Called when the `Channel` has unregistered from its `EventLoop`, and so will no longer be receiving I/O events. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelUnregistered(context: ChannelHandlerContext) { logger(.unregistered, context) context.fireChannelUnregistered() } - + + /// Logs ``Event.active`` to ``logger`` + /// Called when the `Channel` has become active, and is able to send and receive data. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelActive(context: ChannelHandlerContext) { logger(.active, context) context.fireChannelActive() } - + + /// Logs ``Event.inactive`` to ``logger`` + /// Called when the `Channel` has become inactive and is no longer able to send and receive data`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelInactive(context: ChannelHandlerContext) { logger(.inactive, context) context.fireChannelInactive() } - + + /// Logs ``Event.read`` to ``logger`` + /// Called when some data has been read from the remote peer. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data read from the remote peer, wrapped in a `NIOAny`. public func channelRead(context: ChannelHandlerContext, data: NIOAny) { logger(.read(data: data), context) context.fireChannelRead(data) } - + + /// Logs ``Event.readComplete`` to ``logger`` + /// Called when the `Channel` has completed its current read loop, either because no more data is available + /// to read from the transport at this time, or because the `Channel` needs to yield to the event loop to process + /// other I/O events for other `Channel`s. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelReadComplete(context: ChannelHandlerContext) { logger(.readComplete, context) context.fireChannelReadComplete() } - + + /// Logs ``Event.writabilityChanged`` to ``logger`` + /// The writability state of the `Channel` has changed, either because it has buffered more data than the writability + /// high water mark, or because the amount of buffered data has dropped below the writability low water mark. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func channelWritabilityChanged(context: ChannelHandlerContext) { logger(.writabilityChanged(isWritable: context.channel.isWritable), context) context.fireChannelWritabilityChanged() } - + + /// Logs ``Event.userInboundEventTriggered`` to ``logger`` + /// Called when a user inbound event has been triggered. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - event: The event. public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { logger(.userInboundEventTriggered(event: event), context) context.fireUserInboundEventTriggered(event) } - + + /// Logs ``Event.errorCaught`` to ``logger`` + /// An error was encountered earlier in the inbound `ChannelPipeline`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - error: The `Error` that was encountered. public func errorCaught(context: ChannelHandlerContext, error: Error) { logger(.errorCaught(error), context) context.fireErrorCaught(error) } - + + /// Print and flush a textual description of an ``Event``. + /// - parameters: + /// - event: The ``Event`` to print. + /// - context: The context `event` was received in. public static func defaultPrint(event: Event, in context: ChannelHandlerContext) { let message: String switch event { @@ -114,5 +173,4 @@ public class DebugInboundEventsHandler: ChannelInboundHandler { print(message + " in \(context.name)") fflush(stdout) } - } diff --git a/Sources/NIOExtras/DebugOutboundEventsHandler.swift b/Sources/NIOExtras/DebugOutboundEventsHandler.swift index 43d83a81..eb0437a7 100644 --- a/Sources/NIOExtras/DebugOutboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugOutboundEventsHandler.swift @@ -21,69 +21,130 @@ import Glibc import NIOCore /// ChannelOutboundHandler that prints all outbound events that pass through the pipeline by default, -/// overridable by providing your own closure for custom logging. See DebugInboundEventsHandler for inbound events. +/// overridable by providing your own closure for custom logging. See ``DebugInboundEventsHandler`` for inbound events. public class DebugOutboundEventsHandler: ChannelOutboundHandler { - + /// The type of the outbound data which is wrapped in `NIOAny`. public typealias OutboundIn = Any + /// The type of the outbound data which will be forwarded to the next `ChannelOutboundHandler` in the `ChannelPipeline`. public typealias OutboundOut = Any - + + /// All possible outbound events which could occur. public enum Event { + /// `Channel` registered for I/O events. case register + /// Bound to a `SocketAddress` case bind(address: SocketAddress) + /// Connected to an address. case connect(address: SocketAddress) + /// Write operation. case write(data: NIOAny) + /// Pending writes flushed. case flush + /// Ready to read more data. case read + /// Close the channel. case close(mode: CloseMode) + /// User outbound event triggered. case triggerUserOutboundEvent(event: Any) } var logger: (Event, ChannelHandlerContext) -> () - + + /// Initialiser. + /// - parameters: + /// - logger: Method for logging events which happen. public init(logger: @escaping (Event, ChannelHandlerContext) -> () = DebugOutboundEventsHandler.defaultPrint) { self.logger = logger } - + + /// Logs ``Event.register`` to ``logger`` + /// Called to request that the `Channel` register itself for I/O events with its `EventLoop`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func register(context: ChannelHandlerContext, promise: EventLoopPromise?) { logger(.register, context) context.register(promise: promise) } - + + /// Logs ``Event.bind`` to ``logger`` + /// Called to request that the `Channel` bind to a specific `SocketAddress`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - to: The `SocketAddress` to which this `Channel` should bind. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func bind(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise?) { logger(.bind(address: address), context) context.bind(to: address, promise: promise) } - + + /// Logs ``Event.connect`` to ``logger`` + /// Called to request that the `Channel` connect to a given `SocketAddress`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - to: The `SocketAddress` to which the the `Channel` should connect. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func connect(context: ChannelHandlerContext, to address: SocketAddress, promise: EventLoopPromise?) { logger(.connect(address: address), context) context.connect(to: address, promise: promise) } - + + /// Logs ``Event.data`` to ``logger`` + /// Called to request a write operation. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data to write through the `Channel`, wrapped in a `NIOAny`. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { logger(.write(data: data), context) context.write(data, promise: promise) } - + + /// Logs ``Event.flush`` to ``logger`` + /// Called to request that the `Channel` flush all pending writes. The flush operation will try to flush out all previous written messages + /// that are pending. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func flush(context: ChannelHandlerContext) { logger(.flush, context) context.flush() } - + + /// Logs ``Event.read`` to ``logger`` + /// Called to request that the `Channel` perform a read when data is ready. The read operation will signal that we are ready to read more data. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func read(context: ChannelHandlerContext) { logger(.read, context) context.read() } - + + /// Logs ``Event.close`` to ``logger`` + /// Called to request that the `Channel` close itself down`. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - mode: The `CloseMode` to apply + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func close(context: ChannelHandlerContext, mode: CloseMode, promise: EventLoopPromise?) { logger(.close(mode: mode), context) context.close(mode: mode, promise: promise) } - + + /// Logs ``Event.triggerUserOutboundEvent`` to ``logger`` + /// Called when an user outbound event is triggered. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - event: The triggered event. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func triggerUserOutboundEvent(context: ChannelHandlerContext, event: Any, promise: EventLoopPromise?) { logger(.triggerUserOutboundEvent(event: event), context) context.triggerUserOutboundEvent(event, promise: promise) } + /// Print textual event description to stdout. + /// - parameters: + /// - event: The ``Event`` to print. + /// - in: The context the event occured in. public static func defaultPrint(event: Event, in context: ChannelHandlerContext) { let message: String switch event { @@ -108,5 +169,4 @@ public class DebugOutboundEventsHandler: ChannelOutboundHandler { print(message + " in \(context.name)") fflush(stdout) } - } diff --git a/Sources/NIOExtras/Docs.docc/Article.md b/Sources/NIOExtras/Docs.docc/Article.md new file mode 100644 index 00000000..ed5c0a4a --- /dev/null +++ b/Sources/NIOExtras/Docs.docc/Article.md @@ -0,0 +1,13 @@ +# Article + +Summary + +## Overview + +Text + +## Topics + +### Group + +- ``Symbol`` diff --git a/Sources/NIOExtras/FixedLengthFrameDecoder.swift b/Sources/NIOExtras/FixedLengthFrameDecoder.swift index bf3249cd..4f4a3260 100644 --- a/Sources/NIOExtras/FixedLengthFrameDecoder.swift +++ b/Sources/NIOExtras/FixedLengthFrameDecoder.swift @@ -21,7 +21,7 @@ import NIOCore /// | A | BC | DEFG | HI | /// +---+----+------+----+ /// -/// A `FixedLengthFrameDecoder` will decode them into the +/// A ``FixedLengthFrameDecoder`` will decode them into the /// following three packets with the fixed length: /// /// +-----+-----+-----+ @@ -29,7 +29,9 @@ import NIOCore /// +-----+-----+-----+ /// public final class FixedLengthFrameDecoder: ByteToMessageDecoder { + /// Data type we receive. public typealias InboundIn = ByteBuffer + /// Data type we send to the next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -45,6 +47,11 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { self.frameLength = frameLength } + /// Get a frame of data and `fireChannelRead` if sufficient data exists in the buffer. + /// - Parameters: + /// - context: Calling context. + /// - buffer: Buffer containing data. + /// - Returns: Status detailing if more data is required or if a successful decode occurred. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { guard let slice = buffer.readSlice(length: frameLength) else { return .needMoreData @@ -54,6 +61,13 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { return .continue } + /// Repeatedly decode frames until there is not enough data to decode any more. + /// Reports an error through `fireErrorCaught` if this doesn't empty the buffer exactly. + /// - Parameters: + /// - context: Calling context + /// - buffer: Buffer containing data. + /// - seenEOF: If end of file has been seen. + /// - Returns: needMoreData always as all data is consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { while case .continue = try self.decode(context: context, buffer: &buffer) {} if buffer.readableBytes > 0 { diff --git a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift index 7cc17005..099e93cd 100644 --- a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift +++ b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift @@ -15,22 +15,31 @@ import NIOCore extension NIOJSONRPCFraming { - /// `ContentLengthHeaderFrameEncoder` is responsible for emitting JSON-RPC wire protocol with 'Content-Length' + /// ``ContentLengthHeaderFrameEncoder`` is responsible for emitting JSON-RPC wire protocol with 'Content-Length' /// HTTP-like headers as used by for example by LSP (Language Server Protocol). public final class ContentLengthHeaderFrameEncoder: ChannelOutboundHandler { - /// We'll get handed one message through the `Channel` and ... + /// We'll get handed one message through the `Channel` of this type and will encode into `OutboundOut` public typealias OutboundIn = ByteBuffer - /// ... will encode it into a `ByteBuffer`. + /// Outbound data will be encoded into a `ByteBuffer`. public typealias OutboundOut = ByteBuffer private var scratchBuffer: ByteBuffer! public init() {} + /// Called when this `ChannelHandler` is added to the `ChannelPipeline`. + /// + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. public func handlerAdded(context: ChannelHandlerContext) { self.scratchBuffer = context.channel.allocator.buffer(capacity: 512) } + /// Called to request a write operation. Writes write protocol header and then the message. + /// - parameters: + /// - context: The `ChannelHandlerContext` which this `ChannelHandler` belongs to. + /// - data: The data to write through the `Channel`, wrapped in a `NIOAny`. + /// - promise: The `EventLoopPromise` which should be notified once the operation completes, or nil if no notification should take place. public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { let data = self.unwrapOutboundIn(data) // Step 1, clear the target buffer (note, we are re-using it so if we get lucky we don't need to @@ -52,7 +61,7 @@ extension NIOJSONRPCFraming { } } - /// `ContentLengthHeaderFrameDecoder` is responsible for parsing JSON-RPC wire protocol with 'Content-Length' + /// ``ContentLengthHeaderFrameDecoder`` is responsible for parsing JSON-RPC wire protocol with 'Content-Length' /// HTTP-like headers as used by for example by LSP (Language Server Protocol). public struct ContentLengthHeaderFrameDecoder: ByteToMessageDecoder { /// We're emitting one `ByteBuffer` corresponding exactly to one full payload, no headers etc. @@ -60,15 +69,15 @@ extension NIOJSONRPCFraming { /// `ContentLengthHeaderFrameDecoder` is a simple state machine. private enum State { - /// either we're waiting for the end of the header block or a new header field, ... + /// Waiting for the end of the header block or a new header field case waitingForHeaderNameOrHeaderBlockEnd - /// ... or for a header value, or ... + /// Waiting for a header value case waitingForHeaderValue(name: String) - /// ... or for the payload of a given size. + /// Waiting for the payload of a given size. case waitingForPayload(length: Int) } - /// A `DecodingError` is sent through the pipeline if anything went wrong. + /// A ``DecodingError`` is sent through the pipeline if anything went wrong. public enum DecodingError: Error, Equatable { /// Missing 'Content-Length' header. case missingContentLengthHeader @@ -106,7 +115,12 @@ extension NIOJSONRPCFraming { } } - // `decode` will be invoked whenever there is more data available (or if we return `.continue`). + /// Decode the data in the supplied `buffer`. + /// `decode` will be invoked whenever there is more data available (or if we return `.continue`). + /// - parameters: + /// - context: Calling context. + /// - buffer: The data to decode. + /// - returns: Status describing need for more data or otherwise. public mutating func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { switch self.state { case .waitingForHeaderNameOrHeaderBlockEnd: @@ -166,7 +180,14 @@ extension NIOJSONRPCFraming { } } - /// Invoked when the `Channel` is being brough down. + /// Decode all remaining data. + /// Invoked when the `Channel` is being brought down. + /// Reports error through `ByteToMessageDecoderError.leftoverDataWhenDone` if not all data is consumed. + /// - parameters: + /// - context: Calling context. + /// - buffer: Buffer of data to decode. + /// - seenEOF: If the end of file has been seen. + /// - returns: .needMoreData always as all data should be consumed. public mutating func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { diff --git a/Sources/NIOExtras/JSONRPCFraming.swift b/Sources/NIOExtras/JSONRPCFraming.swift index 2ba0c114..37099dcb 100644 --- a/Sources/NIOExtras/JSONRPCFraming.swift +++ b/Sources/NIOExtras/JSONRPCFraming.swift @@ -12,6 +12,7 @@ // //===----------------------------------------------------------------------===// +/// Namespace to contain JSON framing implementation. public enum NIOJSONRPCFraming { // just a name-space } diff --git a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift index f461bce8..071591a5 100644 --- a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift @@ -47,23 +47,25 @@ extension ByteBuffer { } public enum NIOLengthFieldBasedFrameDecoderError: Error { - /// This error can be thrown by `LengthFieldBasedFrameDecoder` if the length field value is larger than `Int.max` + /// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `Int.max` case lengthFieldValueTooLarge - /// This error can be thrown by `LengthFieldBasedFrameDecoder` if the length field value is larger than `LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize` + /// This error can be thrown by ``LengthFieldBasedFrameDecoder`` if the length field value is larger than `LengthFieldBasedFrameDecoder.maxSupportedLengthFieldSize` case lengthFieldValueLargerThanMaxSupportedSize } /// /// A decoder that splits the received `ByteBuffer` by the number of bytes specified in a fixed length header /// contained within the buffer. +/// /// For example, if you received the following four fragmented packets: +/// /// +---+----+------+----+ /// | A | BC | DEFG | HI | /// +---+----+------+----+ /// /// Given that the specified header length is 1 byte, /// where the first header specifies 3 bytes while the second header specifies 4 bytes, -/// a `LengthFieldBasedFrameDecoder` will decode them into the following packets: +/// a ``LengthFieldBasedFrameDecoder`` will decode them into the following packets: /// /// +-----+------+ /// | BCD | FGHI | @@ -72,15 +74,18 @@ public enum NIOLengthFieldBasedFrameDecoderError: Error { /// 'A' and 'E' will be the headers and will not be passed forward. /// public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { - /// Maximum supported length field size in bytes of `LengthFieldBasedFrameDecoder` and is currently `Int32.max` + /// Maximum supported length field size in bytes of ``LengthFieldBasedFrameDecoder`` and is currently `Int32.max` public static let maxSupportedLengthFieldSize: Int = Int(Int32.max) - /// + /// An enumeration to describe the length of a piece of data in bytes. - /// public enum ByteLength { + /// One byte case one + /// Two bytes case two + /// Four bytes case four + /// Eight bytes case eight fileprivate var bitLength: NIOLengthFieldBitLength { @@ -92,18 +97,20 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { } } } - - /// + /// The decoder has two distinct sections of data to read. /// Each must be fully present before it is considered as read. - /// During the time when it is not present the decoder must wait. `DecoderReadState` details that waiting state. - /// + /// During the time when it is not present the decoder must wait. ``DecoderReadState`` details that waiting state. private enum DecoderReadState { + // Expending a header next. case waitingForHeader + // Expecting the frame next. case waitingForFrame(length: Int) } + /// Incoming data is in `ByteBuffer` public typealias InboundIn = ByteBuffer + /// `ByteBuffer` is type passed to next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -118,7 +125,6 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { /// - parameters: /// - lengthFieldLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public convenience init(lengthFieldLength: ByteLength, lengthFieldEndianness: Endianness = .big) { self.init(lengthFieldBitLength: lengthFieldLength.bitLength, lengthFieldEndianness: lengthFieldEndianness) } @@ -128,12 +134,16 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { /// - parameters: /// - lengthFieldBitLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public init(lengthFieldBitLength: NIOLengthFieldBitLength, lengthFieldEndianness: Endianness = .big) { self.lengthFieldLength = lengthFieldBitLength self.lengthFieldEndianness = lengthFieldEndianness } - + + /// Decode supplied data. + /// - Parameters: + /// - context: Calling context. + /// - buffer: data to decode. + /// - Returns: `DecodingState` describing what's needed next. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { if case .waitingForHeader = self.readState { @@ -152,7 +162,14 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return .continue } - + + /// Decode all data supplied. No more is expected after this. + /// If all data is not exactly consumed reports and error through `context.fireErrorCaught` + /// - Parameters: + /// - context: Calling context. + /// - buffer: The data to decode + /// - seenEOF: If End of File has been seen. + /// - Returns: .needMoreData always as all data has been consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { // we'll just try to decode as much as we can as usually while case .continue = try self.decode(context: context, buffer: &buffer) {} @@ -162,12 +179,10 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return .needMoreData } - /// /// Attempts to read the header data. Updates the status is successful. /// /// - parameters: /// - buffer: The buffer containing the integer frame length. - /// private func readNextLengthFieldToState(buffer: inout ByteBuffer) throws { // Convert the length field to an integer specifying the length @@ -177,14 +192,12 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { self.readState = .waitingForFrame(length: lengthFieldValue) } - - /// + /// Attempts to read the body data for a given length. Updates the status is successful. /// /// - parameters: /// - buffer: The buffer containing the frame data. /// - frameLength: The length of the frame data to be read. - /// private func readNextFrame(buffer: inout ByteBuffer, frameLength: Int) throws -> ByteBuffer? { guard let contentsFieldSlice = buffer.readSlice(length: frameLength) else { @@ -196,13 +209,11 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return contentsFieldSlice } - /// /// Decodes the specified region of the buffer into an unadjusted frame length. The default implementation is /// capable of decoding the specified region into an unsigned 8/16/24/32/64 bit integer. /// /// - parameters: /// - buffer: The buffer containing the integer frame length. - /// private func readFrameLength(for buffer: inout ByteBuffer) throws -> Int? { let frameLength: Int? switch self.lengthFieldLength.bitLength { diff --git a/Sources/NIOExtras/LengthFieldPrepender.swift b/Sources/NIOExtras/LengthFieldPrepender.swift index 62423661..37d797ca 100644 --- a/Sources/NIOExtras/LengthFieldPrepender.swift +++ b/Sources/NIOExtras/LengthFieldPrepender.swift @@ -34,30 +34,35 @@ extension ByteBuffer { } +/// Error types from ``LengthFieldPrepender`` public enum LengthFieldPrependerError: Error { + /// More data was given than the maximum encodable length value. case messageDataTooLongForLengthField } -/// /// An encoder that takes a `ByteBuffer` message and prepends the number of bytes in the message. /// The length field is always the same fixed length specified on construction. /// These bytes contain a binary specification of the message size. /// /// For example, if you received a packet with the 3 byte length (BCD)... /// Given that the specified header length is 1 byte, there would be a single byte prepended which contains the number 3 +/// /// +---+-----+ /// | A | BCD | ('A' contains 0x03) /// +---+-----+ +/// /// This initial prepended byte is called the 'length field'. /// public final class LengthFieldPrepender: ChannelOutboundHandler { - /// /// An enumeration to describe the length of a piece of data in bytes. - /// public enum ByteLength { + /// One byte case one + /// Two bytes case two + /// Four bytes case four + /// Eight bytes case eight fileprivate var bitLength: NIOLengthFieldBitLength { @@ -70,7 +75,9 @@ public final class LengthFieldPrepender: ChannelOutboundHandler { } } + /// `ByteBuffer` is the expected type to be given for encoding. public typealias OutboundIn = ByteBuffer + /// Encoded output is passed in a `ByteBuffer` public typealias OutboundOut = ByteBuffer private let lengthFieldLength: NIOLengthFieldBitLength @@ -78,15 +85,19 @@ public final class LengthFieldPrepender: ChannelOutboundHandler { private var lengthBuffer: ByteBuffer? - /// Create `LengthFieldPrepender` with a given length field length. + /// Create ``LengthFieldPrepender`` with a given length field length. /// /// - parameters: /// - lengthFieldLength: The length of the field specifying the remaining length of the frame. /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. - /// public convenience init(lengthFieldLength: ByteLength, lengthFieldEndianness: Endianness = .big) { self.init(lengthFieldBitLength: lengthFieldLength.bitLength, lengthFieldEndianness: lengthFieldEndianness) } + + /// Create ``LengthFieldPrepender`` with a given length field length. + /// - parameters: + /// - lengthFieldBitLength: The length of the field specifying the remaining length of the frame. + /// - lengthFieldEndianness: The endianness of the field specifying the remaining length of the frame. public init(lengthFieldBitLength: NIOLengthFieldBitLength, lengthFieldEndianness: Endianness = .big) { // The value contained in the length field must be able to be represented by an integer type on the platform. // ie. .eight == 64bit which would not fit into the Int type on a 32bit platform. diff --git a/Sources/NIOExtras/LineBasedFrameDecoder.swift b/Sources/NIOExtras/LineBasedFrameDecoder.swift index d0bb8544..fd7a809c 100644 --- a/Sources/NIOExtras/LineBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LineBasedFrameDecoder.swift @@ -23,7 +23,7 @@ import NIOCore /// | AB | C\nDE | F\r\nGHI\n | /// +----+-------+------------+ /// -/// A instance of `LineBasedFrameDecoder` will split this buffer +/// A instance of ``LineBasedFrameDecoder`` will split this buffer /// as follows: /// /// +-----+-----+-----+ @@ -31,7 +31,9 @@ import NIOCore /// +-----+-----+-----+ /// public class LineBasedFrameDecoder: ByteToMessageDecoder { + /// `ByteBuffer` is the expected type passed in. public typealias InboundIn = ByteBuffer + /// `ByteBuffer`s will be passed to the next stage. public typealias InboundOut = ByteBuffer @available(*, deprecated, message: "No longer used") @@ -40,7 +42,12 @@ public class LineBasedFrameDecoder: ByteToMessageDecoder { private var lastScanOffset = 0 public init() { } - + + /// Decode data in the supplied buffer. + /// - Parameters: + /// - context: Calling cotext + /// - buffer: Buffer containing data to decode. + /// - Returns: State describing if more data is required. public func decode(context: ChannelHandlerContext, buffer: inout ByteBuffer) throws -> DecodingState { if let frame = try self.findNextFrame(buffer: &buffer) { context.fireChannelRead(wrapInboundOut(frame)) @@ -49,7 +56,14 @@ public class LineBasedFrameDecoder: ByteToMessageDecoder { return .needMoreData } } - + + /// Decode all remaining data. + /// If it is not possible to consume all the data then ``NIOExtrasErrors/LeftOverBytesError`` is reported via `context.fireErrorCaught` + /// - Parameters: + /// - context: Calling context. + /// - buffer: Buffer containing the data to decode. + /// - seenEOF: Has end of file been seen. + /// - Returns: Always .needMoreData as all data will be consumed. public func decodeLast(context: ChannelHandlerContext, buffer: inout ByteBuffer, seenEOF: Bool) throws -> DecodingState { while try self.decode(context: context, buffer: &buffer) == .continue {} if buffer.readableBytes > 0 { diff --git a/Sources/NIOExtras/NIOExtrasError.swift b/Sources/NIOExtras/NIOExtrasError.swift index 8c88b1ba..9750f563 100644 --- a/Sources/NIOExtras/NIOExtrasError.swift +++ b/Sources/NIOExtras/NIOExtrasError.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import NIOCore +/// Base type for errors from NIOExtras public protocol NIOExtrasError: Equatable, Error { } /// Errors that are raised in NIOExtras. diff --git a/Sources/NIOExtras/NIOLengthFieldBitLength.swift b/Sources/NIOExtras/NIOLengthFieldBitLength.swift index 38978b79..a41ab117 100644 --- a/Sources/NIOExtras/NIOLengthFieldBitLength.swift +++ b/Sources/NIOExtras/NIOLengthFieldBitLength.swift @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -/// An struct to describe the length of a piece of data in bits +/// A struct to describe the length of a piece of data in bits public struct NIOLengthFieldBitLength { internal enum Backing { case bits8 @@ -23,16 +23,26 @@ public struct NIOLengthFieldBitLength { } internal let bitLength: Backing + /// One byte - the same as ``eightBits`` public static let oneByte = NIOLengthFieldBitLength(bitLength: .bits8) + /// Two bytes - the same as ``sixteenBits`` public static let twoBytes = NIOLengthFieldBitLength(bitLength: .bits16) + /// Three bytes - the same as ``twentyFourBits`` public static let threeBytes = NIOLengthFieldBitLength(bitLength: .bits24) + /// Four bytes - the same as ``thirtyTwoBits`` public static let fourBytes = NIOLengthFieldBitLength(bitLength: .bits32) + /// Eight bytes - the same as ``sixtyFourBits`` public static let eightBytes = NIOLengthFieldBitLength(bitLength: .bits64) - + + /// Eight bits - the same as ``oneByte`` public static let eightBits = NIOLengthFieldBitLength(bitLength: .bits8) + /// Sixteen bits - the same as ``twoBytes`` public static let sixteenBits = NIOLengthFieldBitLength(bitLength: .bits16) + /// Twenty-four bits - the same as ``threeBytes`` public static let twentyFourBits = NIOLengthFieldBitLength(bitLength: .bits24) + /// Thirty-two bits - the same as ``fourBytes`` public static let thirtyTwoBits = NIOLengthFieldBitLength(bitLength: .bits32) + /// Sixty-four bits - the same as ``eightBytes`` public static let sixtyFourBits = NIOLengthFieldBitLength(bitLength: .bits64) internal var length: Int { diff --git a/Sources/NIOExtras/PCAPRingBuffer.swift b/Sources/NIOExtras/PCAPRingBuffer.swift index 3100f2c8..4273d28e 100644 --- a/Sources/NIOExtras/PCAPRingBuffer.swift +++ b/Sources/NIOExtras/PCAPRingBuffer.swift @@ -16,7 +16,7 @@ import NIOCore // MARK: NIOPCAPRingBuffer /// Storage for the most recent set of packets captured subject to constraints. -/// Use `addFragment` as the sink to a `NIOWritePCAPHandler` and call `emitPCAP` +/// Use ``addFragment(_:)`` as the sink to a ``NIOWritePCAPHandler`` and call ``emitPCAP()`` /// when you wish to get the recorded data. /// - Warning: This class is not thread safe so should only be called from one thread. public class NIOPCAPRingBuffer { diff --git a/Sources/NIOExtras/QuiescingHelper.swift b/Sources/NIOExtras/QuiescingHelper.swift index 193d394b..ea1ffec7 100644 --- a/Sources/NIOExtras/QuiescingHelper.swift +++ b/Sources/NIOExtras/QuiescingHelper.swift @@ -191,8 +191,8 @@ private final class CollectAcceptedChannelsHandler: ChannelInboundHandler { /// Helper that can be used to orchestrate the quiescing of a server `Channel` and all the child `Channel`s that are /// open at a given point in time. /// -/// `ServerQuiescingHelper` makes it easy to collect all child `Channel`s that a given server `Channel` accepts. When -/// the quiescing period starts (that is when `ServerQuiescingHelper.initiateShutdown` is invoked), it will perform the +/// ``ServerQuiescingHelper`` makes it easy to collect all child `Channel`s that a given server `Channel` accepts. When +/// the quiescing period starts (that is when ``initiateShutdown(promise:)`` is invoked), it will perform the /// following actions: /// /// 1. close the server `Channel` so no further connections get accepted @@ -240,8 +240,9 @@ public final class ServerQuiescingHelper { return CollectAcceptedChannelsHandler(channelCollector: collector) } - /// Initiate the shutdown. The following actions will be performed: + /// Initiate the shutdown. /// + /// The following actions will be performed: /// 1. close the server `Channel` so no further connections get accepted /// 2. send a `ChannelShouldQuiesceEvent` user event to all currently still open child `Channel`s /// 3. after all previously open child `Channel`s have closed, notify `promise` diff --git a/Sources/NIOExtras/RequestResponseHandler.swift b/Sources/NIOExtras/RequestResponseHandler.swift index 011e58eb..907ce2ec 100644 --- a/Sources/NIOExtras/RequestResponseHandler.swift +++ b/Sources/NIOExtras/RequestResponseHandler.swift @@ -14,21 +14,25 @@ import NIOCore -/// `RequestResponseHandler` receives a `Request` alongside an `EventLoopPromise` from the `Channel`'s +/// ``RequestResponseHandler`` receives a `Request` alongside an `EventLoopPromise` from the `Channel`'s /// outbound side. It will fulfill the promise with the `Response` once it's received from the `Channel`'s inbound /// side. /// -/// `RequestResponseHandler` does support pipelining `Request`s and it will send them pipelined further down the -/// `Channel`. Should `RequestResponseHandler` receive an error from the `Channel`, it will fail all promises meant for +/// ``RequestResponseHandler`` does support pipelining `Request`s and it will send them pipelined further down the +/// `Channel`. Should ``RequestResponseHandler`` receive an error from the `Channel`, it will fail all promises meant for /// the outstanding `Reponse`s and close the `Channel`. All requests enqueued after an error occured will be immediately /// failed with the first error the channel received. /// -/// `RequestResponseHandler` requires that the `Response`s arrive on `Channel` in the same order as the `Request`s +/// ``RequestResponseHandler`` requires that the `Response`s arrive on `Channel` in the same order as the `Request`s /// were submitted. public final class RequestResponseHandler: ChannelDuplexHandler { + /// `Response` is the type this class expects to receive inbound. public typealias InboundIn = Response + /// Don't expect to pass anything on in-bound. public typealias InboundOut = Never + /// Type this class expect to receive in an outbound direction. public typealias OutboundIn = (Request, EventLoopPromise) + /// Type this class passes out. public typealias OutboundOut = Request private enum State { @@ -49,10 +53,10 @@ public final class RequestResponseHandler: ChannelDuplexHandl private var promiseBuffer: CircularBuffer> - /// Create a new `RequestResponseHandler`. + /// Create a new ``RequestResponseHandler``. /// /// - parameters: - /// - initialBufferCapacity: `RequestResponseHandler` saves the promises for all outstanding responses in a + /// - initialBufferCapacity: ``RequestResponseHandler`` saves the promises for all outstanding responses in a /// buffer. `initialBufferCapacity` is the initial capacity for this buffer. You usually do not need to set /// this parameter unless you intend to pipeline very deeply and don't want the buffer to resize. public init(initialBufferCapacity: Int = 4) { diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index b457a231..8ace0bb8 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -119,7 +119,7 @@ struct PCAPRecordHeader { /// example when your real network traffic is TLS protected (so `tcpdump`/Wireshark can't read it directly), or if you /// don't have enough privileges on the running host to dump the network traffic. /// -/// `NIOWritePCAPHandler` will also work with Unix Domain Sockets in which case it will still synthesize a TCP packet +/// ``NIOWritePCAPHandler`` will also work with Unix Domain Sockets in which case it will still synthesize a TCP packet /// capture with local address `111.111.111.111` (port `1111`) and remote address `222.222.222.222` (port `2222`). public class NIOWritePCAPHandler: RemovableChannelHandler { public enum Mode { @@ -127,21 +127,21 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { case server } - /// Settings for `NIOWritePCAPHandler`. + /// Settings for ``NIOWritePCAPHandler``. public struct Settings { /// When to issue data into the `.pcap` file. public enum EmitPCAP { - /// Write the data immediately when `NIOWritePCAPHandler` saw the event on the `ChannelPipeline`. + /// Write the data immediately when ``NIOWritePCAPHandler`` saw the event on the `ChannelPipeline`. /// /// For writes this means when the `write` event is triggered. Please note that this will write potentially /// unflushed data into the `.pcap` file. /// - /// If in doubt, prefer `.whenCompleted`. + /// If in doubt, prefer ``whenCompleted``. case whenIssued /// Write the data when the event completed. /// - /// For writes this means when the `write` promise is succeeded. The `whenCompleted` mode mirrors most + /// For writes this means when the `write` promise is succeeded. The ``whenCompleted`` mode mirrors most /// closely what's actually sent over the wire. case whenCompleted } @@ -149,7 +149,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { /// When to emit the data from the `write` event into the `.pcap` file. public var emitPCAPWrites: EmitPCAP - /// Default settings for the `NIOWritePCAPHandler`. + /// Default settings for the ``NIOWritePCAPHandler``. public init() { self = .init(emitPCAPWrites: .whenCompleted) } @@ -157,7 +157,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { /// Settings with customization. /// /// - parameters: - /// - emitPCAPWrites: When to issue the writes into the `.pcap` file, see `EmitPCAP`. + /// - emitPCAPWrites: When to issue the writes into the `.pcap` file, see ``EmitPCAP``. public init(emitPCAPWrites: EmitPCAP) { self.emitPCAPWrites = emitPCAPWrites } @@ -184,20 +184,21 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { private var localAddress: SocketAddress? private var remoteAddress: SocketAddress? + /// Reusable header for `. pcap` file. public static var pcapFileHeader: ByteBuffer { var buffer = ByteBufferAllocator().buffer(capacity: 24) buffer.writePCAPHeader() return buffer } - /// Initialize a `NIOWritePCAPHandler`. + /// Initialize a ``NIOWritePCAPHandler``. /// /// - parameters: /// - fakeLocalAddress: Allows you to optionally override the local address to be different from the real one. /// - fakeRemoteAddress: Allows you to optionally override the remote address to be different from the real one. - /// - settings: The settings for the `NIOWritePCAPHandler`. + /// - settings: The settings for the ``NIOWritePCAPHandler``. /// - fileSink: The `fileSink` closure is called every time a new chunk of the `.pcap` file is ready to be - /// written to disk or elsewhere. See `SynchronizedFileSink` for a convenient way to write to + /// written to disk or elsewhere. See ``SynchronizedFileSink`` for a convenient way to write to /// disk. public init(mode: Mode, fakeLocalAddress: SocketAddress? = nil, @@ -215,7 +216,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { } } - /// Initialize a `NIOWritePCAPHandler` with default settings. + /// Initialize a ``NIOWritePCAPHandler`` with default settings. /// /// - parameters: /// - fakeLocalAddress: Allows you to optionally override the local address to be different from the real one. diff --git a/Sources/NIOHTTPCompression/HTTPCompression.swift b/Sources/NIOHTTPCompression/HTTPCompression.swift index 9a30f94e..8eb9b867 100644 --- a/Sources/NIOHTTPCompression/HTTPCompression.swift +++ b/Sources/NIOHTTPCompression/HTTPCompression.swift @@ -15,8 +15,10 @@ import CNIOExtrasZlib import NIOCore +/// Namespace for compression code. public enum NIOCompression { + /// Which algorithm should be used for compression. public struct Algorithm: CustomStringConvertible, Equatable { fileprivate enum AlgorithmEnum: String { case gzip @@ -26,11 +28,14 @@ public enum NIOCompression { /// return as String public var description: String { return algorithm.rawValue } - + + /// `gzip` method public static let gzip = Algorithm(algorithm: .gzip) + /// `deflate` method public static let deflate = Algorithm(algorithm: .deflate) } - + + /// Error types for ``NIOCompression`` public struct Error: Swift.Error, CustomStringConvertible, Equatable { fileprivate enum ErrorEnum: String { case uncompressedWritesPending @@ -40,11 +45,14 @@ public enum NIOCompression { /// return as String public var description: String { return error.rawValue } - + + /// There were writes pending which were not processed before shutdown. public static let uncompressedWritesPending = Error(error: .uncompressedWritesPending) + /// Currently never used. public static let noDataToWrite = Error(error: .noDataToWrite) } - + + /// Data compression utility. struct Compressor { private var stream = z_stream() var isActive = false diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index c030fa1b..9bf6b51a 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -46,9 +46,13 @@ public enum NIOHTTPDecompression { } } + /// Error types for ``NIOHTTPCompression`` public enum DecompressionError: Error, Equatable { + /// The set ``NIOHTTPDecompression/DecompressionLimit`` has been exceeded case limit + /// An error occured when inflating. Error code is included to aid diagnosis. case inflationError(Int) + /// Decoder could not be initialised. Error code is included to aid diagnosis. case initializationError(Int) } diff --git a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift index 555afe74..52d262da 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift @@ -16,7 +16,7 @@ import CNIOExtrasZlib import NIOCore import NIOHTTP1 -/// NIOHTTPResponseCompressor is an outbound channel handler that handles automatic streaming compression of +/// ``NIOHTTPRequestCompressor`` is an outbound channel handler that handles automatic streaming compression of /// HTTP requests. /// /// This compressor supports gzip and deflate. It works best if many writes are made between flushes. @@ -27,7 +27,9 @@ import NIOHTTP1 /// benefit. This channel handler should be present in the pipeline only for dynamically-generated and /// highly-compressible content, which will see the biggest benefits from streaming compression. public final class NIOHTTPRequestCompressor: ChannelOutboundHandler, RemovableChannelHandler { + /// Class takes `HTTPClientRequestPart` as the type to send. public typealias OutboundIn = HTTPClientRequestPart + /// Passes `HTTPClientRequestPart` to the next stage in the pipeline when sending. public typealias OutboundOut = HTTPClientRequestPart /// Handler state @@ -53,18 +55,22 @@ public final class NIOHTTPRequestCompressor: ChannelOutboundHandler, RemovableCh /// pending write promise var pendingWritePromise: EventLoopPromise! - /// Initialize a NIOHTTPRequestCompressor + /// Initialize a ``NIOHTTPRequestCompressor`` /// - Parameter encoding: Compression algorithm to use public init(encoding: NIOCompression.Algorithm) { self.encoding = encoding self.state = .idle self.compressor = NIOCompression.Compressor() } - + + /// Add handler to the pipeline. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { pendingWritePromise = context.eventLoop.makePromise() } + /// Remove handler from pipeline. + /// - Parameter context: Calling context. public func handlerRemoved(context: ChannelHandlerContext) { pendingWritePromise.fail(NIOCompression.Error.uncompressedWritesPending) compressor.shutdownIfActive() diff --git a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift index 368b7118..cf8d6721 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift @@ -16,10 +16,15 @@ import CNIOExtrasZlib import NIOHTTP1 import NIOCore +/// Channel hander to decompress incoming HTTP data. public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// Expect to receive `HTTPServerRequestPart` from the network public typealias InboundIn = HTTPServerRequestPart + /// Pass `HTTPServerRequestPart` to the next pipeline state in an inbound direction. public typealias InboundOut = HTTPServerRequestPart + /// Pass through `HTTPServerResponsePart` outbound. public typealias OutboundIn = HTTPServerResponsePart + /// Pass through `HTTPServerResponsePart` outbound. public typealias OutboundOut = HTTPServerResponsePart private struct Compression { @@ -30,6 +35,8 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh private var decompressor: NIOHTTPDecompression.Decompressor private var compression: Compression? + /// Initialise with limits. + /// - Parameter limit: Limit to how much inflation can occur to protect against bad cases. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) self.compression = nil diff --git a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift index dcfb7ac4..614b1aff 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift @@ -46,7 +46,7 @@ private func qValueFromHeader(_ text: S) -> Float { return qValue } -/// A HTTPResponseCompressor is a duplex channel handler that handles automatic streaming compression of +/// A ``HTTPResponseCompressor`` is a duplex channel handler that handles automatic streaming compression of /// HTTP responses. It respects the client's Accept-Encoding preferences, including q-values if present, /// and ensures that clients are served the compression algorithm that works best for them. /// @@ -58,14 +58,20 @@ private func qValueFromHeader(_ text: S) -> Float { /// benefit. This channel handler should be present in the pipeline only for dynamically-generated and /// highly-compressible content, which will see the biggest benefits from streaming compression. public final class HTTPResponseCompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// This class accepts `HTTPServerRequestPart` inbound public typealias InboundIn = HTTPServerRequestPart + /// This class emits `HTTPServerRequestPart` inbound. public typealias InboundOut = HTTPServerRequestPart + /// This class accepts `HTTPServerResponsePart` outbound, public typealias OutboundIn = HTTPServerResponsePart + /// This class emits `HTTPServerResponsePart` outbound. public typealias OutboundOut = HTTPServerResponsePart - + /// Errors which can occur when compressing public enum CompressionError: Error { + // Writes were still pending when shutdown. case uncompressedWritesPending + /// Data was somehow lost without being written. case noDataToWrite } @@ -79,16 +85,22 @@ public final class HTTPResponseCompressor: ChannelDuplexHandler, RemovableChanne private let initialByteBufferCapacity: Int + /// Initialise a ``HTTPResponseCompressor`` + /// - Parameter initialByteBufferCapacity: Initial size of buffer to allocate when hander is first added. public init(initialByteBufferCapacity: Int = 1024) { self.initialByteBufferCapacity = initialByteBufferCapacity self.compressor = NIOCompression.Compressor() } + /// Setup and add to the pipeline. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { pendingResponse = PartialHTTPResponse(bodyBuffer: context.channel.allocator.buffer(capacity: initialByteBufferCapacity)) pendingWritePromise = context.eventLoop.makePromise() } + /// Remove channel handler from the pipeline. + /// - Parameter context: Calling context public func handlerRemoved(context: ChannelHandlerContext) { pendingWritePromise?.fail(CompressionError.uncompressedWritesPending) compressor.shutdownIfActive() diff --git a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift index 639215f7..4c90a8de 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift @@ -15,10 +15,15 @@ import NIOCore import NIOHTTP1 +/// Duplex channel handler which will accept deflate and gzip encoded responses and decompress them. public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableChannelHandler { + /// Expect `HTTPClientResponsePart` inbound. public typealias InboundIn = HTTPClientResponsePart + /// Sends `HTTPClientResponsePart` to the next pipeline stage inbound. public typealias InboundOut = HTTPClientResponsePart + /// Expect `HTTPClientRequestPart` outbound. public typealias OutboundIn = HTTPClientRequestPart + /// Send `HTTPClientRequestPart` to the next stage outbound. public typealias OutboundOut = HTTPClientRequestPart /// this struct encapsulates the state of a single http response decompression @@ -34,6 +39,8 @@ public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableC private var compression: Compression? = nil private var decompressor: NIOHTTPDecompression.Decompressor + /// Initialise + /// - Parameter limit: Limit on the amount of decompression allowed. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) } diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift index 05e6a9c0..ad3994d6 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift @@ -19,10 +19,13 @@ import NIOCore /// channel's pipeline. Note that SOCKS only supports fully-qualified /// domain names and IPv4 or IPv6 sockets, and not UNIX sockets. public final class SOCKSClientHandler: ChannelDuplexHandler { - + /// Accepts `ByteBuffer` as input where receiving. public typealias InboundIn = ByteBuffer + /// Sends `ByteBuffer` to the next pipeline stage when receiving. public typealias InboundOut = ByteBuffer + /// Accepts `ByteBuffer` as the type to send. public typealias OutboundIn = ByteBuffer + /// Sends `ByteBuffer` to the next outbound stage. public typealias OutboundOut = ByteBuffer private let targetAddress: SOCKSAddress @@ -33,7 +36,7 @@ public final class SOCKSClientHandler: ChannelDuplexHandler { private var bufferedWrites: MarkedCircularBuffer<(NIOAny, EventLoopPromise?)> = .init(initialCapacity: 8) - /// Creates a new `SOCKSClientHandler` that connects to a server + /// Creates a new ``SOCKSClientHandler`` that connects to a server /// and instructs the server to connect to `targetAddress`. /// - parameter targetAddress: The desired end point - note that only IPv4, IPv6, and FQDNs are supported. public init(targetAddress: SOCKSAddress) { @@ -52,7 +55,9 @@ public final class SOCKSClientHandler: ChannelDuplexHandler { public func channelActive(context: ChannelHandlerContext) { self.beginHandshake(context: context) } - + + /// Add handler to pipeline and start handshake. + /// - Parameter context: Calling context. public func handlerAdded(context: ChannelHandlerContext) { self.beginHandshake(context: context) } diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift index b5b455a6..537c6155 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift @@ -17,13 +17,16 @@ import NIOCore /// Add this handshake handler to the front of your channel, closest to the network. /// The handler will receive bytes from the network and run them through a state machine /// and parser to enforce SOCKSv5 protocol correctness. Inbound bytes will by parsed into -/// `ClientMessage` for downstream consumption. Send `ServerMessage` to this +/// ``ClientMessage`` for downstream consumption. Send ``ServerMessage`` to this /// handler. public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableChannelHandler { - + /// Accepts `ByteBuffer` when receiving data. public typealias InboundIn = ByteBuffer + /// Passes `ClientMessage` to the next stage of the pipeline when receiving data. public typealias InboundOut = ClientMessage + /// Accepts `ServerMessage` when sending data. public typealias OutboundIn = ServerMessage + /// Passes `ByteBuffer` to the next pipeline stage when sending data. public typealias OutboundOut = ByteBuffer var inboundBuffer: ByteBuffer? @@ -52,7 +55,9 @@ public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableC context.fireErrorCaught(error) } } - + + /// Add hander to pipeline and enter state ready for connection establishment. + /// - Parameter context: Calling context public func handlerAdded(context: ChannelHandlerContext) { do { try self.stateMachine.connectionEstablished() @@ -60,7 +65,9 @@ public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableC context.fireErrorCaught(error) } } - + + /// Remove handler from channel pipeline. Causes any inbound buffer to be surfaced. + /// - Parameter context: Calling context. public func handlerRemoved(context: ChannelHandlerContext) { guard let buffer = self.inboundBuffer else { return diff --git a/Sources/NIOSOCKS/Messages/ClientGreeting.swift b/Sources/NIOSOCKS/Messages/ClientGreeting.swift index 7f621735..51862134 100644 --- a/Sources/NIOSOCKS/Messages/ClientGreeting.swift +++ b/Sources/NIOSOCKS/Messages/ClientGreeting.swift @@ -26,7 +26,7 @@ public struct ClientGreeting: Hashable { /// The SOCKS server will select one to use. public var methods: [AuthenticationMethod] - /// Creates a new `ClientGreeting` + /// Creates a new ``ClientGreeting`` /// - parameter methods: The client-supported authentication methods. public init(methods: [AuthenticationMethod]) { self.methods = methods diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index 4e622323..37e55e3b 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -35,7 +35,7 @@ public struct SOCKSRequest: Hashable { /// The target host address. public var addressType: SOCKSAddress - /// Creates a new `SOCKSRequest`. + /// Creates a new ``SOCKSRequest``. /// - parameter command: How to connect to the host. /// - parameter addressType: The target host address. public init(command: SOCKSCommand, addressType: SOCKSAddress) { @@ -87,9 +87,10 @@ public struct SOCKSCommand: Hashable { /// Used to establish an association within the UDP relay process to /// handle UDP datagrams. public static let udpAssociate = SOCKSCommand(value: 0x03) - + + /// Command value as defined in RFC public var value: UInt8 - + public init(value: UInt8) { self.value = value } @@ -99,9 +100,9 @@ public struct SOCKSCommand: Hashable { /// The address used to connect to the target host. public enum SOCKSAddress: Hashable { - + /// Socket Adress case address(SocketAddress) - + /// Host and port case domain(String, port: Int) static let ipv4IdentifierByte: UInt8 = 0x01 diff --git a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift index 9a23b543..d8b244bb 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift @@ -30,7 +30,7 @@ public struct SOCKSResponse: Hashable { /// The host address. public var boundAddress: SOCKSAddress - /// Creates a new `SOCKSResponse`. + /// Creates a new ``SOCKSResponse``. /// - parameter reply: The status of the connection - used to check if the request /// succeeded or failed. /// - parameter boundAddress: The host address. diff --git a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift index 0f3729e5..1d94d0f6 100644 --- a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift @@ -25,7 +25,7 @@ public struct SelectedAuthenticationMethod: Hashable { /// The server's selected authentication method. public var method: AuthenticationMethod - /// Creates a new `MethodSelection` wrapping an `AuthenticationMethod`. + /// Creates a new `MethodSelection` wrapping an ``AuthenticationMethod``. /// - parameter method: The selected `AuthenticationMethod`. public init(method: AuthenticationMethod) { self.method = method From dabef818d3c158ce47eba3540b8a9da0fb31e6da Mon Sep 17 00:00:00 2001 From: Peter Adams <63288215+PeterAdams-A@users.noreply.github.com> Date: Fri, 12 Aug 2022 15:31:17 +0100 Subject: [PATCH 10/22] Simple index pages for docc (#170) Motivation: An index page ties all the other documentation together Modifications: Add index pages for the library targets. Correct a few minor errors in the main docs. Result: A more joined up documentation experience. --- Sources/NIOExtras/Docs.docc/Article.md | 13 ------ Sources/NIOExtras/Docs.docc/index.md | 43 +++++++++++++++++++ Sources/NIOExtras/WritePCAPHandler.swift | 2 +- Sources/NIOHTTPCompression/Docs.docc/index.md | 27 ++++++++++++ .../HTTPDecompression.swift | 1 + Sources/NIOSOCKS/Docs.docc/index.md | 35 +++++++++++++++ 6 files changed, 107 insertions(+), 14 deletions(-) delete mode 100644 Sources/NIOExtras/Docs.docc/Article.md create mode 100644 Sources/NIOExtras/Docs.docc/index.md create mode 100644 Sources/NIOHTTPCompression/Docs.docc/index.md create mode 100644 Sources/NIOSOCKS/Docs.docc/index.md diff --git a/Sources/NIOExtras/Docs.docc/Article.md b/Sources/NIOExtras/Docs.docc/Article.md deleted file mode 100644 index ed5c0a4a..00000000 --- a/Sources/NIOExtras/Docs.docc/Article.md +++ /dev/null @@ -1,13 +0,0 @@ -# Article - -Summary - -## Overview - -Text - -## Topics - -### Group - -- ``Symbol`` diff --git a/Sources/NIOExtras/Docs.docc/index.md b/Sources/NIOExtras/Docs.docc/index.md new file mode 100644 index 00000000..1790bb99 --- /dev/null +++ b/Sources/NIOExtras/Docs.docc/index.md @@ -0,0 +1,43 @@ +# ``NIOExtras`` + +A collection of helpful utilities to assist in building and debugging Swift-NIO based applications. + +## Overview + +A collection of helpful utilities to assist in building and debugging Swift-NIO based applications. Topics covered include packet capture, the logging of channel pipeline events, frame decoding of various common forms and helpful data types. + +Debugging aids include `ChannelHandler`s to log channel pipeline events both inbound and outbound; and a `ChannelHandler` to log data in packet capture format. + +To support encoding and decoding helpers are provided for data frames which have fixed length; are new line terminated; contain a length prefix; or are defined by a `context-length` header. + +To help simplify building a robust pipeline the ``ServerQuiescingHelper`` makes it easy to collect all child `Channel`s that a given server `Channel` accepts. + +Easy request response flows can be built using the ``RequestResponseHandler`` which takes a request and a promise which is fulfilled when an expected response is received. + +## Topics + +### Debugging Aids + +- ``DebugInboundEventsHandler`` +- ``DebugOutboundEventsHandler`` +- ``NIOWritePCAPHandler`` +- ``NIOPCAPRingBuffer`` + +### Encoding and Decoding + +- ``FixedLengthFrameDecoder`` +- ``NIOJSONRPCFraming`` +- ``LengthFieldBasedFrameDecoder`` +- ``NIOLengthFieldBasedFrameDecoderError`` +- ``LengthFieldPrepender`` +- ``LengthFieldPrependerError`` +- ``LineBasedFrameDecoder`` +- ``NIOExtrasErrors`` +- ``NIOExtrasError`` + +### Channel Pipeline Aids +- ``ServerQuiescingHelper`` +- ``RequestResponseHandler`` + +### Data Types +- ``NIOLengthFieldBitLength`` diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 8ace0bb8..92bcf4ec 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -184,7 +184,7 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { private var localAddress: SocketAddress? private var remoteAddress: SocketAddress? - /// Reusable header for `. pcap` file. + /// Reusable header for `.pcap` file. public static var pcapFileHeader: ByteBuffer { var buffer = ByteBufferAllocator().buffer(capacity: 24) buffer.writePCAPHeader() diff --git a/Sources/NIOHTTPCompression/Docs.docc/index.md b/Sources/NIOHTTPCompression/Docs.docc/index.md new file mode 100644 index 00000000..40067107 --- /dev/null +++ b/Sources/NIOHTTPCompression/Docs.docc/index.md @@ -0,0 +1,27 @@ +# ``NIOHTTPCompression`` + +Automatic compression and decompression of HTTP data. + +## Overview + +Channel handlers to support automatic compression and decompression of HTTP data. Add the handlers to your pipeline to support the features you need. + +`Content-Encoding`, `Content-Length`, and `accept-encoding` HTTP headers are set and respected where appropriate. + +Be aware that this works best if there is sufficient data written between flushes. This also performs compute on the event loop thread which could impact performance. + +## Topics + +### Client Channel Handlers + +- ``NIOHTTPRequestCompressor`` +- ``NIOHTTPResponseDecompressor`` + +### Server Channel Handlers +- ``NIOHTTPRequestDecompressor`` +- ``HTTPResponseCompressor`` + +### Compression Methods + +- ``NIOCompression`` +- ``NIOHTTPDecompression`` diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index 9bf6b51a..e4531d3a 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -15,6 +15,7 @@ import CNIOExtrasZlib import NIOCore +/// Namespace for decompression code. public enum NIOHTTPDecompression { /// Specifies how to limit decompression inflation. public struct DecompressionLimit { diff --git a/Sources/NIOSOCKS/Docs.docc/index.md b/Sources/NIOSOCKS/Docs.docc/index.md new file mode 100644 index 00000000..6b28057e --- /dev/null +++ b/Sources/NIOSOCKS/Docs.docc/index.md @@ -0,0 +1,35 @@ +# ``NIOSOCKS`` + +SOCKS v5 protocol implementation + +## Overview + +An implementation of SOCKS v5 protocol. See [RFC1928](https://www.rfc-editor.org/rfc/rfc1928). + +Add the appropriate channel handler to the start of your channel pipeline to use this protocol. + +For an example see the NIOSOCKSClient target. + +## Topics + +### Channel Handlers +- ``SOCKSClientHandler`` +- ``SOCKSServerHandshakeHandler`` + +### Client Messages +- ``ClientMessage`` +- ``ClientGreeting`` +- ``SOCKSRequest`` + +### Server Messages +- ``ServerMessage`` +- ``SelectedAuthenticationMethod`` +- ``SOCKSResponse`` + +### Supporting Types +- ``AuthenticationMethod`` +- ``SOCKSServerReply`` +- ``SOCKSCommand`` +- ``SOCKSAddress`` +- ``SOCKSProxyEstablishedEvent`` +- ``SOCKSError`` From 9697a611c41a9e3e146a5ce7c29f35a8786e940c Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Tue, 16 Aug 2022 09:19:37 +0100 Subject: [PATCH 11/22] Validate missing imports in CI (#171) --- docker/docker-compose.2004.main.yaml | 2 ++ docker/docker-compose.yaml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docker/docker-compose.2004.main.yaml b/docker/docker-compose.2004.main.yaml index a2621e87..6e9fe8c6 100644 --- a/docker/docker-compose.2004.main.yaml +++ b/docker/docker-compose.2004.main.yaml @@ -10,6 +10,8 @@ services: test: image: swift-nio-extras:20.04-main + environment: + - IMPORT_CHECK_ARG=--explicit-target-dependency-import-check error shell: image: swift-nio-extras:20.04-main diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 06b042be..2d33c549 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -28,7 +28,7 @@ services: test: <<: *common - command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors --enable-test-discovery $${SANITIZER_ARG-}" + command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors --enable-test-discovery $${SANITIZER_ARG-} $${IMPORT_CHECK_ARG-}" # util From 1ef46c03521d41107890a3a79548521e5cf681e7 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 23 Aug 2022 14:24:16 +0200 Subject: [PATCH 12/22] Adopt `Sendable` in `NIOHTTPCompression` (#172) --- Sources/NIOHTTPCompression/HTTPCompression.swift | 2 +- Sources/NIOHTTPCompression/HTTPDecompression.swift | 2 +- Sources/NIOHTTPCompression/HTTPRequestCompressor.swift | 4 ++++ Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift | 5 +++++ Sources/NIOHTTPCompression/HTTPResponseCompressor.swift | 6 ++++++ Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift | 5 +++++ 6 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/NIOHTTPCompression/HTTPCompression.swift b/Sources/NIOHTTPCompression/HTTPCompression.swift index 8eb9b867..9218b3ec 100644 --- a/Sources/NIOHTTPCompression/HTTPCompression.swift +++ b/Sources/NIOHTTPCompression/HTTPCompression.swift @@ -19,7 +19,7 @@ import NIOCore public enum NIOCompression { /// Which algorithm should be used for compression. - public struct Algorithm: CustomStringConvertible, Equatable { + public struct Algorithm: CustomStringConvertible, Equatable, NIOSendable { fileprivate enum AlgorithmEnum: String { case gzip case deflate diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index e4531d3a..d8b8cad8 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -18,7 +18,7 @@ import NIOCore /// Namespace for decompression code. public enum NIOHTTPDecompression { /// Specifies how to limit decompression inflation. - public struct DecompressionLimit { + public struct DecompressionLimit: NIOSendable { private enum Limit { case none case size(Int) diff --git a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift index 52d262da..81ab9fe9 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestCompressor.swift @@ -179,3 +179,7 @@ public final class NIOHTTPRequestCompressor: ChannelOutboundHandler, RemovableCh } } +#if swift(>=5.6) +@available(*, unavailable) +extension NIOHTTPRequestCompressor: Sendable {} +#endif diff --git a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift index cf8d6721..d3e52fa9 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift @@ -89,3 +89,8 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh } } } + +#if swift(>=5.6) +@available(*, unavailable) +extension NIOHTTPRequestDecompressor: Sendable {} +#endif diff --git a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift index 614b1aff..eee4ec93 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseCompressor.swift @@ -221,6 +221,12 @@ public final class HTTPResponseCompressor: ChannelDuplexHandler, RemovableChanne pendingWritePromise = context.eventLoop.makePromise() } } + +#if swift(>=5.6) +@available(*, unavailable) +extension HTTPResponseCompressor: Sendable {} +#endif + /// A buffer object that allows us to keep track of how much of a HTTP response we've seen before /// a flush. /// diff --git a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift index 4c90a8de..1e7442f3 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift @@ -105,3 +105,8 @@ public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableC } } } + +#if swift(>=5.6) +@available(*, unavailable) +extension NIOHTTPResponseDecompressor: Sendable {} +#endif From b64e59956e3ccbe1941c7b1d2f433d19e57833df Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 23 Aug 2022 14:28:01 +0200 Subject: [PATCH 13/22] Adopt `Sendable` in `NIOSOCKS` (#173) Incremental `Sendable` adoption. Co-authored-by: Cory Benfield --- Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift | 7 ++++++- .../Channel Handlers/SOCKSServerHandshakeHandler.swift | 5 +++++ Sources/NIOSOCKS/Messages/AuthenticationMethod.swift | 4 +++- Sources/NIOSOCKS/Messages/ClientGreeting.swift | 2 +- Sources/NIOSOCKS/Messages/Messages.swift | 4 ++-- Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 6 +++--- Sources/NIOSOCKS/Messages/SOCKSResponse.swift | 4 ++-- .../NIOSOCKS/Messages/SelectedAuthenticationMethod.swift | 2 +- 8 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift index ad3994d6..1890d952 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift @@ -114,6 +114,11 @@ public final class SOCKSClientHandler: ChannelDuplexHandler { } } +#if swift(>=5.6) +@available(*, unavailable) +extension SOCKSClientHandler: Sendable {} +#endif + extension SOCKSClientHandler { private func beginHandshake(context: ChannelHandlerContext) { @@ -207,7 +212,7 @@ extension SOCKSClientHandler: RemovableChannelHandler { /// A `Channel` user event that is sent when a SOCKS connection has been established /// /// After this event has been received it is save to remove the `SOCKSClientHandler` from the channel pipeline. -public struct SOCKSProxyEstablishedEvent { +public struct SOCKSProxyEstablishedEvent: NIOSendable { public init() { } } diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift index 537c6155..619a2947 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSServerHandshakeHandler.swift @@ -121,3 +121,8 @@ public final class SOCKSServerHandshakeHandler: ChannelDuplexHandler, RemovableC } } + +#if swift(>=5.6) +@available(*, unavailable) +extension SOCKSServerHandshakeHandler: Sendable {} +#endif diff --git a/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift index 29fff6fe..11687232 100644 --- a/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift @@ -12,8 +12,10 @@ // //===----------------------------------------------------------------------===// +import NIOCore + /// The SOCKS authentication method to use, defined in RFC 1928. -public struct AuthenticationMethod: Hashable { +public struct AuthenticationMethod: Hashable, NIOSendable { /// No authentication required public static let noneRequired = AuthenticationMethod(value: 0x00) diff --git a/Sources/NIOSOCKS/Messages/ClientGreeting.swift b/Sources/NIOSOCKS/Messages/ClientGreeting.swift index 51862134..5ddc3f82 100644 --- a/Sources/NIOSOCKS/Messages/ClientGreeting.swift +++ b/Sources/NIOSOCKS/Messages/ClientGreeting.swift @@ -17,7 +17,7 @@ import NIOCore /// Clients begin the SOCKS handshake process /// by providing an array of suggested authentication /// methods. -public struct ClientGreeting: Hashable { +public struct ClientGreeting: Hashable, NIOSendable { /// The protocol version. public let version: UInt8 = 5 diff --git a/Sources/NIOSOCKS/Messages/Messages.swift b/Sources/NIOSOCKS/Messages/Messages.swift index 6e2a496f..cb1bbec4 100644 --- a/Sources/NIOSOCKS/Messages/Messages.swift +++ b/Sources/NIOSOCKS/Messages/Messages.swift @@ -15,7 +15,7 @@ import NIOCore /// Sent by the client and received by the server. -public enum ClientMessage: Hashable { +public enum ClientMessage: Hashable, NIOSendable { /// Contains the proposed authentication methods. case greeting(ClientGreeting) @@ -28,7 +28,7 @@ public enum ClientMessage: Hashable { } /// Sent by the server and received by the client. -public enum ServerMessage: Hashable { +public enum ServerMessage: Hashable, NIOSendable { /// Used by the server to instruct the client of the authentication method to use. case selectedAuthenticationMethod(SelectedAuthenticationMethod) diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index 37e55e3b..e5e7ade0 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -24,7 +24,7 @@ import NIOCore /// Instructs the SOCKS proxy server of the target host, /// and how to connect. -public struct SOCKSRequest: Hashable { +public struct SOCKSRequest: Hashable, NIOSendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 @@ -75,7 +75,7 @@ extension ByteBuffer { /// What type of connection the SOCKS server should establish with /// the target host. -public struct SOCKSCommand: Hashable { +public struct SOCKSCommand: Hashable, NIOSendable { /// Typically the primary connection type, suitable for HTTP. public static let connect = SOCKSCommand(value: 0x01) @@ -99,7 +99,7 @@ public struct SOCKSCommand: Hashable { // MARK: - SOCKSAddress /// The address used to connect to the target host. -public enum SOCKSAddress: Hashable { +public enum SOCKSAddress: Hashable, NIOSendable { /// Socket Adress case address(SocketAddress) /// Host and port diff --git a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift index d8b244bb..a29be857 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift @@ -18,7 +18,7 @@ import NIOCore /// The SOCKS Server's response to the client's request /// indicating if the request succeeded or failed. -public struct SOCKSResponse: Hashable { +public struct SOCKSResponse: Hashable, NIOSendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 @@ -69,7 +69,7 @@ extension ByteBuffer { /// Used to indicate if the SOCKS client's connection request succeeded /// or failed. -public struct SOCKSServerReply: Hashable { +public struct SOCKSServerReply: Hashable, NIOSendable { /// The connection succeeded and data can now be transmitted. public static let succeeded = SOCKSServerReply(value: 0x00) diff --git a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift index 1d94d0f6..87fb4503 100644 --- a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift @@ -17,7 +17,7 @@ import NIOCore /// Used by the SOCKS server to inform the client which /// authentication method it would like to use out of those /// offered. -public struct SelectedAuthenticationMethod: Hashable { +public struct SelectedAuthenticationMethod: Hashable, NIOSendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 From 5334d949febb396a4e2e5235e9fbcd9c3c014bb3 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Tue, 23 Aug 2022 16:20:41 +0200 Subject: [PATCH 14/22] Adopt `Sendable` in `NIOExtras` (#174) Incremental `Sendable` adoption. Co-authored-by: Cory Benfield --- Sources/NIOExtras/DebugInboundEventsHandler.swift | 5 +++++ Sources/NIOExtras/DebugOutboundEventsHandler.swift | 5 +++++ Sources/NIOExtras/FixedLengthFrameDecoder.swift | 5 +++++ .../NIOExtras/JSONRPCFraming+ContentLengthHeader.swift | 10 ++++++++++ Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift | 5 +++++ Sources/NIOExtras/LengthFieldPrepender.swift | 5 +++++ Sources/NIOExtras/LineBasedFrameDecoder.swift | 5 +++++ Sources/NIOExtras/NIOLengthFieldBitLength.swift | 4 +++- Sources/NIOExtras/PCAPRingBuffer.swift | 5 +++++ Sources/NIOExtras/RequestResponseHandler.swift | 5 +++++ Sources/NIOExtras/WritePCAPHandler.swift | 5 +++++ 11 files changed, 58 insertions(+), 1 deletion(-) diff --git a/Sources/NIOExtras/DebugInboundEventsHandler.swift b/Sources/NIOExtras/DebugInboundEventsHandler.swift index 10c2e5b9..5e25693c 100644 --- a/Sources/NIOExtras/DebugInboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugInboundEventsHandler.swift @@ -174,3 +174,8 @@ public class DebugInboundEventsHandler: ChannelInboundHandler { fflush(stdout) } } + +#if swift(>=5.6) +@available(*, unavailable) +extension DebugInboundEventsHandler: Sendable {} +#endif diff --git a/Sources/NIOExtras/DebugOutboundEventsHandler.swift b/Sources/NIOExtras/DebugOutboundEventsHandler.swift index eb0437a7..6cff4581 100644 --- a/Sources/NIOExtras/DebugOutboundEventsHandler.swift +++ b/Sources/NIOExtras/DebugOutboundEventsHandler.swift @@ -170,3 +170,8 @@ public class DebugOutboundEventsHandler: ChannelOutboundHandler { fflush(stdout) } } + +#if swift(>=5.6) +@available(*, unavailable) +extension DebugOutboundEventsHandler: Sendable {} +#endif diff --git a/Sources/NIOExtras/FixedLengthFrameDecoder.swift b/Sources/NIOExtras/FixedLengthFrameDecoder.swift index 4f4a3260..326dc77b 100644 --- a/Sources/NIOExtras/FixedLengthFrameDecoder.swift +++ b/Sources/NIOExtras/FixedLengthFrameDecoder.swift @@ -76,3 +76,8 @@ public final class FixedLengthFrameDecoder: ByteToMessageDecoder { return .needMoreData } } + +#if swift(>=5.6) +@available(*, unavailable) +extension FixedLengthFrameDecoder: Sendable {} +#endif diff --git a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift index 099e93cd..9bf462d0 100644 --- a/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift +++ b/Sources/NIOExtras/JSONRPCFraming+ContentLengthHeader.swift @@ -214,3 +214,13 @@ extension String { return self[firstElementIndex ..< lastElementIndex.base] } } + + + +#if swift(>=5.6) +@available(*, unavailable) +extension NIOJSONRPCFraming.ContentLengthHeaderFrameDecoder: Sendable {} + +@available(*, unavailable) +extension NIOJSONRPCFraming.ContentLengthHeaderFrameEncoder: Sendable {} +#endif diff --git a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift index 071591a5..6875e862 100644 --- a/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LengthFieldBasedFrameDecoder.swift @@ -246,3 +246,8 @@ public final class LengthFieldBasedFrameDecoder: ByteToMessageDecoder { return frameLength } } + +#if swift(>=5.6) +@available(*, unavailable) +extension LengthFieldBasedFrameDecoder: Sendable {} +#endif diff --git a/Sources/NIOExtras/LengthFieldPrepender.swift b/Sources/NIOExtras/LengthFieldPrepender.swift index 37d797ca..fdabf79f 100644 --- a/Sources/NIOExtras/LengthFieldPrepender.swift +++ b/Sources/NIOExtras/LengthFieldPrepender.swift @@ -144,3 +144,8 @@ public final class LengthFieldPrepender: ChannelOutboundHandler { context.write(data, promise: promise) } } + +#if swift(>=5.6) +@available(*, unavailable) +extension LengthFieldPrepender: Sendable {} +#endif diff --git a/Sources/NIOExtras/LineBasedFrameDecoder.swift b/Sources/NIOExtras/LineBasedFrameDecoder.swift index fd7a809c..cc1b53b2 100644 --- a/Sources/NIOExtras/LineBasedFrameDecoder.swift +++ b/Sources/NIOExtras/LineBasedFrameDecoder.swift @@ -91,3 +91,8 @@ public class LineBasedFrameDecoder: ByteToMessageDecoder { return nil } } + +#if swift(>=5.6) +@available(*, unavailable) +extension LineBasedFrameDecoder: Sendable {} +#endif diff --git a/Sources/NIOExtras/NIOLengthFieldBitLength.swift b/Sources/NIOExtras/NIOLengthFieldBitLength.swift index a41ab117..8bbff5b7 100644 --- a/Sources/NIOExtras/NIOLengthFieldBitLength.swift +++ b/Sources/NIOExtras/NIOLengthFieldBitLength.swift @@ -12,8 +12,10 @@ // //===----------------------------------------------------------------------===// +import NIOCore + /// A struct to describe the length of a piece of data in bits -public struct NIOLengthFieldBitLength { +public struct NIOLengthFieldBitLength: NIOSendable { internal enum Backing { case bits8 case bits16 diff --git a/Sources/NIOExtras/PCAPRingBuffer.swift b/Sources/NIOExtras/PCAPRingBuffer.swift index 4273d28e..93648108 100644 --- a/Sources/NIOExtras/PCAPRingBuffer.swift +++ b/Sources/NIOExtras/PCAPRingBuffer.swift @@ -96,3 +96,8 @@ public class NIOPCAPRingBuffer { return toReturn } } + +#if swift(>=5.6) +@available(*, unavailable) +extension NIOPCAPRingBuffer: Sendable {} +#endif diff --git a/Sources/NIOExtras/RequestResponseHandler.swift b/Sources/NIOExtras/RequestResponseHandler.swift index 907ce2ec..1c91e1eb 100644 --- a/Sources/NIOExtras/RequestResponseHandler.swift +++ b/Sources/NIOExtras/RequestResponseHandler.swift @@ -119,3 +119,8 @@ public final class RequestResponseHandler: ChannelDuplexHandl } } } + +#if swift(>=5.6) +@available(*, unavailable) +extension RequestResponseHandler: Sendable {} +#endif diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 92bcf4ec..70b6d8eb 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -290,6 +290,11 @@ public class NIOWritePCAPHandler: RemovableChannelHandler { } } +#if swift(>=5.6) +@available(*, unavailable) +extension NIOWritePCAPHandler: Sendable {} +#endif + extension NIOWritePCAPHandler: ChannelDuplexHandler { public typealias InboundIn = ByteBuffer public typealias InboundOut = ByteBuffer From 6c84d247754ad77487a6f0694273b89b83efd056 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Fri, 16 Sep 2022 09:22:42 +0200 Subject: [PATCH 15/22] Correctly validate the bounds of decompression (#177) Motivation Currently we don't confirm that the decompression has completed successfully. This means that we can incorrectly spin forever attempting to decompress past the end of a message, and that we can fail to notice that a message is truncated. Neither of these is good. Modifications Propagate the message zlib gives us as to whether or not decompression is done, and keep track of it. Add some tests written by @vojtarylko to validate the behaviour. Result Correctly police the bounds of the messages. Resolves #175 and #176. --- .../HTTPDecompression.swift | 55 ++++++++++++++++--- .../HTTPRequestDecompressor.swift | 20 ++++++- .../HTTPResponseDecompressor.swift | 20 ++++++- .../HTTPRequestDecompressorTest+XCTest.swift | 2 + .../HTTPRequestDecompressorTest.swift | 26 ++++++++- .../HTTPResponseDecompressorTest+XCTest.swift | 2 + .../HTTPResponseDecompressorTest.swift | 25 +++++++++ 7 files changed, 136 insertions(+), 14 deletions(-) diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index d8b8cad8..f9e5aaf2 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -57,6 +57,29 @@ public enum NIOHTTPDecompression { case initializationError(Int) } + public struct ExtraDecompressionError: Error, Hashable, CustomStringConvertible { + private var backing: Backing + + private enum Backing { + case invalidTrailingData + case truncatedData + } + + private init(_ backing: Backing) { + self.backing = backing + } + + /// Decompression completed but there was invalid trailing data behind the compressed data. + public static let invalidTrailingData = Self(.invalidTrailingData) + + /// The decompressed data was incorrectly truncated. + public static let truncatedData = Self(.truncatedData) + + public var description: String { + return String(describing: self.backing) + } + } + enum CompressionAlgorithm: String { case gzip case deflate @@ -91,12 +114,15 @@ public enum NIOHTTPDecompression { self.limit = limit } - mutating func decompress(part: inout ByteBuffer, buffer: inout ByteBuffer, compressedLength: Int) throws { - self.inflated += try self.stream.inflatePart(input: &part, output: &buffer) + mutating func decompress(part: inout ByteBuffer, buffer: inout ByteBuffer, compressedLength: Int) throws -> InflateResult { + let result = try self.stream.inflatePart(input: &part, output: &buffer) + self.inflated += result.written if self.limit.exceeded(compressed: compressedLength, decompressed: self.inflated) { throw NIOHTTPDecompression.DecompressionError.limit } + + return result } mutating func initializeDecoder(encoding: NIOHTTPDecompression.CompressionAlgorithm) throws { @@ -117,9 +143,10 @@ public enum NIOHTTPDecompression { } extension z_stream { - mutating func inflatePart(input: inout ByteBuffer, output: inout ByteBuffer) throws -> Int { + mutating func inflatePart(input: inout ByteBuffer, output: inout ByteBuffer) throws -> InflateResult { let minimumCapacity = input.readableBytes * 2 - var written = 0 + var inflateResult = InflateResult(written: 0, complete: false) + try input.readWithUnsafeMutableReadableBytes { pointer in self.avail_in = UInt32(pointer.count) self.next_in = CNIOExtrasZlib_voidPtr_to_BytefPtr(pointer.baseAddress!) @@ -131,24 +158,34 @@ extension z_stream { self.next_out = nil } - written += try self.inflatePart(to: &output, minimumCapacity: minimumCapacity) + inflateResult = try self.inflatePart(to: &output, minimumCapacity: minimumCapacity) return pointer.count - Int(self.avail_in) } - return written + return inflateResult } - private mutating func inflatePart(to buffer: inout ByteBuffer, minimumCapacity: Int) throws -> Int { - return try buffer.writeWithUnsafeMutableBytes(minimumWritableBytes: minimumCapacity) { pointer in + private mutating func inflatePart(to buffer: inout ByteBuffer, minimumCapacity: Int) throws -> InflateResult { + var rc = Z_OK + + let written = try buffer.writeWithUnsafeMutableBytes(minimumWritableBytes: minimumCapacity) { pointer in self.avail_out = UInt32(pointer.count) self.next_out = CNIOExtrasZlib_voidPtr_to_BytefPtr(pointer.baseAddress!) - let rc = inflate(&self, Z_NO_FLUSH) + rc = inflate(&self, Z_NO_FLUSH) guard rc == Z_OK || rc == Z_STREAM_END else { throw NIOHTTPDecompression.DecompressionError.inflationError(Int(rc)) } return pointer.count - Int(self.avail_out) } + + return InflateResult(written: written, complete: rc == Z_STREAM_END) } } + +struct InflateResult { + var written: Int + + var complete: Bool +} diff --git a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift index d3e52fa9..bbbc81a8 100644 --- a/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPRequestDecompressor.swift @@ -34,12 +34,14 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh private var decompressor: NIOHTTPDecompression.Decompressor private var compression: Compression? + private var decompressionComplete: Bool /// Initialise with limits. /// - Parameter limit: Limit to how much inflation can occur to protect against bad cases. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) self.compression = nil + self.decompressionComplete = false } public func channelRead(context: ChannelHandlerContext, data: NIOAny) { @@ -68,10 +70,13 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh return } - while part.readableBytes > 0 { + while part.readableBytes > 0 && !self.decompressionComplete { do { var buffer = context.channel.allocator.buffer(capacity: 16384) - try self.decompressor.decompress(part: &part, buffer: &buffer, compressedLength: compression.contentLength) + let result = try self.decompressor.decompress(part: &part, buffer: &buffer, compressedLength: compression.contentLength) + if result.complete { + self.decompressionComplete = true + } context.fireChannelRead(self.wrapInboundOut(.body(buffer))) } catch let error { @@ -79,10 +84,21 @@ public final class NIOHTTPRequestDecompressor: ChannelDuplexHandler, RemovableCh return } } + + if part.readableBytes > 0 { + context.fireErrorCaught(NIOHTTPDecompression.ExtraDecompressionError.invalidTrailingData) + } case .end: if self.compression != nil { + let wasDecompressionComplete = self.decompressionComplete + self.decompressor.deinitializeDecoder() self.compression = nil + self.decompressionComplete = false + + if !wasDecompressionComplete { + context.fireErrorCaught(NIOHTTPDecompression.ExtraDecompressionError.truncatedData) + } } context.fireChannelRead(data) diff --git a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift index 1e7442f3..64c60182 100644 --- a/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift +++ b/Sources/NIOHTTPCompression/HTTPResponseDecompressor.swift @@ -38,11 +38,13 @@ public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableC private var compression: Compression? = nil private var decompressor: NIOHTTPDecompression.Decompressor + private var decompressionComplete: Bool /// Initialise /// - Parameter limit: Limit on the amount of decompression allowed. public init(limit: NIOHTTPDecompression.DecompressionLimit) { self.decompressor = NIOHTTPDecompression.Decompressor(limit: limit) + self.decompressionComplete = false } public func write(context: ChannelHandlerContext, data: NIOAny, promise: EventLoopPromise?) { @@ -84,22 +86,36 @@ public final class NIOHTTPResponseDecompressor: ChannelDuplexHandler, RemovableC do { compression.compressedLength += part.readableBytes - while part.readableBytes > 0 { + while part.readableBytes > 0 && !self.decompressionComplete { var buffer = context.channel.allocator.buffer(capacity: 16384) - try self.decompressor.decompress(part: &part, buffer: &buffer, compressedLength: compression.compressedLength) + let result = try self.decompressor.decompress(part: &part, buffer: &buffer, compressedLength: compression.compressedLength) + if result.complete { + self.decompressionComplete = true + } context.fireChannelRead(self.wrapInboundOut(.body(buffer))) } // assign the changed local property back to the class state self.compression = compression + + if part.readableBytes > 0 { + context.fireErrorCaught(NIOHTTPDecompression.ExtraDecompressionError.invalidTrailingData) + } } catch { context.fireErrorCaught(error) } case .end: if self.compression != nil { + let wasDecompressionComplete = self.decompressionComplete + self.decompressor.deinitializeDecoder() self.compression = nil + self.decompressionComplete = false + + if !wasDecompressionComplete { + context.fireErrorCaught(NIOHTTPDecompression.ExtraDecompressionError.truncatedData) + } } context.fireChannelRead(data) } diff --git a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift index 12649957..04a31a9c 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift @@ -30,6 +30,8 @@ extension HTTPRequestDecompressorTest { ("testDecompressionLimitRatio", testDecompressionLimitRatio), ("testDecompressionLimitSize", testDecompressionLimitSize), ("testDecompression", testDecompression), + ("testDecompressionTrailingData", testDecompressionTrailingData), + ("testDecompressionTruncatedInput", testDecompressionTruncatedInput), ] } } diff --git a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest.swift b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest.swift index 38d0701f..8e035785 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest.swift @@ -120,9 +120,33 @@ class HTTPRequestDecompressorTest: XCTestCase { ) XCTAssertNoThrow(try channel.writeInbound(HTTPServerRequestPart.body(compressed))) + XCTAssertNoThrow(try channel.writeInbound(HTTPServerRequestPart.end(nil))) } + } + + func testDecompressionTrailingData() throws { + // Valid compressed data with some trailing garbage + let compressed = ByteBuffer(bytes: [120, 156, 99, 0, 0, 0, 1, 0, 1] + [1, 2, 3]) + + let channel = EmbeddedChannel() + try channel.pipeline.addHandler(NIOHTTPRequestDecompressor(limit: .none)).wait() + let headers = HTTPHeaders([("Content-Encoding", "deflate"), ("Content-Length", "\(compressed.readableBytes)")]) + try channel.writeInbound(HTTPServerRequestPart.head(.init(version: .init(major: 1, minor: 1), method: .POST, uri: "https://nio.swift.org/test", headers: headers))) + + XCTAssertThrowsError(try channel.writeInbound(HTTPServerRequestPart.body(compressed))) + } + + func testDecompressionTruncatedInput() throws { + // Truncated compressed data + let compressed = ByteBuffer(bytes: [120, 156, 99, 0]) - XCTAssertNoThrow(try channel.writeInbound(HTTPServerRequestPart.end(nil))) + let channel = EmbeddedChannel() + try channel.pipeline.addHandler(NIOHTTPRequestDecompressor(limit: .none)).wait() + let headers = HTTPHeaders([("Content-Encoding", "deflate"), ("Content-Length", "\(compressed.readableBytes)")]) + try channel.writeInbound(HTTPServerRequestPart.head(.init(version: .init(major: 1, minor: 1), method: .POST, uri: "https://nio.swift.org/test", headers: headers))) + + XCTAssertNoThrow(try channel.writeInbound(HTTPServerRequestPart.body(compressed))) + XCTAssertThrowsError(try channel.writeInbound(HTTPServerRequestPart.end(nil))) } private func compress(_ body: ByteBuffer, _ algorithm: String) -> ByteBuffer { diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift index 30d6d459..9f844d11 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift @@ -37,6 +37,8 @@ extension HTTPResponseDecompressorTest { ("testDecompressionLimitRatioWithoutContentLenghtHeaderFails", testDecompressionLimitRatioWithoutContentLenghtHeaderFails), ("testDecompression", testDecompression), ("testDecompressionWithoutContentLength", testDecompressionWithoutContentLength), + ("testDecompressionTrailingData", testDecompressionTrailingData), + ("testDecompressionTruncatedInput", testDecompressionTruncatedInput), ] } } diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest.swift index 1d9ccf79..b42e629f 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest.swift @@ -239,6 +239,31 @@ class HTTPResponseDecompressorTest: XCTestCase { XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.end(nil))) } + func testDecompressionTrailingData() throws { + // Valid compressed data with some trailing garbage + let compressed = ByteBuffer(bytes: [120, 156, 99, 0, 0, 0, 1, 0, 1] + [1, 2, 3]) + + let channel = EmbeddedChannel() + try channel.pipeline.addHandler(NIOHTTPResponseDecompressor(limit: .none)).wait() + let headers = HTTPHeaders([("Content-Encoding", "deflate"), ("Content-Length", "\(compressed.readableBytes)")]) + try channel.writeInbound(HTTPClientResponsePart.head(.init(version: .init(major: 1, minor: 1), status: .ok, headers: headers))) + + XCTAssertThrowsError(try channel.writeInbound(HTTPClientResponsePart.body(compressed))) + } + + func testDecompressionTruncatedInput() throws { + // Truncated compressed data + let compressed = ByteBuffer(bytes: [120, 156, 99, 0]) + + let channel = EmbeddedChannel() + try channel.pipeline.addHandler(NIOHTTPResponseDecompressor(limit: .none)).wait() + let headers = HTTPHeaders([("Content-Encoding", "deflate"), ("Content-Length", "\(compressed.readableBytes)")]) + try channel.writeInbound(HTTPClientResponsePart.head(.init(version: .init(major: 1, minor: 1), status: .ok, headers: headers))) + + XCTAssertNoThrow(try channel.writeInbound(HTTPClientResponsePart.body(compressed))) + XCTAssertThrowsError(try channel.writeInbound(HTTPClientResponsePart.end(nil))) + } + private func compress(_ body: ByteBuffer, _ algorithm: String) -> ByteBuffer { var stream = z_stream() From 55f37f388c2cbe717395176fbe8984354d8488d2 Mon Sep 17 00:00:00 2001 From: George Barnett Date: Thu, 29 Sep 2022 09:21:17 +0100 Subject: [PATCH 16/22] Raise minimum supported Swift version from 5.4 to 5.5 (#180) Motivation: SwiftNIO periodically drops support for older Swift versions. Now that 5.7 has been released, 5.4 will be dropped. Modifications: - Remove 5.4 specific Package.swift and docker-compose - Update the 5.7 docker-compose to use the released 5.7 and move from focal (2004) to jammy (2204) - Update docs Results: Minimum Swift version is 5.5 --- Package.swift | 2 +- Package@swift-5.4.swift | 125 ------------------ README.md | 11 +- .../HTTPResponseCompressorTest.swift | 10 +- docker/Dockerfile | 2 +- docker/docker-compose.1804.54.yaml | 18 --- docker/docker-compose.2004.57.yaml | 15 --- docker/docker-compose.2204.57.yaml | 16 +++ 8 files changed, 32 insertions(+), 167 deletions(-) delete mode 100644 Package@swift-5.4.swift delete mode 100644 docker/docker-compose.1804.54.yaml delete mode 100644 docker/docker-compose.2004.57.yaml create mode 100644 docker/docker-compose.2204.57.yaml diff --git a/Package.swift b/Package.swift index e97f6c3a..edf6a3f4 100644 --- a/Package.swift +++ b/Package.swift @@ -119,7 +119,7 @@ let package = Package( .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), ], dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.42.0"), .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"), ], targets: targets diff --git a/Package@swift-5.4.swift b/Package@swift-5.4.swift deleted file mode 100644 index b7dd215f..00000000 --- a/Package@swift-5.4.swift +++ /dev/null @@ -1,125 +0,0 @@ -// swift-tools-version:5.4 -//===----------------------------------------------------------------------===// -// -// 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 -// -//===----------------------------------------------------------------------===// - -import PackageDescription - -var targets: [PackageDescription.Target] = [ - .target( - name: "NIOExtras", - dependencies: [ - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - ]), - .target( - name: "NIOHTTPCompression", - dependencies: [ - "CNIOExtrasZlib", - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - ]), - .executableTarget( - name: "HTTPServerWithQuiescingDemo", - dependencies: [ - "NIOExtras", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - ]), - .executableTarget( - name: "NIOWritePCAPDemo", - dependencies: [ - "NIOExtras", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - ]), - .executableTarget( - name: "NIOWritePartialPCAPDemo", - dependencies: [ - "NIOExtras", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - ]), - .executableTarget( - name: "NIOExtrasPerformanceTester", - dependencies: [ - "NIOExtras", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - ]), - .target( - name: "NIOSOCKS", - dependencies: [ - .product(name: "NIO", package: "swift-nio"), - .product(name: "NIOCore", package: "swift-nio"), - ]), - .executableTarget( - name: "NIOSOCKSClient", - dependencies: [ - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - "NIOSOCKS" - ]), - .target( - name: "CNIOExtrasZlib", - dependencies: [], - linkerSettings: [ - .linkedLibrary("z") - ]), - .testTarget( - name: "NIOExtrasTests", - dependencies: [ - "NIOExtras", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - .product(name: "NIOPosix", package: "swift-nio"), - .product(name: "NIOTestUtils", package: "swift-nio"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - ]), - .testTarget( - name: "NIOHTTPCompressionTests", - dependencies: [ - "CNIOExtrasZlib", - "NIOHTTPCompression", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - .product(name: "NIOHTTP1", package: "swift-nio"), - .product(name: "NIOConcurrencyHelpers", package: "swift-nio"), - ]), - .testTarget( - name: "NIOSOCKSTests", - dependencies: [ - "NIOSOCKS", - .product(name: "NIOCore", package: "swift-nio"), - .product(name: "NIOEmbedded", package: "swift-nio"), - ]) -] - -let package = Package( - name: "swift-nio-extras", - products: [ - .library(name: "NIOExtras", targets: ["NIOExtras"]), - .library(name: "NIOSOCKS", targets: ["NIOSOCKS"]), - .library(name: "NIOHTTPCompression", targets: ["NIOHTTPCompression"]), - ], - dependencies: [ - .package(url: "https://github.com/apple/swift-nio.git", from: "2.34.0"), - ], - targets: targets -) diff --git a/README.md b/README.md index 13a17b9b..a1ac4a72 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ All code will go through code review like in the other repositories related to t `swift-nio-extras` part of the SwiftNIO 2 family of repositories and depends on the following: - [`swift-nio`](https://github.com/apple/swift-nio), version 2.30.0 or better. -- Swift 5.4. +- Swift 5.5. - `zlib` and its development headers installed on the system. But don't worry, you'll find `zlib` on pretty much any UNIX system that can compile any sort of code. To depend on `swift-nio-extras`, put the following in the `dependencies` of your `Package.swift`: @@ -25,7 +25,14 @@ To depend on `swift-nio-extras`, put the following in the `dependencies` of your ### Support for older Swift versions -Earlier versions of SwiftNIO (2.39.x and lower) and SwiftNIOExtras (1.10.x and lower) supported Swift 5.2 and 5.3, SwiftNIO (2.29.x and lower) and SwiftNIOExtras (1.9.x and lower) supported Swift 5.0 and 5.1. +The most recent versions of SwiftNIO Extras support Swift 5.5 and newer. The minimum Swift version supported by SwiftNIO Extras releases are detailed below: + +SwiftNIO Extras | Minimum Swift Version +--------------------|---------------------- +`1.0.0 ..< 1.10.0` | 5.0 +`1.10.0 ..< 1.11.0` | 5.2 +`1.11.0 ..< 1.14.0` | 5.4 +`1.14.0 ...` | 5.5 On the [`nio-extras-0.1`](https://github.com/apple/swift-nio-extras/tree/nio-extras-0.1) branch, you can find the `swift-nio-extras` version for the SwiftNIO 1 family. It requires Swift 4.1 or better. diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift index ae74b554..97c03053 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift @@ -546,7 +546,7 @@ class HTTPResponseCompressorTest: XCTestCase { XCTAssertNoThrow(try channel.pipeline.removeHandler(name: "compressor").wait()) XCTAssertNoThrow(try writePromise.futureResult.wait()) } - + func testChunkedGzipResponseProducesCorrectNumberOfWrites() throws { let channel = try compressionChannel() try sendRequest(acceptEncoding: "gzip", channel: channel) @@ -558,14 +558,14 @@ class HTTPResponseCompressorTest: XCTestCase { channel.write(NIOAny(HTTPServerResponsePart.head(head)), promise: nil) channel.writeAndFlush(NIOAny(HTTPServerResponsePart.body(.byteBuffer(bodyBuffer))), promise: nil) channel.writeAndFlush(NIOAny(HTTPServerResponsePart.end(nil)), promise: finalPromise) - + try finalPromise.futureResult.wait() - + var writeCount = 0 while try channel.readOutbound(as: ByteBuffer.self) != nil { writeCount += 1 } - + // Expected number of emitted writes in the chunked response is 8: // 1. HTTP response header // 2. First chunk length @@ -673,7 +673,7 @@ extension EventLoopFuture { } return fulfilled } else { - let lock = Lock() + let lock = NIOLock() let group = DispatchGroup() var fulfilled = false // protected by lock diff --git a/docker/Dockerfile b/docker/Dockerfile index 9e20b07b..62c76d82 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -ARG swift_version=5.4 +ARG swift_version=5.7 ARG ubuntu_version=focal ARG base_image=swift:$swift_version-$ubuntu_version FROM $base_image diff --git a/docker/docker-compose.1804.54.yaml b/docker/docker-compose.1804.54.yaml deleted file mode 100644 index eb9c80a8..00000000 --- a/docker/docker-compose.1804.54.yaml +++ /dev/null @@ -1,18 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-extras:18.04-5.4 - build: - args: - base_image: "swift:5.4-bionic" - ubuntu_version: "bionic" - swift_version: "5.4" - - test: - image: swift-nio-extras:18.04-5.4 - command: /bin/bash -xcl "cat /etc/lsb-release && swift -version && swift test -Xswiftc -warnings-as-errors $${SANITIZER_ARG-}" - - shell: - image: swift-nio-extras:18.04-5.4 diff --git a/docker/docker-compose.2004.57.yaml b/docker/docker-compose.2004.57.yaml deleted file mode 100644 index 37a62ef9..00000000 --- a/docker/docker-compose.2004.57.yaml +++ /dev/null @@ -1,15 +0,0 @@ -version: "3" - -services: - - runtime-setup: - image: swift-nio-extras:20.04-5.7 - build: - args: - base_image: "swiftlang/swift:nightly-5.7-focal" - - test: - image: swift-nio-extras:20.04-5.7 - - shell: - image: swift-nio-extras:20.04-5.7 diff --git a/docker/docker-compose.2204.57.yaml b/docker/docker-compose.2204.57.yaml new file mode 100644 index 00000000..ef427b04 --- /dev/null +++ b/docker/docker-compose.2204.57.yaml @@ -0,0 +1,16 @@ +version: "3" + +services: + + runtime-setup: + image: swift-nio-extras:22.04-5.7 + build: + args: + ubuntu_version: "jammy" + swift_version: "5.7" + + test: + image: swift-nio-extras:22.04-5.7 + + shell: + image: swift-nio-extras:22.04-5.7 From 985a485f71792ca507b17730dd2081f843aea6eb Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Thu, 13 Oct 2022 14:04:27 +0100 Subject: [PATCH 17/22] Remove `#if compiler(>=5.5)` (#182) --- README.md | 6 +++--- Sources/NIOExtras/QuiescingHelper.swift | 5 +---- Sources/NIOExtras/WritePCAPHandler.swift | 6 +----- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index a1ac4a72..63260981 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ All code will go through code review like in the other repositories related to t `swift-nio-extras` part of the SwiftNIO 2 family of repositories and depends on the following: - [`swift-nio`](https://github.com/apple/swift-nio), version 2.30.0 or better. -- Swift 5.5. +- Swift 5.5.2. - `zlib` and its development headers installed on the system. But don't worry, you'll find `zlib` on pretty much any UNIX system that can compile any sort of code. To depend on `swift-nio-extras`, put the following in the `dependencies` of your `Package.swift`: @@ -25,14 +25,14 @@ To depend on `swift-nio-extras`, put the following in the `dependencies` of your ### Support for older Swift versions -The most recent versions of SwiftNIO Extras support Swift 5.5 and newer. The minimum Swift version supported by SwiftNIO Extras releases are detailed below: +The most recent versions of SwiftNIO Extras support Swift 5.5.2 and newer. The minimum Swift version supported by SwiftNIO Extras releases are detailed below: SwiftNIO Extras | Minimum Swift Version --------------------|---------------------- `1.0.0 ..< 1.10.0` | 5.0 `1.10.0 ..< 1.11.0` | 5.2 `1.11.0 ..< 1.14.0` | 5.4 -`1.14.0 ...` | 5.5 +`1.14.0 ...` | 5.5.2 On the [`nio-extras-0.1`](https://github.com/apple/swift-nio-extras/tree/nio-extras-0.1) branch, you can find the `swift-nio-extras` version for the SwiftNIO 1 family. It requires Swift 4.1 or better. diff --git a/Sources/NIOExtras/QuiescingHelper.swift b/Sources/NIOExtras/QuiescingHelper.swift index ea1ffec7..882ec9b4 100644 --- a/Sources/NIOExtras/QuiescingHelper.swift +++ b/Sources/NIOExtras/QuiescingHelper.swift @@ -144,11 +144,8 @@ private final class ChannelCollector { } } -#if swift(>=5.5) && canImport(_Concurrency) -extension ChannelCollector: @unchecked Sendable { -} -#endif +extension ChannelCollector: @unchecked Sendable {} /// A `ChannelHandler` that adds all channels that it receives through the `ChannelPipeline` to a `ChannelCollector`. /// diff --git a/Sources/NIOExtras/WritePCAPHandler.swift b/Sources/NIOExtras/WritePCAPHandler.swift index 70b6d8eb..23a3e9af 100644 --- a/Sources/NIOExtras/WritePCAPHandler.swift +++ b/Sources/NIOExtras/WritePCAPHandler.swift @@ -722,8 +722,4 @@ extension NIOWritePCAPHandler { } } -#if swift(>=5.5) && canImport(_Concurrency) -extension NIOWritePCAPHandler.SynchronizedFileSink: @unchecked Sendable { - -} -#endif +extension NIOWritePCAPHandler.SynchronizedFileSink: @unchecked Sendable {} From e9ed606ce31d134777acd64ba3e2c23a243ddb23 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Thu, 13 Oct 2022 14:17:37 +0100 Subject: [PATCH 18/22] Fix README.md (#183) I have accidentally added an additional dot in https://github.com/apple/swift-nio-extras/pull/182 This PR removes it again. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 63260981..cde72363 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ All code will go through code review like in the other repositories related to t `swift-nio-extras` part of the SwiftNIO 2 family of repositories and depends on the following: - [`swift-nio`](https://github.com/apple/swift-nio), version 2.30.0 or better. -- Swift 5.5.2. +- Swift 5.5.2 - `zlib` and its development headers installed on the system. But don't worry, you'll find `zlib` on pretty much any UNIX system that can compile any sort of code. To depend on `swift-nio-extras`, put the following in the `dependencies` of your `Package.swift`: From d373eaf7dba3a275f7051bad63d4f3eb67936576 Mon Sep 17 00:00:00 2001 From: David Nadoba Date: Thu, 13 Oct 2022 15:43:15 +0100 Subject: [PATCH 19/22] Replace `NIOSendable` with `Sendable` (#181) --- Sources/NIOExtras/NIOLengthFieldBitLength.swift | 2 +- Sources/NIOExtras/QuiescingHelper.swift | 2 +- Sources/NIOHTTPCompression/HTTPCompression.swift | 2 +- Sources/NIOHTTPCompression/HTTPDecompression.swift | 2 +- Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift | 2 +- Sources/NIOSOCKS/Messages/AuthenticationMethod.swift | 2 +- Sources/NIOSOCKS/Messages/ClientGreeting.swift | 2 +- Sources/NIOSOCKS/Messages/Messages.swift | 4 ++-- Sources/NIOSOCKS/Messages/SOCKSRequest.swift | 6 +++--- Sources/NIOSOCKS/Messages/SOCKSResponse.swift | 4 ++-- .../NIOSOCKS/Messages/SelectedAuthenticationMethod.swift | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sources/NIOExtras/NIOLengthFieldBitLength.swift b/Sources/NIOExtras/NIOLengthFieldBitLength.swift index 8bbff5b7..cb3aa955 100644 --- a/Sources/NIOExtras/NIOLengthFieldBitLength.swift +++ b/Sources/NIOExtras/NIOLengthFieldBitLength.swift @@ -15,7 +15,7 @@ import NIOCore /// A struct to describe the length of a piece of data in bits -public struct NIOLengthFieldBitLength: NIOSendable { +public struct NIOLengthFieldBitLength: Sendable { internal enum Backing { case bits8 case bits16 diff --git a/Sources/NIOExtras/QuiescingHelper.swift b/Sources/NIOExtras/QuiescingHelper.swift index 882ec9b4..846fe177 100644 --- a/Sources/NIOExtras/QuiescingHelper.swift +++ b/Sources/NIOExtras/QuiescingHelper.swift @@ -256,6 +256,6 @@ public final class ServerQuiescingHelper { } } -extension ServerQuiescingHelper: NIOSendable { +extension ServerQuiescingHelper: Sendable { } diff --git a/Sources/NIOHTTPCompression/HTTPCompression.swift b/Sources/NIOHTTPCompression/HTTPCompression.swift index 9218b3ec..3013aaf3 100644 --- a/Sources/NIOHTTPCompression/HTTPCompression.swift +++ b/Sources/NIOHTTPCompression/HTTPCompression.swift @@ -19,7 +19,7 @@ import NIOCore public enum NIOCompression { /// Which algorithm should be used for compression. - public struct Algorithm: CustomStringConvertible, Equatable, NIOSendable { + public struct Algorithm: CustomStringConvertible, Equatable, Sendable { fileprivate enum AlgorithmEnum: String { case gzip case deflate diff --git a/Sources/NIOHTTPCompression/HTTPDecompression.swift b/Sources/NIOHTTPCompression/HTTPDecompression.swift index f9e5aaf2..d99b65b0 100644 --- a/Sources/NIOHTTPCompression/HTTPDecompression.swift +++ b/Sources/NIOHTTPCompression/HTTPDecompression.swift @@ -18,7 +18,7 @@ import NIOCore /// Namespace for decompression code. public enum NIOHTTPDecompression { /// Specifies how to limit decompression inflation. - public struct DecompressionLimit: NIOSendable { + public struct DecompressionLimit: Sendable { private enum Limit { case none case size(Int) diff --git a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift index 1890d952..679093a0 100644 --- a/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift +++ b/Sources/NIOSOCKS/Channel Handlers/SOCKSClientHandler.swift @@ -212,7 +212,7 @@ extension SOCKSClientHandler: RemovableChannelHandler { /// A `Channel` user event that is sent when a SOCKS connection has been established /// /// After this event has been received it is save to remove the `SOCKSClientHandler` from the channel pipeline. -public struct SOCKSProxyEstablishedEvent: NIOSendable { +public struct SOCKSProxyEstablishedEvent: Sendable { public init() { } } diff --git a/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift index 11687232..125da033 100644 --- a/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/AuthenticationMethod.swift @@ -15,7 +15,7 @@ import NIOCore /// The SOCKS authentication method to use, defined in RFC 1928. -public struct AuthenticationMethod: Hashable, NIOSendable { +public struct AuthenticationMethod: Hashable, Sendable { /// No authentication required public static let noneRequired = AuthenticationMethod(value: 0x00) diff --git a/Sources/NIOSOCKS/Messages/ClientGreeting.swift b/Sources/NIOSOCKS/Messages/ClientGreeting.swift index 5ddc3f82..603ae075 100644 --- a/Sources/NIOSOCKS/Messages/ClientGreeting.swift +++ b/Sources/NIOSOCKS/Messages/ClientGreeting.swift @@ -17,7 +17,7 @@ import NIOCore /// Clients begin the SOCKS handshake process /// by providing an array of suggested authentication /// methods. -public struct ClientGreeting: Hashable, NIOSendable { +public struct ClientGreeting: Hashable, Sendable { /// The protocol version. public let version: UInt8 = 5 diff --git a/Sources/NIOSOCKS/Messages/Messages.swift b/Sources/NIOSOCKS/Messages/Messages.swift index cb1bbec4..d187329b 100644 --- a/Sources/NIOSOCKS/Messages/Messages.swift +++ b/Sources/NIOSOCKS/Messages/Messages.swift @@ -15,7 +15,7 @@ import NIOCore /// Sent by the client and received by the server. -public enum ClientMessage: Hashable, NIOSendable { +public enum ClientMessage: Hashable, Sendable { /// Contains the proposed authentication methods. case greeting(ClientGreeting) @@ -28,7 +28,7 @@ public enum ClientMessage: Hashable, NIOSendable { } /// Sent by the server and received by the client. -public enum ServerMessage: Hashable, NIOSendable { +public enum ServerMessage: Hashable, Sendable { /// Used by the server to instruct the client of the authentication method to use. case selectedAuthenticationMethod(SelectedAuthenticationMethod) diff --git a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift index e5e7ade0..e5edea8e 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSRequest.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSRequest.swift @@ -24,7 +24,7 @@ import NIOCore /// Instructs the SOCKS proxy server of the target host, /// and how to connect. -public struct SOCKSRequest: Hashable, NIOSendable { +public struct SOCKSRequest: Hashable, Sendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 @@ -75,7 +75,7 @@ extension ByteBuffer { /// What type of connection the SOCKS server should establish with /// the target host. -public struct SOCKSCommand: Hashable, NIOSendable { +public struct SOCKSCommand: Hashable, Sendable { /// Typically the primary connection type, suitable for HTTP. public static let connect = SOCKSCommand(value: 0x01) @@ -99,7 +99,7 @@ public struct SOCKSCommand: Hashable, NIOSendable { // MARK: - SOCKSAddress /// The address used to connect to the target host. -public enum SOCKSAddress: Hashable, NIOSendable { +public enum SOCKSAddress: Hashable, Sendable { /// Socket Adress case address(SocketAddress) /// Host and port diff --git a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift index a29be857..67630c04 100644 --- a/Sources/NIOSOCKS/Messages/SOCKSResponse.swift +++ b/Sources/NIOSOCKS/Messages/SOCKSResponse.swift @@ -18,7 +18,7 @@ import NIOCore /// The SOCKS Server's response to the client's request /// indicating if the request succeeded or failed. -public struct SOCKSResponse: Hashable, NIOSendable { +public struct SOCKSResponse: Hashable, Sendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 @@ -69,7 +69,7 @@ extension ByteBuffer { /// Used to indicate if the SOCKS client's connection request succeeded /// or failed. -public struct SOCKSServerReply: Hashable, NIOSendable { +public struct SOCKSServerReply: Hashable, Sendable { /// The connection succeeded and data can now be transmitted. public static let succeeded = SOCKSServerReply(value: 0x00) diff --git a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift index 87fb4503..24dba9a2 100644 --- a/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift +++ b/Sources/NIOSOCKS/Messages/SelectedAuthenticationMethod.swift @@ -17,7 +17,7 @@ import NIOCore /// Used by the SOCKS server to inform the client which /// authentication method it would like to use out of those /// offered. -public struct SelectedAuthenticationMethod: Hashable, NIOSendable { +public struct SelectedAuthenticationMethod: Hashable, Sendable { /// The SOCKS protocol version - we currently only support v5. public let version: UInt8 = 5 From 91dd2d61fb772e1311bb5f13b59266b579d77e42 Mon Sep 17 00:00:00 2001 From: carolinacass <67160898+carolinacass@users.noreply.github.com> Date: Fri, 28 Oct 2022 13:56:39 +0100 Subject: [PATCH 20/22] Use #fileID/#filePath instead of #file (#184) Motivation: Modifications: Changed #file to #filePath or #fileID depending on situation --- Tests/NIOExtrasTests/WritePCAPHandlerTest.swift | 4 ++-- .../NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Tests/NIOExtrasTests/WritePCAPHandlerTest.swift b/Tests/NIOExtrasTests/WritePCAPHandlerTest.swift index fafd04f7..ab873841 100644 --- a/Tests/NIOExtrasTests/WritePCAPHandlerTest.swift +++ b/Tests/NIOExtrasTests/WritePCAPHandlerTest.swift @@ -62,7 +62,7 @@ class WritePCAPHandlerTest: XCTestCase { func assertEqual(expectedAddress: SocketAddress?, actualIPv4Address: in_addr, actualPort: UInt16, - file: StaticString = #file, + file: StaticString = #filePath, line: UInt = #line) { guard let port = expectedAddress?.port else { XCTFail("expected address nil or has no port", file: (file), line: line) @@ -83,7 +83,7 @@ class WritePCAPHandlerTest: XCTestCase { func assertEqual(expectedAddress: SocketAddress?, actualIPv6Address: in6_addr, actualPort: UInt16, - file: StaticString = #file, + file: StaticString = #filePath, line: UInt = #line) { guard let port = expectedAddress?.port else { XCTFail("expected address nil or has no port", file: (file), line: line) diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift index 97c03053..01c93af7 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest.swift @@ -29,7 +29,7 @@ private class PromiseOrderer { self.eventLoop = eventLoop } - func makePromise(file: StaticString = #file, line: UInt = #line) -> EventLoopPromise { + func makePromise(file: StaticString = #fileID, line: UInt = #line) -> EventLoopPromise { let promise = eventLoop.makePromise(of: Void.self, file: (file), line: line) appendPromise(promise) return promise From b89549b2897a00cf35b5ffaf2ead9ae1f581e465 Mon Sep 17 00:00:00 2001 From: Yim Lee Date: Fri, 2 Dec 2022 01:48:47 -0800 Subject: [PATCH 21/22] Add .spi.yml for Swift Package Index DocC support (#186) --- .spi.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .spi.yml diff --git a/.spi.yml b/.spi.yml new file mode 100644 index 00000000..b8df20cf --- /dev/null +++ b/.spi.yml @@ -0,0 +1,4 @@ +version: 1 +builder: + configs: + - documentation_targets: [NIOExtras, NIOHTTPCompression, NIOSOCKS] From 54def83a52da31c01dfc028ec26927b69a8e9cf8 Mon Sep 17 00:00:00 2001 From: Rick Newton-Rogers <104022490+rnro@users.noreply.github.com> Date: Fri, 2 Dec 2022 15:17:43 +0000 Subject: [PATCH 22/22] update generated test script from swift-nio (#188) The newer version of the script automatically calculates accurate copyright date statements. --- Tests/LinuxMain.swift | 69 ++++++++++-------- ...DebugInboundEventsHandlerTest+XCTest.swift | 3 +- ...ebugOutboundEventsHandlerTest+XCTest.swift | 3 +- .../FixedLengthFrameDecoderTest+XCTest.swift | 3 +- ...ntentLengthHeaderDecoderTests+XCTest.swift | 3 +- ...ntentLengthHeaderEncoderTests+XCTest.swift | 3 +- ...gthFieldBasedFrameDecoderTest+XCTest.swift | 3 +- .../LengthFieldPrependerTest+XCTest.swift | 3 +- .../LineBasedFrameDecoderTest+XCTest.swift | 3 +- .../PCAPRingBufferTest+XCTest.swift | 3 +- .../QuiescingHelperTest+XCTest.swift | 3 +- .../RequestResponseHandlerTest+XCTest.swift | 3 +- .../WritePCAPHandlerTest+XCTest.swift | 3 +- .../HTTPRequestCompressorTest+XCTest.swift | 3 +- .../HTTPRequestDecompressorTest+XCTest.swift | 3 +- .../HTTPResponseCompressorTest+XCTest.swift | 3 +- .../HTTPResponseDecompressorTest+XCTest.swift | 3 +- .../ClientGreeting+Tests+XCTest.swift | 3 +- .../ClientRequest+Tests+XCTest.swift | 3 +- .../ClientStateMachine+Tests+XCTest.swift | 3 +- .../NIOSOCKSTests/Helpers+Tests+XCTest.swift | 3 +- .../MethodSelection+Tests+XCTest.swift | 3 +- ...SServerHandshakeHandler+Tests+XCTest.swift | 3 +- .../ServerResponse+Tests+XCTest.swift | 3 +- .../ServerStateMachine+Tests+XCTest.swift | 3 +- .../SocksClientHandler+Tests+XCTest.swift | 3 +- scripts/generate_linux_tests.rb | 71 +++++++++++++++++-- scripts/soundness.sh | 2 +- 28 files changed, 157 insertions(+), 60 deletions(-) diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 17c6a714..4bfb3d66 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -22,36 +22,47 @@ import XCTest /// Do NOT edit this file directly as it will be regenerated automatically when needed. /// -#if os(Linux) || os(FreeBSD) +#if !compiler(>=5.5) +#if os(Linux) || os(FreeBSD) || os(Android) @testable import NIOExtrasTests @testable import NIOHTTPCompressionTests @testable import NIOSOCKSTests - XCTMain([ - testCase(ClientGreetingTests.allTests), - testCase(ClientRequestTests.allTests), - testCase(ClientStateMachineTests.allTests), - testCase(DebugInboundEventsHandlerTest.allTests), - testCase(DebugOutboundEventsHandlerTest.allTests), - testCase(FixedLengthFrameDecoderTest.allTests), - testCase(HTTPRequestCompressorTest.allTests), - testCase(HTTPRequestDecompressorTest.allTests), - testCase(HTTPResponseCompressorTest.allTests), - testCase(HTTPResponseDecompressorTest.allTests), - testCase(HelperTests.allTests), - testCase(JSONRPCFramingContentLengthHeaderDecoderTests.allTests), - testCase(JSONRPCFramingContentLengthHeaderEncoderTests.allTests), - testCase(LengthFieldBasedFrameDecoderTest.allTests), - testCase(LengthFieldPrependerTest.allTests), - testCase(LineBasedFrameDecoderTest.allTests), - testCase(MethodSelectionTests.allTests), - testCase(PCAPRingBufferTest.allTests), - testCase(QuiescingHelperTest.allTests), - testCase(RequestResponseHandlerTest.allTests), - testCase(SOCKSServerHandlerTests.allTests), - testCase(ServerResponseTests.allTests), - testCase(ServerStateMachineTests.allTests), - testCase(SocksClientHandlerTests.allTests), - testCase(WritePCAPHandlerTest.allTests), - ]) +@available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") +@main +class LinuxMainRunner { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") + static func main() { + XCTMain([ + testCase(ClientGreetingTests.allTests), + testCase(ClientRequestTests.allTests), + testCase(ClientStateMachineTests.allTests), + testCase(DebugInboundEventsHandlerTest.allTests), + testCase(DebugOutboundEventsHandlerTest.allTests), + testCase(FixedLengthFrameDecoderTest.allTests), + testCase(HTTPRequestCompressorTest.allTests), + testCase(HTTPRequestDecompressorTest.allTests), + testCase(HTTPResponseCompressorTest.allTests), + testCase(HTTPResponseDecompressorTest.allTests), + testCase(HelperTests.allTests), + testCase(JSONRPCFramingContentLengthHeaderDecoderTests.allTests), + testCase(JSONRPCFramingContentLengthHeaderEncoderTests.allTests), + testCase(LengthFieldBasedFrameDecoderTest.allTests), + testCase(LengthFieldPrependerTest.allTests), + testCase(LineBasedFrameDecoderTest.allTests), + testCase(MethodSelectionTests.allTests), + testCase(PCAPRingBufferTest.allTests), + testCase(QuiescingHelperTest.allTests), + testCase(RequestResponseHandlerTest.allTests), + testCase(SOCKSServerHandlerTests.allTests), + testCase(ServerResponseTests.allTests), + testCase(ServerStateMachineTests.allTests), + testCase(SocksClientHandlerTests.allTests), + testCase(WritePCAPHandlerTest.allTests), + ]) + } +} +#endif +#else +#error("on Swift 5.5 and newer, --enable-test-discovery is required") #endif diff --git a/Tests/NIOExtrasTests/DebugInboundEventsHandlerTest+XCTest.swift b/Tests/NIOExtrasTests/DebugInboundEventsHandlerTest+XCTest.swift index bc5127e3..67c9dd01 100644 --- a/Tests/NIOExtrasTests/DebugInboundEventsHandlerTest+XCTest.swift +++ b/Tests/NIOExtrasTests/DebugInboundEventsHandlerTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension DebugInboundEventsHandlerTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (DebugInboundEventsHandlerTest) -> () throws -> Void)] { return [ ("testRegistered", testRegistered), diff --git a/Tests/NIOExtrasTests/DebugOutboundEventsHandlerTest+XCTest.swift b/Tests/NIOExtrasTests/DebugOutboundEventsHandlerTest+XCTest.swift index 5646f670..6a637839 100644 --- a/Tests/NIOExtrasTests/DebugOutboundEventsHandlerTest+XCTest.swift +++ b/Tests/NIOExtrasTests/DebugOutboundEventsHandlerTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension DebugOutboundEventsHandlerTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (DebugOutboundEventsHandlerTest) -> () throws -> Void)] { return [ ("testRegister", testRegister), diff --git a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift index 5e6b5d7a..93782a2e 100644 --- a/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/FixedLengthFrameDecoderTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension FixedLengthFrameDecoderTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (FixedLengthFrameDecoderTest) -> () throws -> Void)] { return [ ("testDecodeIfFewerBytesAreSent", testDecodeIfFewerBytesAreSent), diff --git a/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderDecoderTests+XCTest.swift b/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderDecoderTests+XCTest.swift index 46dd09ff..1af720d7 100644 --- a/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderDecoderTests+XCTest.swift +++ b/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderDecoderTests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension JSONRPCFramingContentLengthHeaderDecoderTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (JSONRPCFramingContentLengthHeaderDecoderTests) -> () throws -> Void)] { return [ ("testBasicMessage", testBasicMessage), diff --git a/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderEncoderTests+XCTest.swift b/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderEncoderTests+XCTest.swift index 52d84cd9..6092546b 100644 --- a/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderEncoderTests+XCTest.swift +++ b/Tests/NIOExtrasTests/JSONRPCFramingContentLengthHeaderEncoderTests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension JSONRPCFramingContentLengthHeaderEncoderTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (JSONRPCFramingContentLengthHeaderEncoderTests) -> () throws -> Void)] { return [ ("testEmptyMessage", testEmptyMessage), diff --git a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift index f576f286..0fb509a8 100644 --- a/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/LengthFieldBasedFrameDecoderTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension LengthFieldBasedFrameDecoderTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (LengthFieldBasedFrameDecoderTest) -> () throws -> Void)] { return [ ("testReadUInt32From3Bytes", testReadUInt32From3Bytes), diff --git a/Tests/NIOExtrasTests/LengthFieldPrependerTest+XCTest.swift b/Tests/NIOExtrasTests/LengthFieldPrependerTest+XCTest.swift index a168cbf8..78635689 100644 --- a/Tests/NIOExtrasTests/LengthFieldPrependerTest+XCTest.swift +++ b/Tests/NIOExtrasTests/LengthFieldPrependerTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension LengthFieldPrependerTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (LengthFieldPrependerTest) -> () throws -> Void)] { return [ ("testWrite3BytesOfUInt32Write", testWrite3BytesOfUInt32Write), diff --git a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift index 565c4cdd..7f051b28 100644 --- a/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift +++ b/Tests/NIOExtrasTests/LineBasedFrameDecoderTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension LineBasedFrameDecoderTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (LineBasedFrameDecoderTest) -> () throws -> Void)] { return [ ("testDecodeOneCharacterAtATime", testDecodeOneCharacterAtATime), diff --git a/Tests/NIOExtrasTests/PCAPRingBufferTest+XCTest.swift b/Tests/NIOExtrasTests/PCAPRingBufferTest+XCTest.swift index ddd149c3..d58f04e0 100644 --- a/Tests/NIOExtrasTests/PCAPRingBufferTest+XCTest.swift +++ b/Tests/NIOExtrasTests/PCAPRingBufferTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension PCAPRingBufferTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (PCAPRingBufferTest) -> () throws -> Void)] { return [ ("testNotLimited", testNotLimited), diff --git a/Tests/NIOExtrasTests/QuiescingHelperTest+XCTest.swift b/Tests/NIOExtrasTests/QuiescingHelperTest+XCTest.swift index a39540da..91d64281 100644 --- a/Tests/NIOExtrasTests/QuiescingHelperTest+XCTest.swift +++ b/Tests/NIOExtrasTests/QuiescingHelperTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension QuiescingHelperTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (QuiescingHelperTest) -> () throws -> Void)] { return [ ("testShutdownIsImmediateWhenNoChannelsCollected", testShutdownIsImmediateWhenNoChannelsCollected), diff --git a/Tests/NIOExtrasTests/RequestResponseHandlerTest+XCTest.swift b/Tests/NIOExtrasTests/RequestResponseHandlerTest+XCTest.swift index 017aca7d..98a4ca5f 100644 --- a/Tests/NIOExtrasTests/RequestResponseHandlerTest+XCTest.swift +++ b/Tests/NIOExtrasTests/RequestResponseHandlerTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension RequestResponseHandlerTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (RequestResponseHandlerTest) -> () throws -> Void)] { return [ ("testSimpleRequestWorks", testSimpleRequestWorks), diff --git a/Tests/NIOExtrasTests/WritePCAPHandlerTest+XCTest.swift b/Tests/NIOExtrasTests/WritePCAPHandlerTest+XCTest.swift index 3321682b..4e20fc6a 100644 --- a/Tests/NIOExtrasTests/WritePCAPHandlerTest+XCTest.swift +++ b/Tests/NIOExtrasTests/WritePCAPHandlerTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension WritePCAPHandlerTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (WritePCAPHandlerTest) -> () throws -> Void)] { return [ ("testConnectIssuesThreePacketsForIPv4", testConnectIssuesThreePacketsForIPv4), diff --git a/Tests/NIOHTTPCompressionTests/HTTPRequestCompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPRequestCompressorTest+XCTest.swift index b5eb1224..1846ac1f 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPRequestCompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPRequestCompressorTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2020-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension HTTPRequestCompressorTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (HTTPRequestCompressorTest) -> () throws -> Void)] { return [ ("testGzipContentEncoding", testGzipContentEncoding), diff --git a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift index 04a31a9c..fb7497ad 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPRequestDecompressorTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension HTTPRequestDecompressorTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (HTTPRequestDecompressorTest) -> () throws -> Void)] { return [ ("testDecompressionNoLimit", testDecompressionNoLimit), diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest+XCTest.swift index cc1c3782..f1bf6ea6 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseCompressorTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2019-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension HTTPResponseCompressorTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (HTTPResponseCompressorTest) -> () throws -> Void)] { return [ ("testCanCompressSimpleBodies", testCanCompressSimpleBodies), diff --git a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift index 9f844d11..f015373c 100644 --- a/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift +++ b/Tests/NIOHTTPCompressionTests/HTTPResponseDecompressorTest+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension HTTPResponseDecompressorTest { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (HTTPResponseDecompressorTest) -> () throws -> Void)] { return [ ("testDecompressionNoLimit", testDecompressionNoLimit), diff --git a/Tests/NIOSOCKSTests/ClientGreeting+Tests+XCTest.swift b/Tests/NIOSOCKSTests/ClientGreeting+Tests+XCTest.swift index 3b7a8d0f..ec4afe98 100644 --- a/Tests/NIOSOCKSTests/ClientGreeting+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/ClientGreeting+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension ClientGreetingTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (ClientGreetingTests) -> () throws -> Void)] { return [ ("testInitFromBuffer", testInitFromBuffer), diff --git a/Tests/NIOSOCKSTests/ClientRequest+Tests+XCTest.swift b/Tests/NIOSOCKSTests/ClientRequest+Tests+XCTest.swift index aaaa0d1d..ce0c090c 100644 --- a/Tests/NIOSOCKSTests/ClientRequest+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/ClientRequest+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension ClientRequestTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (ClientRequestTests) -> () throws -> Void)] { return [ ("testWriteClientRequest", testWriteClientRequest), diff --git a/Tests/NIOSOCKSTests/ClientStateMachine+Tests+XCTest.swift b/Tests/NIOSOCKSTests/ClientStateMachine+Tests+XCTest.swift index 3ff5a142..b7dc55e7 100644 --- a/Tests/NIOSOCKSTests/ClientStateMachine+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/ClientStateMachine+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension ClientStateMachineTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (ClientStateMachineTests) -> () throws -> Void)] { return [ ("testUsualWorkflow", testUsualWorkflow), diff --git a/Tests/NIOSOCKSTests/Helpers+Tests+XCTest.swift b/Tests/NIOSOCKSTests/Helpers+Tests+XCTest.swift index cee05e27..f1609d0d 100644 --- a/Tests/NIOSOCKSTests/Helpers+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/Helpers+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension HelperTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (HelperTests) -> () throws -> Void)] { return [ ("testUnwindingReturnNil", testUnwindingReturnNil), diff --git a/Tests/NIOSOCKSTests/MethodSelection+Tests+XCTest.swift b/Tests/NIOSOCKSTests/MethodSelection+Tests+XCTest.swift index 9e32d3ff..2a704502 100644 --- a/Tests/NIOSOCKSTests/MethodSelection+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/MethodSelection+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension MethodSelectionTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (MethodSelectionTests) -> () throws -> Void)] { return [ ("testReadFromByteBuffer", testReadFromByteBuffer), diff --git a/Tests/NIOSOCKSTests/SOCKSServerHandshakeHandler+Tests+XCTest.swift b/Tests/NIOSOCKSTests/SOCKSServerHandshakeHandler+Tests+XCTest.swift index 3c6d3583..2a113ac0 100644 --- a/Tests/NIOSOCKSTests/SOCKSServerHandshakeHandler+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/SOCKSServerHandshakeHandler+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension SOCKSServerHandlerTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (SOCKSServerHandlerTests) -> () throws -> Void)] { return [ ("testTypicalWorkflow", testTypicalWorkflow), diff --git a/Tests/NIOSOCKSTests/ServerResponse+Tests+XCTest.swift b/Tests/NIOSOCKSTests/ServerResponse+Tests+XCTest.swift index 008b615c..e3b9a47e 100644 --- a/Tests/NIOSOCKSTests/ServerResponse+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/ServerResponse+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension ServerResponseTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (ServerResponseTests) -> () throws -> Void)] { return [ ("testServerResponseReadFromByteBuffer", testServerResponseReadFromByteBuffer), diff --git a/Tests/NIOSOCKSTests/ServerStateMachine+Tests+XCTest.swift b/Tests/NIOSOCKSTests/ServerStateMachine+Tests+XCTest.swift index 33c85c80..b3240baa 100644 --- a/Tests/NIOSOCKSTests/ServerStateMachine+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/ServerStateMachine+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension ServerStateMachineTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (ServerStateMachineTests) -> () throws -> Void)] { return [ ("testUsualWorkflow", testUsualWorkflow), diff --git a/Tests/NIOSOCKSTests/SocksClientHandler+Tests+XCTest.swift b/Tests/NIOSOCKSTests/SocksClientHandler+Tests+XCTest.swift index 50cab22b..4bb23e7e 100644 --- a/Tests/NIOSOCKSTests/SocksClientHandler+Tests+XCTest.swift +++ b/Tests/NIOSOCKSTests/SocksClientHandler+Tests+XCTest.swift @@ -2,7 +2,7 @@ // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) 2018-2022 Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -24,6 +24,7 @@ import XCTest extension SocksClientHandlerTests { + @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings") static var allTests : [(String, (SocksClientHandlerTests) -> () throws -> Void)] { return [ ("testTypicalWorkflow", testTypicalWorkflow), diff --git a/scripts/generate_linux_tests.rb b/scripts/generate_linux_tests.rb index b7ff919a..4794f984 100755 --- a/scripts/generate_linux_tests.rb +++ b/scripts/generate_linux_tests.rb @@ -19,6 +19,7 @@ # # Created by Tony Stone on 5/4/16. # +require 'date' require 'getoptlong' require 'fileutils' require 'pathname' @@ -28,13 +29,61 @@ # # This ruby script will auto generate LinuxMain.swift and the +XCTest.swift extension files for Swift Package Manager on Linux platforms. # +def extractCopyright(log) + startYear = 0 + endYear = 0 + + indices = log.enum_for(:scan, /(?=Date:)/).map do + Regexp.last_match.offset(0).first + end + + # If there are no years, assume this year. + if indices.count == 0 + return "#{Date.today.year}" + end + + # Return one year if there is only one year + if indices.count == 1 + year = log[indices[0]+27..indices[0]+31] + return "#{year}" + end + # Return a year range + indices.each_with_index do |ind, i| + year = log[ind+27..ind+31] + # Seed start year + if i == 0 + startYear = Integer(year) + end + + # For all other years following + if Integer(year) > endYear + endYear = Integer(year) + end + + if Integer(year) < startYear + startYear = Integer(year) + end + + end + + # If the years end up being the same + if startYear == endYear + return "#{startYear}" + end + # Otherwise, return the year range + return "#{startYear}-#{endYear}" +end + def header(fileName) + log = %x(git log --follow -p #{fileName}) + copyrightYears = extractCopyright(log).strip + string = <<-eos //===----------------------------------------------------------------------===// // // This source file is part of the SwiftNIO open source project // -// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors +// Copyright (c) #{copyrightYears} Apple Inc. and the SwiftNIO project authors // Licensed under Apache License v2.0 // // See LICENSE.txt for license information @@ -70,6 +119,7 @@ def createExtensionFile(fileName, classes) for classArray in classes file.write 'extension ' + classArray[0] + " {\n\n" + file.write ' @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' +"\n" file.write ' static var allTests : [(String, (' + classArray[0] + ") -> () throws -> Void)] {\n" file.write " return [\n" @@ -92,12 +142,18 @@ def createLinuxMain(testsDirectory, allTestSubDirectories, files) file.write header(fileName) file.write "\n" - file.write "#if os(Linux) || os(FreeBSD)\n" + file.write "#if !compiler(>=5.5)\n" + file.write "#if os(Linux) || os(FreeBSD) || os(Android)\n" for testSubDirectory in allTestSubDirectories.sort { |x, y| x <=> y } file.write ' @testable import ' + testSubDirectory + "\n" end file.write "\n" - file.write " XCTMain([\n" + file.write '@available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' + "\n" + file.write "@main\n" + file.write "class LinuxMainRunner {\n" + file.write ' @available(*, deprecated, message: "not actually deprecated. Just deprecated to allow deprecated tests (which test deprecated functionality) without warnings")' + "\n" + file.write " static func main() {\n" + file.write " XCTMain([\n" testCases = [] for classes in files @@ -107,9 +163,14 @@ def createLinuxMain(testsDirectory, allTestSubDirectories, files) end for testCase in testCases.sort { |x, y| x <=> y } - file.write ' testCase(' + testCase + ".allTests),\n" + file.write ' testCase(' + testCase + ".allTests),\n" end - file.write " ])\n" + file.write " ])\n" + file.write " }\n" + file.write "}\n" + file.write "#endif\n" + file.write "#else\n" + file.write "#error(\"on Swift 5.5 and newer, --enable-test-discovery is required\")\n" file.write "#endif\n" end end diff --git a/scripts/soundness.sh b/scripts/soundness.sh index 96136a6f..574d3bac 100755 --- a/scripts/soundness.sh +++ b/scripts/soundness.sh @@ -18,7 +18,7 @@ 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][78901]-20[12][78901]/YEARS/' -e 's/2019/YEARS/' -e 's/2020/YEARS/g' -e 's/2021/YEARS/g' + sed -e 's/20[12][789012]-20[12][789012]/YEARS/' -e 's/20[12][89012]/YEARS/' } printf "=> Checking linux tests... "