-
-
Notifications
You must be signed in to change notification settings - Fork 857
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
Finalising the Transport API for 1.0. #1274
Comments
I've been doing some thinking around this, and I do think we'll want to re-jig the Transport API slightly. 1. Supporting both sync & async on a single classI think we really want The next set is a bit more complex to talk through. So... 2. Optional context / extensionsWe're aware that we might at some point want to add functionality such as trailing headers #1149 or server push, that don't currently fit in to the existing API. The proposal for trailing headers was that when we got there we could essentially just bolt it on to the content streams as an additional method. We also have some really nice functionality where we can point As a result, here's what I'm thinking... def request(method, url, headers, stream, context):
return (status_code, headers, stream, context) Here, the Once you start digging there's actually quite a lot of areas where you might want to dig into additional extentions/context... Response context
Request context
The raw connection one if particularly interesting, since it would allow us to support CONNECT & Upgrade & HTTP/2 bi-directional streaming #1150 without needing a new kind of method on the transport API in order to do so. Eg... with transport.request(method, url, headers, stream, context) as response:
status_code, headers, stream, context = response
if status_code >= 400:
... # request was declined
# CONNECT requests, Upgrade requests, and HTTP/2 requests may provide
# an interface onto the raw connection. For HTTP/2 the "connection" will represent
# a single data stream. Once we have a connection we can perform
# raw I/O on it.
connection = context["connection"]
while True:
outgoing = input()
print(f">>> {outgoing}")
connection.send(outgoing.encode("utf-8")
incoming = connection.recv()
print(f"<<< {incoming}") This allows third parties to build components such as websockets or gRPC on top of httpx. It might also allow us to revisit our proxy implementations, so that they're properly compose with the connection pool, rather than subclassing it and hooking into bit of otherwise private behaviour. 3. Transport API as a context managerI think I'm fairly sold on #145. Note that 2 together with 3 mean that |
I think we probably ought to get a bit more lenient on the allowable types. For instance... We could do the same thing and...
The nice thing together with We could??? even consider adding mandatory headers automatically for the case where
# Make a request using the transport API directly.
transport = httpx.HTTPTransport()
with transport.request("GET", "https://www.example.com") as response:
status_code, headers, stream, ext = response
body = b"".join(stream) |
@tomchristie Just to confirm something I'm not yet sure about… Eventually, are we meaning to:
In case 1/ this would lead us to reconsider a big bunch of things, such as our unasync approach, and the general way we'd expect people to build transports, not thinking about breaking API changes, so… Just wanted to clarify that aspect. :) |
I don't expect us to have a single
|
0.11.1 Fixed - Add await to async semaphore release() coroutine - Drop incorrect curio classifier 0.11.0 The Transport API with 0.11.0 has a couple of significant changes. Firstly we've moved changed the request interface in order to allow extensions, which will later enable us to support features such as trailing headers, HTTP/2 server push, and CONNECT/Upgrade connections. The interface changes from: ```python def request(method, url, headers, stream, timeout): return (http_version, status_code, reason, headers, stream) ``` To instead including an optional dictionary of extensions on the request and response: ```python def request(method, url, headers, stream, ext): return (status_code, headers, stream, ext) ``` Having an open-ended extensions point will allow us to add later support for various optional features, that wouldn't otherwise be supported without these API changes. In particular: * Trailing headers support. * HTTP/2 Server Push * sendfile. * Exposing raw connection on CONNECT, Upgrade, HTTP/2 bi-di streaming. * Exposing debug information out of the API, including template name, template context. Currently extensions are limited to: * request: `timeout` - Optional. Timeout dictionary. * response: `http_version` - Optional. Include the HTTP version used on the response. * response: `reason` - Optional. Include the reason phrase used on the response. Only valid with HTTP/1.*. See encode/httpx#1274 (comment) for the history behind this. Secondly, the async version of `request` is now namespaced as `arequest`. This allows concrete transports to support both sync and async implementations on the same class. Added - Add curio support. - Add anyio support, with `backend="anyio"`. Changed - Update the Transport API to use 'ext' for optional extensions. - Update the Transport API to use `.request` and `.arequest` so implementations can support both sync and async. 0.10.2 Added - Added Unix Domain Socket support. Fixed - Always include the port on proxy CONNECT requests. - Fix `max_keepalive_connections` configuration. - Fixes behaviour in HTTP/1.1 where server disconnects can be used to signal the end of the response body. 0.10.1 - Include `max_keepalive_connections` on `AsyncHTTPProxy`/`SyncHTTPProxy` classes. 0.10.0 The most notable change in the 0.10.0 release is that HTTP/2 support is now fully optional. Use either `pip install httpcore` for HTTP/1.1 support only, or `pip install httpcore[http2]` for HTTP/1.1 and HTTP/2 support. Added - HTTP/2 support becomes optional. - Add `local_address=...` support. - Add `PlainByteStream`, `IteratorByteStream`, `AsyncIteratorByteStream`. The `AsyncByteSteam` and `SyncByteStream` classes are now pure interface classes. - Add `LocalProtocolError`, `RemoteProtocolError` exceptions. - Add `UnsupportedProtocol` exception. - Add `.get_connection_info()` method. - Add better TRACE logs. Changed - `max_keepalive` is deprecated in favour of `max_keepalive_connections`. Fixed - Improve handling of server disconnects.
Closed via #1522 |
There's a few bits of thinking around the Transport API that we'd like to be completely set on before we roll with a 1.0 release.
.request
/.arequest
. This would allow transport implementations that support both sync+async usage, which would be a really nice usability for some use-cases..request
being a context manager. See ShouldTransport.request()
return a context manager? httpcore#145 - Needs some very careful investigation.httpx.HTTPTransport(...)
rather than forcing users to drop intohttpcore
once they're configuring transports directly.To give a few examples of the effect of some of these decisions...
Expose low-level configuration options on the transport but not the client, without having gnarly extra configuration needed...
Mount a customised transport for one specific domain, without having gnarly extra configuration needed...
Transport API using
.request
and.arequest
...The text was updated successfully, but these errors were encountered: