diff --git a/Package.swift b/Package.swift index 892b613..66a96bc 100644 --- a/Package.swift +++ b/Package.swift @@ -18,11 +18,11 @@ import PackageDescription var swiftSettings: [PackageDescription.SwiftSetting] = [ .enableUpcomingFeature("ExistentialAny"), .enableUpcomingFeature("InternalImportsByDefault"), + .enableExperimentalFeature("AnyAppleOSAvailability"), ] let package = Package( name: "swift-nio-http3", - platforms: [.macOS("26.0"), .iOS("26.0"), .tvOS("26.0"), .watchOS("26.0"), .visionOS("26.0")], products: [ .library(name: "NIOHTTP3", targets: ["NIOHTTP3"]) ], diff --git a/Sources/NIOHTTP3/HTTP3ConnectionHandler.swift b/Sources/NIOHTTP3/HTTP3ConnectionHandler.swift index e1d187c..b16581b 100644 --- a/Sources/NIOHTTP3/HTTP3ConnectionHandler.swift +++ b/Sources/NIOHTTP3/HTTP3ConnectionHandler.swift @@ -185,6 +185,7 @@ public final class HTTP3ConnectionHandler( eventLoop: any EventLoop, configuration: HTTP3ServerConfiguration, diff --git a/Sources/NIOHTTP3/HTTP3ConnectionMultiplexer.swift b/Sources/NIOHTTP3/HTTP3ConnectionMultiplexer.swift index 14356d1..e2b3bf7 100644 --- a/Sources/NIOHTTP3/HTTP3ConnectionMultiplexer.swift +++ b/Sources/NIOHTTP3/HTTP3ConnectionMultiplexer.swift @@ -21,6 +21,7 @@ public import NIOQUICHelpers // Allow creating outbound push streams. /// A HTTP/3 connection from a servers side. Allows iterating inbound streams. @_spi(HTTP3AsyncInterface) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct HTTP3ServerConnection: Sendable { /// Channel initializer called for each new inbound stream. private let inboundStreamInitializer: @Sendable (HTTP3StreamInitializerParameters) -> EventLoopFuture @@ -85,6 +86,7 @@ extension HTTP3ServerConnection.InboundStreams.AsyncIterator: Sendable {} // Allow iterating incoming push streams. /// A HTTP/3 connection from a clients side. Allows creating outbound request streams. @_spi(HTTP3AsyncInterface) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct HTTP3ClientConnection< Output: Sendable, StreamCreator: QUICStreamCreator & SendableMetatype @@ -144,6 +146,7 @@ package protocol StreamMultiplexerContinuation: Sendable { func finish() } +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) extension HTTP3ServerConnection: StreamMultiplexerContinuation { package func initialize(parameters: HTTP3StreamInitializerParameters) -> EventLoopFuture { self.inboundStreamInitializer(parameters).map { $0 as any Sendable } @@ -163,6 +166,7 @@ extension HTTP3ServerConnection: StreamMultiplexerContinuation { /// You can use this type to wrap a QUIC implementation. Have the QUIC implementation call ``yield(connection:)`` and ``finish()`` as appropriate. /// Then, you can easily iterate through incoming connections and handle them. @_spi(HTTP3AsyncInterface) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct HTTP3ServerConnectionMultiplexer: Sendable { /// The inboundConnections' continuation. private let inboundConnectionsContinuation: AsyncStream>.Continuation @@ -219,6 +223,7 @@ extension HTTP3ServerConnectionMultiplexer.InboundConnections.AsyncIterator: Sen /// Implementations of QUIC should implement this protocol to be able to use the HTTP3ClientConnectionMultiplexer. @_spi(HTTP3AsyncInterface) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public protocol HTTP3ConnectionCreator { /// Create a new QUIC connection using the parameters provided. /// - Parameters: @@ -235,6 +240,7 @@ public protocol HTTP3ConnectionCreator { /// Allows you to create outbound connections as a client. @_spi(HTTP3AsyncInterface) +@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) public struct HTTP3ClientConnectionMultiplexer< ConnectionCreator: HTTP3ConnectionCreator & SendableMetatype, StreamCreator: QUICStreamCreator diff --git a/Tests/H3IntegrationTests/AsyncEndToEndTests.swift b/Tests/H3IntegrationTests/AsyncEndToEndTests.swift index 5bac036..6e230ab 100644 --- a/Tests/H3IntegrationTests/AsyncEndToEndTests.swift +++ b/Tests/H3IntegrationTests/AsyncEndToEndTests.swift @@ -28,30 +28,16 @@ import struct NIOQUIC.QUICStreamCreator struct AsyncEndToEndTests { private let eventLoopGroup = MultiThreadedEventLoopGroup.singleton - private static func supportsAsyncInterface() -> Bool { - if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, visionOS 1.0, macCatalyst 13.0, *) { - return true - } else { - return false - } - } - private static let standardAuthenticationConfigurations: [AuthenticationConfiguration] = { [.keys, .certs] }() - @Test( - .enabled(if: Self.supportsAsyncInterface()), - arguments: Self.standardAuthenticationConfigurations - ) + @Test(arguments: Self.standardAuthenticationConfigurations) + @available(anyAppleOS 26, *) func asyncRoundtrip(authenticationConfiguration: AuthenticationConfiguration) async throws { let serverLogger = Logger(label: "Server") let clientLogger = Logger(label: "Client") - guard #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, visionOS 1.0, macCatalyst 13.0, *) else { - fatalError("Test shouldn't be enabled on this platform") - } - let credentials = try TestCertificates.makeCredentials(for: authenticationConfiguration) let serverName: String switch credentials { @@ -138,16 +124,13 @@ struct AsyncEndToEndTests { } } - @Test(.enabled(if: Self.supportsAsyncInterface()), arguments: Self.standardAuthenticationConfigurations) + @Test(arguments: Self.standardAuthenticationConfigurations) + @available(anyAppleOS 26, *) func goawayMidRequestWithHighID(authenticationConfiguration: AuthenticationConfiguration) async throws { // Send a goaway to the client in the middle of a request. See that the client can not make further requests. let serverLogger = Logger(label: "Server") let clientLogger = Logger(label: "Client") - guard #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, visionOS 1.0, macCatalyst 13.0, *) else { - fatalError("Test shouldn't be enabled on this platform") - } - let credentials = try TestCertificates.makeCredentials(for: authenticationConfiguration) let serverName: String switch credentials { @@ -245,17 +228,14 @@ struct AsyncEndToEndTests { try await clientChannel.close() } - @Test(.enabled(if: Self.supportsAsyncInterface()), arguments: Self.standardAuthenticationConfigurations) + @Test(arguments: Self.standardAuthenticationConfigurations) + @available(anyAppleOS 26, *) func goawayMidRequest(authenticationConfiguration: AuthenticationConfiguration) async throws { // Send a goaway to a client during a request, such that the goaway affects the request in flight. // See that the server rejects the request. let serverLogger = Logger(label: "Server") let clientLogger = Logger(label: "Client") - guard #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, visionOS 1.0, macCatalyst 13.0, *) else { - fatalError("Test shouldn't be enabled on this platform") - } - let credentials = try TestCertificates.makeCredentials(for: authenticationConfiguration) let serverName: String switch credentials { @@ -385,6 +365,7 @@ struct AsyncEndToEndTests { // MARK: - Helper Functions + @available(anyAppleOS 26, *) private func makeHTTP3Server( credentials: Credentials, settings: HTTP3Settings, @@ -473,6 +454,7 @@ struct AsyncEndToEndTests { } } + @available(anyAppleOS 26, *) private func makeHTTP3Client( credentials: Credentials, settings: HTTP3Settings, diff --git a/Tests/H3IntegrationTests/EndToEndTests.swift b/Tests/H3IntegrationTests/EndToEndTests.swift index 9ccc43c..3ea182c 100644 --- a/Tests/H3IntegrationTests/EndToEndTests.swift +++ b/Tests/H3IntegrationTests/EndToEndTests.swift @@ -218,6 +218,7 @@ struct EndToEndTests { }() @Test(arguments: Self.standardAuthenticationConfigurations) + @available(anyAppleOS 26, *) func testSettingsFrame(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -304,6 +305,7 @@ struct EndToEndTests { .disabled("See DynamicTable document in NIOHTTP3 module"), arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testRoundtripWithSlowDynamicTable(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -524,6 +526,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testRoundtripWithoutDynamicTable(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -629,6 +632,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testConnectionError(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -685,6 +689,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testServerClosesConnection(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -732,6 +737,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testClosingConnectionAlsoClosesStreams(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -786,6 +792,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testServerClosesConnectionWithActiveStream( authenticationConfiguration: AuthenticationConfiguration ) async throws { @@ -853,6 +860,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testStreamError(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -924,6 +932,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testUnknownIncomingStream(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -977,10 +986,11 @@ struct EndToEndTests { try await serverChannel.close() } + // This is always an error; clients must not open push streams to servers. @Test( arguments: Self.standardAuthenticationConfigurations ) - // This is always an error; clients must not open push streams to servers. + @available(anyAppleOS 26, *) func testIncomingPushStreamOnServer(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -1034,10 +1044,11 @@ struct EndToEndTests { try await serverChannel.close() } + // This is an error because the client hasn't specified that it wants to receive push (by sending a MAX_PUSH_ID frame) @Test( arguments: Self.standardAuthenticationConfigurations ) - // This is an error because the client hasn't specified that it wants to receive push (by sending a MAX_PUSH_ID frame) + @available(anyAppleOS 26, *) func testIncomingPushStreamOnClient(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -1124,6 +1135,7 @@ struct EndToEndTests { .disabled("See DynamicTable document in NIOHTTP3 module"), arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testBadQPACKInstruction(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -1192,10 +1204,11 @@ struct EndToEndTests { try await serverChannel.close() } + // If the connection handler gets a shouldquiesce event then the stream handlers should also @Test( arguments: Self.standardAuthenticationConfigurations ) - // If the connection handler gets a shouldquiesce event then the stream handlers should also + @available(anyAppleOS 26, *) func testForwardShouldQuiesce(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -1254,6 +1267,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testNoOutboundRequestsAfterGoawayReceived( authenticationConfiguration: AuthenticationConfiguration ) async throws { @@ -1377,6 +1391,7 @@ struct EndToEndTests { @Test( arguments: Self.standardAuthenticationConfigurations ) + @available(anyAppleOS 26, *) func testControlStreamClosed(authenticationConfiguration: AuthenticationConfiguration) async throws { let host = "127.0.0.1" @@ -1461,6 +1476,7 @@ struct EndToEndTests { // MARK: - Helper Functions + @available(anyAppleOS 26, *) private func makeServer( credentials: Credentials, host: String, @@ -1491,6 +1507,7 @@ struct EndToEndTests { ) } + @available(anyAppleOS 26, *) private func makeClient( credentials: Credentials, host: String, @@ -1515,6 +1532,7 @@ struct EndToEndTests { ) } + @available(anyAppleOS 26, *) private static func createHTTP3ClientChannel( eventLoopGroup: any EventLoopGroup, host: String, @@ -1563,6 +1581,7 @@ struct EndToEndTests { return try await httpChannelFuture.get() } + @available(anyAppleOS 26, *) private static func createHTTP3ServerChannel( eventLoopGroup: any EventLoopGroup, host: String, diff --git a/Tests/H3IntegrationTests/HTTP3+NIOQUIC.swift b/Tests/H3IntegrationTests/HTTP3+NIOQUIC.swift index 6263028..5e53b2c 100644 --- a/Tests/H3IntegrationTests/HTTP3+NIOQUIC.swift +++ b/Tests/H3IntegrationTests/HTTP3+NIOQUIC.swift @@ -28,6 +28,7 @@ import struct NIOQUIC.QUICStreamCreator typealias QUICHTTP3ConnectionHandler = HTTP3ConnectionHandler // MARK: Configure with async interface +@available(anyAppleOS 26, *) extension ChannelPipeline.SynchronousOperations { /// Setup a HTTP/3 server pipeline on a UDP channel. /// @@ -201,6 +202,7 @@ extension ChannelPipeline.SynchronousOperations { // MARK: Configure without async interface +@available(anyAppleOS 26, *) extension ChannelPipeline.SynchronousOperations { /// Setup a HTTP/3 server pipeline on a UDP channel. /// diff --git a/Tests/H3IntegrationTests/QUICConfiguration.swift b/Tests/H3IntegrationTests/QUICConfiguration.swift index 79a8d6d..16fd832 100644 --- a/Tests/H3IntegrationTests/QUICConfiguration.swift +++ b/Tests/H3IntegrationTests/QUICConfiguration.swift @@ -16,6 +16,7 @@ import class Foundation.FileManager import struct Foundation.UUID import struct NIOQUIC.QUICConfiguration +@available(anyAppleOS 26, *) extension QUICConfiguration { static func makeH3ServerConfig( serverName: String, diff --git a/Tests/H3IntegrationTests/QUICConnectionCreator.swift b/Tests/H3IntegrationTests/QUICConnectionCreator.swift index 122a0d5..2bade9b 100644 --- a/Tests/H3IntegrationTests/QUICConnectionCreator.swift +++ b/Tests/H3IntegrationTests/QUICConnectionCreator.swift @@ -20,6 +20,7 @@ import NIOQUICHelpers typealias ConcreteQUICStreamCreator = NIOQUIC.QUICStreamCreator +@available(anyAppleOS 26, *) struct QUICConnectionCreator: HTTP3ConnectionCreator { let quicHandler: QUICHandler let connectionInitializer: @Sendable (any Channel, ConcreteQUICStreamCreator) -> EventLoopFuture