New perform with protocols and make the default client a concrete type#60
New perform with protocols and make the default client a concrete type#60FranzBusch merged 11 commits intoapple:mainfrom
Conversation
FranzBusch
left a comment
There was a problem hiding this comment.
Can we add exhaustive public documentation to every method please?
| body: consuming HTTPClientRequestBody<RequestWriter>? = nil, | ||
| options: RequestOptions = .init(), | ||
| responseHandler: (HTTPResponse, consuming ResponseConcludingReader) async throws -> Return, | ||
| configureOptions: (inout RequestOptions) -> Void = { _ in } |
There was a problem hiding this comment.
Do we really need this? Could we keep split adding this closure into a follow up PR to discuss it in isolation?
There was a problem hiding this comment.
I'd be fine proposing this in a different PR, but it would be very handy to be able to adopt options inline with perform, and I'm in support of the convenience.
There was a problem hiding this comment.
Removed for now
| url: URL, | ||
| headerFields: HTTPFields = [:], |
There was a problem hiding this comment.
This is surprising to me. Why is this not taking an HTTPRequest? AFAIK HTTPRequest has methods to convert from a URL.
There was a problem hiding this comment.
Not a deal-breaker, but I could see this as a welcome convenience - I wonder how often people will actually realize they can initialize an HTTPRequest with just a URL if they're less familiar with HTTP.
There was a problem hiding this comment.
We want HTTP.get(url) to work. Yes, it could take a request, but then there is not much difference from a regular perform.
There was a problem hiding this comment.
Also request already has a method. What if there is a mismatch, which one do we respect?
There was a problem hiding this comment.
@FranzBusch what do we do here, if the users set the http method POST on the passed in request? I'm with Guoye, that those convenience methods should bypass creating a request completely.
There was a problem hiding this comment.
Can we just subset the URL Conveniences out into a separate PR. In general, there is too much happening at once in this PR.
| UnsupportedPlatformHTTPClient() | ||
| #endif | ||
| extension HTTPClientRequestBody where Writer: ~Copyable { | ||
| public init(_ data: Data) { |
There was a problem hiding this comment.
What if we make this take a RawSpan instead of Data? This would make it more generally useful.
There was a problem hiding this comment.
We will have to make a copy since HTTPClientRequestBody needs to own the data
There was a problem hiding this comment.
I am going to argue it has to anyways and Data is the wrong type right now since it requires a FoundationEssentials dependency. I would start off with Array<UInt8> and copy.
There was a problem hiding this comment.
I removed this file for now since it's only used by the conveniences. Foundation team disliked our usage of [UInt8] in the previous iteration and told us that Data will be moved to the stdlib, so we should use Data.
| self.writeBody = writeBody | ||
| } | ||
|
|
||
| public init<OtherWriter: ~Copyable>( |
There was a problem hiding this comment.
It is to convert HTTPRequestBody<ConcreteHTTPClient> to HTTPRequestBody<URLSessionHTTPClient> for our internal backend implementation use. I can make this package instead of public?
There was a problem hiding this comment.
You can just write that in the internal backend as well right? This convenience conversion doesn't require to have internal knowledge. You can just wrap it in another closure. package is okay for now but once we split into separate packages you need to implement this with just the public API.
There was a problem hiding this comment.
It does require internal knowledge since all the closures are private. I guess I could wrap the entire struct and call produce on it, but it would be less maintainable since it might not be updated when a new case is added.
If we can erase the generic from HTTPRequestBody, this workaround wouldn't be necessary.
There was a problem hiding this comment.
Let's make it package and we can sort it out in a follow up
| #endif | ||
|
|
||
| @available(macOS 26.2, iOS 26.2, watchOS 26.2, tvOS 26.2, visionOS 26.2, *) | ||
| public struct HTTPRequestOptions: HTTPRequestOptionsRedirectionHandler, HTTPRequestOptionsTLSVersion, |
There was a problem hiding this comment.
Should we nest this type in HTTPClient or rename it to HTTPClientHTTPRequestOptions?
There was a problem hiding this comment.
This will be one of the most commonly used types, while the abstract protocol is only typed by backend implementation developers. I think we should rename the protocol instead?
| //===----------------------------------------------------------------------===// | ||
|
|
||
| @available(macOS 26.2, iOS 26.2, watchOS 26.2, tvOS 26.2, visionOS 26.2, *) | ||
| public protocol HTTPRequestOptionsDeclarativePathSelection: HTTPAPIs.HTTPRequestOptions { |
There was a problem hiding this comment.
| public protocol HTTPRequestOptionsDeclarativePathSelection: HTTPAPIs.HTTPRequestOptions { | |
| public protocol DeclarativePathSelectionHTTPRequestOptions: HTTPAPIs.HTTPRequestOptions { |
There was a problem hiding this comment.
Flipping the order makes them less discoverable. Currently they all share the same prefix and naturally group together. Maybe we could scope them instead?
There was a problem hiding this comment.
In Swift it's more common to share the suffix. And yeah, some scoping could make sense here.
There was a problem hiding this comment.
I put all protocols in a scope HTTPClientCapability which also addressed the HTTPRequestOptions naming conflict
Sources/HTTPClient/HTTPRequestOptionsDeclarativeTLSHandler.swift
Outdated
Show resolved
Hide resolved
| body: consuming HTTPClientRequestBody<RequestWriter>? = nil, | ||
| options: RequestOptions = .init(), | ||
| responseHandler: (HTTPResponse, consuming ResponseConcludingReader) async throws -> Return, | ||
| configureOptions: (inout RequestOptions) -> Void = { _ in } |
There was a problem hiding this comment.
I'd be fine proposing this in a different PR, but it would be very handy to be able to adopt options inline with perform, and I'm in support of the convenience.
| url: URL, | ||
| headerFields: HTTPFields = [:], |
There was a problem hiding this comment.
Not a deal-breaker, but I could see this as a welcome convenience - I wonder how often people will actually realize they can initialize an HTTPRequest with just a URL if they're less familiar with HTTP.
|
|
||
| public func get( | ||
| url: URL, | ||
| headerFields: HTTPFields = [:], |
There was a problem hiding this comment.
Any reason we couldn't name this parameter to just headers?
There was a problem hiding this comment.
headers is not proper terminology. A header can contain multiple fields, so "headers" is ambiguous whether it's referring to fields or multiple request headers.
There was a problem hiding this comment.
@guoye-zhang While I understand the reasoning here, I wonder if this makes more Catholic than the Pope? I assume for most users headerFields adds more confusion than just pure headers.
There was a problem hiding this comment.
Well I actually initially named the property headers on HTTPRequest and HTTPResponse, and it was called out for using improper terminology during management review. We have precedence for using the IETF-recommended terminology even on the old NSURLRequest so we'd like to avoid backsliding.
https://github.com/apple/swift-http-types/blob/main/Sources/HTTPTypes/HTTPRequest.swift#L305
https://developer.apple.com/documentation/foundation/urlrequest/value(forhttpheaderfield:)
|
|
||
| /// The namespace for all protocols defining HTTP client capabilities. | ||
| @available(macOS 26.2, iOS 26.2, watchOS 26.2, tvOS 26.2, visionOS 26.2, *) | ||
| public enum HTTPClientCapability { |
There was a problem hiding this comment.
Why do we need this namespace?
There was a problem hiding this comment.
This is from the earlier discussion: #60 (comment)
There was a problem hiding this comment.
Hm, let's go ahead with this for now then.
| url: URL, | ||
| headerFields: HTTPFields = [:], |
There was a problem hiding this comment.
Can we just subset the URL Conveniences out into a separate PR. In general, there is too much happening at once in this PR.
| UnsupportedPlatformHTTPClient() | ||
| #endif | ||
| extension HTTPClientRequestBody where Writer: ~Copyable { | ||
| public init(_ data: Data) { |
There was a problem hiding this comment.
I am going to argue it has to anyways and Data is the wrong type right now since it requires a FoundationEssentials dependency. I would start off with Array<UInt8> and copy.
| self.writeBody = writeBody | ||
| } | ||
|
|
||
| public init<OtherWriter: ~Copyable>( |
There was a problem hiding this comment.
You can just write that in the internal backend as well right? This convenience conversion doesn't require to have internal knowledge. You can just wrap it in another closure. package is okay for now but once we split into separate packages you need to implement this with just the public API.
Update the design to the current proposal