Uh oh!
There was an error while loading. Please reload this page.
Middleware API - #1134
Conversation
johnanthonyowens
commented
Aug 8, 2020
Another use case for middleware is tracing. We’re using Datadog APM tracing in our services and in order to get trace spans for all HTTP requests I’m currently monkeypatching |
lovelydinosaur
commented
Aug 8, 2020
@johnanthonyowens It might be worth opening a separate issue to discuss that in more detail. Things that'd be useful reference points here would be...
|
ionelmc
commented
Sep 2, 2020
Is there anything I can use right now to implement a response cache? |
florimondmanca
commented
Sep 2, 2020
@ionelmc Most likely a |
lovelydinosaur
commented
Sep 3, 2020
@ionelmc A sensible first thing to do with any question like that is to start with "how would I do this with The main override points for
|
florimondmanca
commented
Sep 6, 2020
Just described a lighter form of this "middleware API" idea in the form of "interceptors", here… https://github.com/encode/httpx/issues/790#issuecomment-687823915 It's basically the same than the API proposed in this draft PR, except it's callable-based (sync functions for |
Okay, going to close this off again. For "middleware" that just wraps a request between the client and the final transport, it's already perfectly doable using the transport API, even though the instantiation pattern is a bit quirky for now (though not terrible). importhstspreloadimporthttpcoreimporthttpxclassHSTSTransport(httpcore.SyncHTTPTransport, httpcore.AsyncHTTPTransport):
""" A transport wrapper that enforces HTTPS on websites that are on the Chromium HSTS Preload list, mimicking the behavior of web browsers. """def__init__(self, transport: Union[httpcore.SyncHTTPTransport, httpcore.AsyncHTTPTransport]) ->None:
self._transport=transportdef_maybe_https_url(self, url: tuple) ->tuple:
scheme, host, port, path=urlif (
scheme==b"http"andhstspreload.in_hsts_preload(host.decode())
andlen(host.decode().split(".")) >1
):
port=Noneifport==80elseportreturnb"https", host, port, pathreturnurldefrequest(self, method, url, headers, stream, ext):
url=self._maybe_https_url(url)
returnself._transport.request(method, url, headers, stream, ext)
asyncdefarequest(self, method, url, headers, stream, ext):
url=self._maybe_https_url(url)
returnawaitself._transport.arequest(method, url, headers, stream, ext)
transport=httpx.HTTPTransport() # Soontransport=HSTSTransport(transport)
withhttpx.Client(transport=transport):
... |
johtso
commented
Nov 21, 2020
@ionelmc also, regarding response caching.. this should be usable https://github.com/johtso/httpx-caching |
And here I am pitching the idea of a middleware API again… :-)
Definitely not for 1.0, and not pressing at all, but I wanted to give this a new shot to see what this looks like now that we're closer to 1.0 (compared to eg #345).
As I mentioned in https://github.com/encode/httpx/issues/984#issuecomment-633234552, and more recently in #1110 (comment), there is a variety of use cases that don't fall in the "make a custom transport" domain, the "subclass the client without using private API" domain. In general this is transport-agnostic feature that intercepts the request/response cycle, for example...
After playing with ideas, this PR is all we would need to support a middleware API that would support the above use cases and, I believe, many more.
Core ideas are…
BaseHTTPMiddleware: a base class with "dispatch hooks" that are given theRequestand acall_nextfunction for calling into the next middleware in the stack.For example, a
ThrottleMiddlewarecould be implemented as follows:Likewise, a caching middleware could look like this:
Lastly, here's
hstspreloadback in the game:Things I'm not sure about:
middleware=[SomeMiddleware(arg=1, ...)]. I think this is okay. (We don't need an equivalent of Starlette'sMiddlewarewrapping helper, since HTTPX middleware aren't given any "parent app" on init.).send()/.asend().for m in middlewareorfor m in reverse(middleware).call_nextfunction on each request, which could be a performance burden. OTOH Starlette is able to define.call_next()statically as a method ofBaseHTTPMiddleware, and the middleware stack is built once and for all on app init. But HTTPX gets parameters that may differ on each request (auth,allow_redirects) so it seems hard to do differently. But not impossible I guess - needs some more thinking.TODO:
RetryMiddleware.