Skip to content

Commit 686df72

Browse files
Add support for streaming on recent Darwin platforms (#24)
### Motivation Recently, the `ClientTransport` protocol was updated to be in terms of `HTTPBody`, which is an `AsyncSequence<ArraySlice<UInt8>>`. Until now, the URLSession transport has buffered the request and response bodies in memory. This PR seeks to bring streaming support to recent Darwin platforms. ### Modifications On new enough Darwin platforms[^1], use URLSession delegate to perform bidirectional streaming with backpressure. Request body backpressure is provided by the use of `StreamDelegate` with a bound stream pair. Response body backpressure is provided by the use of `AsyncBackpressuredStream` which allows for a high and low watermark to suspend and resume the URLSession task. [^1]: macOS 12, iOS 15, tvOS 15, watchOS 8 In more detail: - Vendor internal `AsyncBackpressuredStream` implementation from SE-0406. - Add `HTTPBodyOutputStreamBridge` which provides the `OutputStreamDelegate` required to bridge the `AsyncSequence`-based `HTTPBody` to an `OutputStream`. - Add `BidirectionalStreamingURLSessionDelegate` which makes use of `HTTPBodyOutputStreamBridge` for streaming request bodies and also uses the delegate callbacks to bridge the response body to `HTTPBody`. - Add `URLSession.bidirectionalStreamingRequest(...) async throws`, which provides a high-level URLSession-like API for setting up the appropriate URLSession task and delegate. - Add `URLSession.bufferedRequest(...) async throws` to provide a similar interfaces on platforms that don't support the streaming APIs on which we depend. - Add internal `enum URLSessionTransport.Configuration.Implementation` to control whether to use buffered or streaming implementation. - Detect appropriate implementation depending on the platform. ### Result - On new enough Darwin platforms[^1], streaming will be used. ### Test Plan - Add a set of tests that run with both buffered and streaming implementation on platforms that support streaming. - Add a set of tests that only run on platforms that support streaming, to test request flows only possible with streaming. However, it's worth noting that our CI only runs on Linux, so we won't be testing the majority of this new feature in CI.
1 parent 70b63ef commit 686df72

20 files changed

+3914
-184
lines changed

NOTICE.txt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,21 @@ This product contains derivations of various scripts from SwiftNIO.
3232
* https://www.apache.org/licenses/LICENSE-2.0
3333
* HOMEPAGE:
3434
* https://github.com/apple/swift-nio
35+
36+
-------------------------------------------------------------------------------
37+
38+
This product contains AsyncSequence implementations from Swift Async Algorithms.
39+
40+
* LICENSE (Apache License 2.0):
41+
* https://github.com/apple/swift-async-algorithms/blob/main/LICENSE.txt
42+
* HOMEPAGE:
43+
* https://github.com/apple/swift-async-algorithms
44+
45+
-------------------------------------------------------------------------------
46+
47+
This product contains AsyncSequence implementations from Swift.
48+
49+
* LICENSE (Apache License 2.0):
50+
* https://github.com/apple/swift/blob/main/LICENSE.txt
51+
* HOMEPAGE:
52+
* https://github.com/apple/swift

Package.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,16 @@ swiftSettings.append(
2424
// Require `any` for existential types.
2525
.enableUpcomingFeature("ExistentialAny")
2626
)
27+
28+
// Strict concurrency is enabled in CI; use this environment variable to enable it locally.
29+
if ProcessInfo.processInfo.environment["SWIFT_OPENAPI_STRICT_CONCURRENCY"].flatMap(Bool.init) ?? false {
30+
swiftSettings.append(contentsOf: [
31+
.define("SWIFT_OPENAPI_STRICT_CONCURRENCY"), .enableExperimentalFeature("StrictConcurrency"),
32+
])
33+
}
2734
#endif
2835

36+
2937
let package = Package(
3038
name: "swift-openapi-urlsession",
3139
platforms: [
@@ -40,19 +48,29 @@ let package = Package(
4048
dependencies: [
4149
.package(url: "https://github.com/apple/swift-openapi-runtime", .upToNextMinor(from: "0.3.0")),
4250
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.0.0"),
51+
.package(url: "https://github.com/apple/swift-collections", from: "1.0.0"),
4352
],
4453
targets: [
4554
.target(
4655
name: "OpenAPIURLSession",
4756
dependencies: [
57+
.product(name: "DequeModule", package: "swift-collections"),
4858
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime"),
4959
],
5060
swiftSettings: swiftSettings
5161
),
5262
.testTarget(
5363
name: "OpenAPIURLSessionTests",
54-
dependencies: ["OpenAPIURLSession"],
64+
dependencies: [
65+
"OpenAPIURLSession",
66+
.product(name: "NIOTestUtils", package: "swift-nio"),
67+
],
5568
swiftSettings: swiftSettings
5669
),
5770
]
5871
)
72+
73+
// Test-only dependencies.
74+
package.dependencies += [
75+
.package(url: "https://github.com/apple/swift-nio", from: "2.62.0")
76+
]

README.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,14 @@ A client transport that uses the [URLSession](https://developer.apple.com/docume
99
Use the transport with client code generated by [Swift OpenAPI Generator](https://github.com/apple/swift-openapi-generator).
1010

1111
## Supported platforms and minimum versions
12-
| macOS | Linux | iOS | tvOS | watchOS |
13-
| :-: | :-: | :-: | :-: | :-: |
14-
| ✅ 10.15+ || ✅ 13+ | ✅ 13+ | ✅ 6+ |
12+
13+
| macOS | Linux | iOS | tvOS | watchOS |
14+
| :-: | :-: | :-: | :-: | :-: |
15+
| ✅ 10.15+ || ✅ 13+ | ✅ 13+ | ✅ 6+ |
16+
17+
Note: Streaming support only available on macOS 12+, iOS 15+, tvOS 15+, and
18+
watchOS 8+.For streaming support on Linux, please use the [AsyncHTTPClient
19+
Transport](https://github.com/swift-server/swift-openapi-async-http-client)
1520

1621
## Usage
1722

0 commit comments

Comments
 (0)