Hi, everyone!
Building on the original pitch by @tachyonics (Generic HTTP client/server library), I would like to formally pitch an HTTP client library, built on top of SwiftNIO.
API could look like this:
class HTTPClient {
public func get(url: String, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
}
public func post(url: String, body: Body? = nil, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
}
public func put(url: String, body: Body? = nil, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
}
public func delete(url: String, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
}
public func execute(request: Request, timeout: Timeout? = nil) -> EventLoopFuture<Response> {
}
}
where HTTPClient.Request
and HTTPClient.Response
are:
extension HTTPClient {
enum Body {
case byteBuffer(ByteBuffer)
case data(Data)
case string(String)
}
struct Request {
public var version: HTTPVersion
public var method: HTTPMethod
public var url: URL
public var headers: HTTPHeaders
public var body: Body?
}
struct Response {
public var host: String
public var status: HTTPResponseStatus
public var headers: HTTPHeaders
public var body: ByteBuffer?
}
}
If one needs greater control over the response, there is HTTPResponseDelegate
with the following protocol:
public protocol HTTPClientResponseDelegate: class {
associatedtype Response
// this method will be called when request body is sent
func didTransmitRequestBody(task: HTTPClient.Task<Response>)
// this method will be called when we receive Head response, with headers and status code
func didReceiveHead(task: HTTPClient.Task<Response>, _ head: HTTPResponseHead)
// this method will be called multiple times with chunks of the HTTP response body (if there is a body)
func didReceivePart(task: HTTPClient.Task<Response>, _ buffer: ByteBuffer)
// this method will be called if an error occurs during request processing
func didReceiveError(task: HTTPClient.Task<Response>, _ error: Error)
// this will be called when HTTP response is read fully
func didFinishRequest(task: HTTPClient.Task<Response>) throws -> Response
}
extension HTTPClient {
public func execute<T: HTTPClientResponseDelegate>(request: Request, delegate: T, timeout: Timeout? = nil) -> Task<T.Response> {
}
}
(Task
is just a wrapper for EventLoopFuture
that in addition to that future provides a way to cancel current request)
To make this proposal concrete, we've built first iteration of such a client:
This client supports the following:
- Asynchronous and non-blocking request methods
- Simple follow-redirects (cookie headers are dropped)
- Streaming body download
- TLS support
- Cookie parsing (but not storage)
What kind of API would you like to see? What functionality would be sufficient to call this 1.0.0?
We also hope that this project would be mainly SSWG/community-driven, and not by just by one party.