Repository navigation
Add per-operation URLRequest overrides - #98
alexisbronchart wants to merge 6 commits into
Conversation
|
What are you trying to achieve here that cannot be achieved using |
|
The idea is to configure transport-specific properties on the underlying Since |
|
That makes more sense. The example in the patch is about adding headers to requests, which is not transport-specific at all. I can see now in the original PR description you mentioned I'm unsure about this approach. We don't want to either (a) reimplement the middleware, just inside a transport, or (b) reimplement a subset of the URLSession delegate. OOI, how much of what you're trying to do can be done with a delegate, if you had the ability to provide one? The transport is built using a delegate internally so might be challenging to compose, but I'm curious. |
|
I agree the example was totally confusing. I also put back the previous description. However this implementation is the only way I found to customize these transport-specific properties. The URLSession delegate does not seem to allow per-request modifications. Another way would be exposing these in the |
|
@guoye-zhang do you have an opinion on what would be the right layer to expose these kind of options? IIUC these options are per-request for folks using URLSession APIs directly with no URLSession configuration option exposed for them? |
|
Looks fine. Don’t think there’s anything better we can do. |
simonjbeaumont
left a comment
There was a problem hiding this comment.
Marking this as needs changes so it doesn't get merged as-is. Adding parameters to a public symbol is API breaking.
@simonjbeaumont do I need to make any changes? Or is this just a matter of versioning? |
|
Yes. We won't release a semver major version for this feature, so you'll need to make these changes in an API backwards compatible manner. |
|
The simplest change you can make is to just not change the |
|
@simonjbeaumont I went for the simple solution. If we ever need yet another param then I guess |
|
First off, thanks for taking this on, and the third commit does address the API break. I hadn't really spent much time reviewing the approach, just stepped in because I didn't want us to accidentally ship a break. Now I've had a closer look. I'm sympathetic to the problem -- there are a few I'm concerned this will be pretty hard to use correctly in the proposed shape. The request the closure sees isn't the same request in the two body processing modes. In Combined with Consider someone using this to write a I also don't love presenting adopters with multiple ways of doing the same thing because it hands them the question of which to use, when, and why. Most There's only a handful of You mentioned the desire for I'm wondering if we should instead allow adopters to return a value type per operation (aside: the client middleware already takes operation ID), e.g. something like this (name TBD): public struct URLRequestOverrides: Sendable {
/// Defers to the session configuration when `nil`.
public var assumesHTTP3Capable: Bool?
// ...
}
public var requestOptions: (@Sendable (_ operationID: String) -> URLRequestOverrides)? = nilWe sidestep the aforementioned issues because this closure can't operate on the body at all, and we can do all the platform dances we need (i.e. There's no arbitrary escape-hatch, but I suspect that might be the right trade here. Curious also to hear what @czechboy0 thinks. |
|
Fully behind @simonjbeaumont here - this Overrides struct approach feels much cleaner, more testable, and predictable across platforms. |
I don't. I actually wish In this new approach, do you mean to only expose properties that cannot be set on I'm confused by “Defers to the session configuration when nil”, since, for instance, |
You should file a feedback request for it.
Only those that cannot be set today. And, actually, just the one you want. This is extensible so we can expand if/when there's a need. Or not, if no need arises.
Yeah, probably a better doc. But also calling it "overrides" makes it forward-thinking because if URLSession did this configuration at the session-level, it's clear this is the per-request override, rather than just a duplicate configuration place. |
b11dad9 to
728cba1
Compare
728cba1 to
d6d1abe
Compare
|
Updated! Let me know if I got that correctly. |
simonjbeaumont
left a comment
There was a problem hiding this comment.
This is starting to take shape. Thanks for working on it. Some additional feedback. One question that still needs some thinking about w.r.t. platform reachability too.
| /// Creates overrides that leave the default URLRequest behavior unchanged. | ||
| public init() {} |
There was a problem hiding this comment.
So maintainers aren't tempted to extend this init when adding another property, I'd propose we actually don't have any public init for this type and instead off a public static var empty: Self { ... }. Happy to workshop the empty name but it'll be used like this:
(I'm also bundling in the nesting of this type and renaming it in the example):
var requestOverrides = URLSessionTransport.Configuration.RequestOverrides.empty
requestOverrides.assumesHTTP3Capable = true
// ...This will be very easy to extend should we need to support other overrides.
There was a problem hiding this comment.
I couldn't come up with anything better than empty so here goes.
| /// This override is applied on macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, | ||
| /// and later, including Mac Catalyst and visionOS. It is ignored on other | ||
| /// platforms and earlier operating system versions. | ||
| public var assumesHTTP3Capable: Bool? = nil |
There was a problem hiding this comment.
These docs are a bit overbearing. Probably worth streamlining to just say that it will be enabled if the platform supports it.
I did check to see if we can do a cute hack here with @available that we've done in some other packages:
public struct RequestOverrides: Sendable {
private var _assumesHTTP3Capable: Bool?
#if canImport(Darwin)
@available(macOS 11.3, iOS 14.5, tvOS 14.5, watchOS 7.4, *)
#else
@available(*, unavailable, message: "This option is not supported on this platform")
#endif
public var assumesHTTP3Capable: Bool? {
get { _assumesHTTP3Capable }
set { _assumesHTTP3Capable = newValue }
}
}But it means adopters have to start managing that in their projects.
@czechboy0 do you have a lean here? The choice is between a boolean that can be set on all platforms but is inert on some; or a boolean that can only be reached on platforms that support it, but with additional ceremony for adopters. WDYT?
| /// let configuration = URLSessionTransport.Configuration(requestOverrides: { operationID in | ||
| /// var overrides = URLSessionTransport.URLRequestOverrides() | ||
| /// if operationID == "getGreeting" { | ||
| /// overrides.assumesHTTP3Capable = true | ||
| /// } | ||
| /// return overrides | ||
| /// }) |
There was a problem hiding this comment.
These docs should be removed or moved to the request overrides type.
If we go with .empty then we'll also want reworking to reflect that.
| @@ -102,13 +139,31 @@ public struct URLSessionTransport: ClientTransport { | |||
|
|
|||
| init(session: URLSession = .shared, implementation: Implementation = .platformDefault) { | |||
There was a problem hiding this comment.
What I'd usually expect is for everything to funnel through to the internal init that takes all the parameters so it's easy to audit. That will mean this one takes an optional, defaulting to nil, I think.
| public init( | ||
| session: URLSession = .shared, | ||
| httpBodyProcessingMode: HTTPBodyProcessingMode = .platformDefault, | ||
| requestOverrides: @escaping @Sendable (_ operationID: String) -> URLRequestOverrides |
There was a problem hiding this comment.
Should this be optional, to match the property it sets? As it stands there's no initialiser in the set that takes an optional, so anyone whose overrides are themselves configuration-driven has to construct and then mutate.
| requestOverrides: @escaping @Sendable (_ operationID: String) -> URLRequestOverrides | |
| requestOverrides: (@Sendable (_ operationID: String) -> RequestOverrides)? = nil |
…tion initializers
simonjbeaumont
left a comment
There was a problem hiding this comment.
This looks much better to me now. Let me kick off the CI. @czechboy0 did you wanna take a look at this as it's adding API or no?
URLSessionTransport creates URLRequest values internally, so callers cannot configure Foundation-specific request properties such as
assumesHTTP3Capablewhile retaining the standard transport.Add
URLSessionTransport.URLRequestOverrideswith an optionalassumesHTTP3Capableproperty, and an optionalConfiguration.requestOverridesclosure that receives the OpenAPI operation ID and returns the overrides for that request. Anilproperty leaves the default URLRequest behavior unchanged.Both buffered and streaming paths apply the overrides when constructing the URLRequest, before creating its task. Platform and OS availability checks stay inside the transport. Existing public initializers are preserved, with a new overload accepting the closure.
Validation: 104 tests executed, 1 skipped, 0 failures on macOS. The 31 affected tests passed again after the final test adjustments. Strict formatting lint and
git diff --checkpassed.