Skip to content

Add support for opting out of streaming HTTP bodies #68

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Apr 2, 2025
Merged
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 31 additions & 1 deletion Sources/OpenAPIURLSession/URLSessionTransport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,32 @@ public struct URLSessionTransport: ClientTransport {
/// - Parameter session: The URLSession used for performing HTTP operations.
/// If none is provided, the system uses the shared URLSession.
public init(session: URLSession = .shared) { self.init(session: session, implementation: .platformDefault) }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding new parameters to existing functions is an API break, as caught by CI.

1 breaking change detected in OpenAPIURLSession:
  💔 API breakage: constructor URLSessionTransport.Configuration.init(session:) has been removed

We'd need to preserve this symbol that calls through to the new one.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the issue.

The new API provides public init(session: URLSession = .shared, httpBodyProcessingMode: HTTPBodyProcessingMode = .platformDefault) - and as there is a default parameter for httpBodyProcessingMode, doesn't Swift auto generate a public init(session: URLSession) for us?

The following code in my app compiles fine:

    func testFunction() async throws {
        
        let sessionConfiguration = URLSessionConfiguration.default
        let otherSession = URLSession(configuration: sessionConfiguration)
        
        let transportConfiguration = URLSessionTransport.Configuration.init(session: .shared)
        let otherTransportConfiguration = URLSessionTransport.Configuration.init(session: otherSession)
        
        
        let transport1 = URLSessionTransport(configuration: transportConfiguration)
        let transport2 = URLSessionTransport(configuration: otherTransportConfiguration)
        
        let client1 = Client(serverURL: URL(string: "https://example.com")!, transport: transport1)
        let client2 = Client(serverURL: URL(string: "https://example.com")!, transport: transport2)
        
        _ = try await client1.get_sol_Appversions_sol_All()
        _ = try await client2.get_sol_Appversions_sol_All()
    }

So to me, there still is an (implicit) URLSessionTransport.Configuration.init(session:) - isn't this API breakage error a false positive?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a false positive. It's possible to refer to the function itself (e.g. let initializer = URLSessionTransport.Configuration.init(session:)) which won't work if the function is no longer present.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the explanation. Interesting, I wasn't aware of this limitation of default parameter values. I have added back the old single parameter init.


/// Specifies the mode in which HTTP request and response bodies are processed.
public enum HTTPBodyProcessingMode {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We generally do not use enums in public API because, right now, they are not extensible without breaking API. Instead for enum-like public API, we wrap an internal enum in a public struct with public static properties.

/// Processes the HTTP body incrementally as bytes become available.
///
/// Use this mode to handle large payloads efficiently or to begin processing
/// before the entire body has been received. Will throw a `URLSessionTransportError.streamingNotSupported`
/// error if not available on the platform.
case streamed

/// Waits until the entire HTTP body has been received before processing begins.
///
/// Use this mode when it's necessary or simpler to handle complete data payloads at once.
case buffered
}

public init(session: URLSession = .shared, httpBodyProcessingMode: HTTPBodyProcessingMode) {
self.session = session
switch httpBodyProcessingMode {

case .streamed:
self.implementation = .defaultStreaming
case .buffered:
self.implementation = .buffering
}
}

enum Implementation {
case buffering
Expand Down Expand Up @@ -354,7 +380,11 @@ extension URLSessionTransport.Configuration.Implementation {

static var platformDefault: Self {
guard platformSupportsStreaming else { return .buffering }
return .streaming(
return .defaultStreaming
}

static var defaultStreaming: Self {
.streaming(
requestBodyStreamBufferSize: 16 * 1024,
responseBodyStreamWatermarks: (low: 16 * 1024, high: 32 * 1024)
)
Expand Down
Loading