Skip to content

Commit d889186

Browse files
authored
Introduce HTTP/3 configuration (#98)
### Motivation: [`swift-nio-quic`](https://github.com/apple/swift-nio-quic) and [`swift-nio-http3`](https://github.com/apple/swift-nio-http3) have recently been announced, paving the way for HTTP/3 support in `NIOHTTPServer`. This PR introduces HTTP/3 configuration types as a first step towards HTTP/3 support. ### Modifications: - Introduced a new `HTTP3` type under `NIOHTTPServerConfiguration` containing three sub-components, namely `QUICConfiguration`, `ProtocolConfiguration`, and `ConnectionSettings`. These types wrap over the corresponding `NIOQUIC` and `NIOHTTP3` types. - Added `swift-nio-quic`, `swift-nio-quic-helpers`, and `swift-nio-http3` dependencies. ### Result: HTTP/3 configuration types are now defined.
1 parent 0dc060b commit d889186

15 files changed

Lines changed: 709 additions & 48 deletions

.github/workflows/main.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,16 @@ jobs:
1919
linux_6_1_enabled: false
2020
linux_6_2_enabled: false
2121
linux_6_3_enabled: false
22-
# linux_6_3_arguments_override: "-Xswiftc -warnings-as-errors --explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
2322
linux_nightly_next_enabled: true
2423
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
2524
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
25+
linux_env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'
2626

2727
static-sdk:
2828
name: Static SDK
2929
uses: apple/swift-nio/.github/workflows/static_sdk.yml@main
30+
with:
31+
env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'
3032

3133
release-builds:
3234
name: Release builds
@@ -38,3 +40,4 @@ jobs:
3840
linux_6_2_enabled: false
3941
linux_6_3_enabled: false
4042
linux_nightly_next_enabled: true
43+
linux_env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'

.github/workflows/pull_request.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ on:
1010
jobs:
1111
soundness:
1212
name: Soundness
13-
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@0.0.11
13+
uses: swiftlang/github-workflows/.github/workflows/soundness.yml@497d9ab8410e69981b3b2f28da24cf7bf4b8733d
1414
with:
1515
api_breakage_check_container_image: "swiftlang/swift:nightly-6.4.x-noble"
1616
format_check_container_image: "swiftlang/swift:nightly-6.4.x-noble"
1717
license_header_check_project_name: "Swift HTTP Server"
18+
linux_pre_build_command: "export SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA=1 ENABLE_ALL_TRAITS=1"
1819

1920
unit-tests:
2021
name: Unit tests
@@ -29,6 +30,7 @@ jobs:
2930
linux_nightly_next_enabled: true
3031
linux_nightly_next_arguments_override: "--explicit-target-dependency-import-check error"
3132
linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error"
33+
linux_env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'
3234

3335
cxx-interop:
3436
name: Cxx interop
@@ -40,10 +42,13 @@ jobs:
4042
linux_6_2_enabled: false
4143
linux_6_3_enabled: false
4244
linux_nightly_next_enabled: true
45+
linux_env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'
4346

4447
static-sdk:
4548
name: Static SDK
4649
uses: apple/swift-nio/.github/workflows/static_sdk.yml@main
50+
with:
51+
env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'
4752

4853
release-builds:
4954
name: Release builds
@@ -55,3 +60,4 @@ jobs:
5560
linux_6_2_enabled: false
5661
linux_6_3_enabled: false
5762
linux_nightly_next_enabled: false # should be disabled until next is 6.4
63+
linux_env_vars: '{"SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA":"1", "ENABLE_ALL_TRAITS":"1"}'

Package.swift

Lines changed: 56 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@
1515

1616
import PackageDescription
1717

18+
#if canImport(FoundationEssentials)
19+
import FoundationEssentials
20+
#else
21+
import Foundation
22+
#endif
23+
1824
let extraSettings: [SwiftSetting] = [
1925
.strictMemorySafety(),
2026
.enableExperimentalFeature("SuppressedAssociatedTypesWithDefaults"),
@@ -28,6 +34,44 @@ let extraSettings: [SwiftSetting] = [
2834
.enableUpcomingFeature("InternalImportsByDefault"),
2935
]
3036

37+
var traits: Set<Trait> = [
38+
.trait(
39+
name: "Configuration",
40+
description: "Enables initializing NIOHTTPServerConfiguration from a swift-configuration ConfigProvider"
41+
),
42+
.trait(
43+
name: "HTTP3",
44+
description: "Enables HTTP/3 support"
45+
),
46+
]
47+
48+
let defaultTraits: Set<String> = ["Configuration"]
49+
50+
// Workaround to ensure that all traits are included in documentation. Swift Package Index adds SPI_GENERATE_DOCS
51+
// (https://github.com/SwiftPackageIndex/SwiftPackageIndex-Server/issues/2336) when building documentation, so only
52+
// tweak the default traits in this condition.
53+
let spiGenerateDocs = ProcessInfo.processInfo.environment["SPI_GENERATE_DOCS"] != nil
54+
55+
// Conditionally add the swift-docc plugin only when previewing docs locally.
56+
// Preview with:
57+
// ```
58+
// SWIFT_PREVIEW_DOCS=1 swift package --disable-sandbox preview-documentation --target NIOHTTPServer
59+
// ```
60+
let previewDocs = ProcessInfo.processInfo.environment["SWIFT_PREVIEW_DOCS"] != nil
61+
62+
// Enable all traits for other CI actions.
63+
let enableAllTraitsExplicit = ProcessInfo.processInfo.environment["ENABLE_ALL_TRAITS"] != nil
64+
65+
let enableAllTraits = spiGenerateDocs || previewDocs || enableAllTraitsExplicit
66+
let addDoccPlugin = previewDocs || spiGenerateDocs
67+
let enableAllCIFlags = enableAllTraitsExplicit
68+
69+
traits.insert(
70+
.default(
71+
enabledTraits: enableAllTraits ? Set(traits.map(\.name)) : defaultTraits
72+
),
73+
)
74+
3175
let package = Package(
3276
name: "swift-http-server",
3377
platforms: [ // TODO: Needed until https://github.com/swiftlang/swift/issues/89028 is fixed
@@ -43,19 +87,19 @@ let package = Package(
4387
targets: ["NIOHTTPServer"]
4488
)
4589
],
46-
traits: [
47-
.trait(name: "Configuration"),
48-
.default(enabledTraits: ["Configuration"]),
49-
],
90+
traits: traits,
5091
dependencies: [
5192
.package(
5293
url: "https://github.com/apple/swift-http-api-proposal.git",
5394
.upToNextMinor(from: "0.2.0")
5495
),
5596
.package(url: "https://github.com/apple/swift-distributed-tracing.git", from: "1.4.1"),
56-
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.19.1"),
97+
.package(url: "https://github.com/apple/swift-certificates.git", from: "1.19.3"),
5798
.package(url: "https://github.com/apple/swift-log.git", from: "1.14.0"),
5899
.package(url: "https://github.com/apple/swift-nio.git", from: "2.101.3"),
100+
.package(url: "https://github.com/apple/swift-nio-quic.git", .upToNextMinor(from: "0.1.0")),
101+
.package(url: "https://github.com/apple/swift-nio-quic-helpers.git", .upToNextMinor(from: "0.1.0")),
102+
.package(url: "https://github.com/apple/swift-nio-http3.git", .upToNextMinor(from: "0.1.0")),
59103
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.37.0"),
60104
.package(url: "https://github.com/apple/swift-nio-extras.git", from: "1.34.1"),
61105
.package(url: "https://github.com/apple/swift-nio-http2.git", from: "1.44.0"),
@@ -110,6 +154,13 @@ let package = Package(
110154
condition: .when(traits: ["Configuration"])
111155
),
112156
.product(name: "NIOExtras", package: "swift-nio-extras"),
157+
.product(name: "NIOQUIC", package: "swift-nio-quic", condition: .when(traits: ["HTTP3"])),
158+
.product(
159+
name: "NIOQUICHelpers",
160+
package: "swift-nio-quic-helpers",
161+
condition: .when(traits: ["HTTP3"])
162+
),
163+
.product(name: "NIOHTTP3", package: "swift-nio-http3", condition: .when(traits: ["HTTP3"])),
113164
.product(name: "HTTPAPIs", package: "swift-http-api-proposal"),
114165
],
115166
swiftSettings: extraSettings

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,24 @@ This package offers additional integrations you can enable using
2121
Available traits:
2222
- **`Configuration`** (default): Enables initializing `NIOHTTPServerConfiguration` from a `swift-configuration`
2323
`ConfigProvider`.
24+
- **`HTTP3`**: Enables HTTP/3 support.
25+
26+
## HTTP/3 support
27+
28+
Packages in the dependency tree depend on a beta release of swift-crypto.
29+
Set the environment variable
30+
`SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA` to allow swift-certificates
31+
(in the dependency tree) to adopt swift-crypto beta releases as well.
32+
33+
```
34+
SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA=1 swift build
35+
```
36+
37+
To run all unit tests, run
38+
39+
```
40+
SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA=1 swift test
41+
```
42+
43+
Use `SWIFT_CERTIFICATES_ALLOW_SWIFT_CRYPTO_BETA=1 xed Package.swift` to open
44+
the project in Xcode with the environment variable set.

Sources/ConnectionHandlerExample/ConnectionHandlerExample.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ struct ConnectionHandlerExample {
6868
// handler and the request handler are inline closures.
6969
try await server.serve { connection, context in
7070
var connection = Optional(connection)
71-
try await withLogger(mergingMetadata: [
71+
await withLogger(mergingMetadata: [
7272
"peer": .string(context.remoteAddress.map { "\($0)" } ?? "unknown"),
7373
"http": .string(context.httpVersion.rawValue),
7474
]) { connectionLogger in
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift HTTP Server open source project
4+
//
5+
// Copyright (c) 2026 Apple Inc. and the Swift HTTP Server project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of Swift HTTP Server project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#if HTTP3
16+
import HTTP3
17+
18+
@available(anyAppleOS 26.0, *)
19+
extension NIOHTTPServerConfiguration.HTTP3 {
20+
/// HTTP/3 connection settings sent to the peer during connection establishment.
21+
public struct ConnectionSettings: Sendable, Hashable {
22+
/// The maximum capacity of the QPACK dynamic table.
23+
///
24+
/// - SeeAlso: https://www.rfc-editor.org/rfc/rfc9204.html#section-5-2.2.1. Corresponds to
25+
/// `SETTINGS_QPACK_MAX_TABLE_CAPACITY`.
26+
public var qpackMaximumTableCapacity: UInt64
27+
28+
/// The maximum number of streams which may be blocked on QPACK at any one time.
29+
///
30+
/// - SeeAlso: https://www.rfc-editor.org/rfc/rfc9204.html#section-5-2.4.1. Corresponds to
31+
/// `SETTINGS_QPACK_BLOCKED_STREAMS`.
32+
public var qpackBlockedStreams: UInt64
33+
34+
/// The maximum size of a field section.
35+
///
36+
/// - SeeAlso: https://www.rfc-editor.org/rfc/rfc9114.html#section-7.2.4.1-2.2.1. Corresponds to
37+
/// `SETTINGS_MAX_FIELD_SECTION_SIZE`.
38+
public var maximumFieldSectionSize: UInt64?
39+
40+
/// The default HTTP/3 connection settings configuration.
41+
///
42+
/// Uses the following default values:
43+
/// - `qpackMaximumTableCapacity`: 0.
44+
/// - `qpackBlockedStreams`: 0.
45+
/// - `maximumFieldSectionSize`: `nil` (no field section size limit).
46+
public static var defaults: Self {
47+
Self(
48+
qpackMaximumTableCapacity: 0,
49+
qpackBlockedStreams: 0,
50+
maximumFieldSectionSize: nil
51+
)
52+
}
53+
}
54+
}
55+
56+
@available(anyAppleOS 26.0, *)
57+
extension HTTP3.HTTP3Settings {
58+
init(_ configuration: NIOHTTPServerConfiguration.HTTP3.ConnectionSettings) {
59+
self.init(
60+
qpackMaximumTableCapacity: configuration.qpackMaximumTableCapacity,
61+
qpackBlockedStreams: configuration.qpackBlockedStreams,
62+
maximumFieldSectionSize: configuration.maximumFieldSectionSize
63+
)
64+
}
65+
}
66+
#endif // HTTP3

0 commit comments

Comments
 (0)