Skip to content

Commit

Permalink
Improve 'Custom transports' docs (#3081)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomchristie committed Feb 14, 2024
1 parent c51af4b commit 3faa4a8
Showing 1 changed file with 69 additions and 16 deletions.
85 changes: 69 additions & 16 deletions docs/advanced/transports.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ HTTPX's `Client` also accepts a `transport` argument. This argument allows you
to provide a custom Transport object that will be used to perform the actual
sending of the requests.

## HTTPTransport
## HTTP Transport

For some advanced configuration you might need to instantiate a transport
class directly, and pass it to the client instance. One example is the
Expand Down Expand Up @@ -83,7 +83,7 @@ with httpx.Client(transport=transport, base_url="http://testserver") as client:
...
```

## ASGITransport
## ASGI Transport

You can configure an `httpx` client to call directly into an async Python web application using the ASGI protocol.

Expand Down Expand Up @@ -148,7 +148,7 @@ However it is suggested to use `LifespanManager` from [asgi-lifespan](https://gi

## Custom transports

A transport instance must implement the low-level Transport API, which deals
A transport instance must implement the low-level Transport API which deals
with sending a single request, and returning a response. You should either
subclass `httpx.BaseTransport` to implement a transport to use with `Client`,
or subclass `httpx.AsyncBaseTransport` to implement a transport to
Expand All @@ -166,28 +166,81 @@ A complete example of a custom transport implementation would be:
import json
import httpx


class HelloWorldTransport(httpx.BaseTransport):
"""
A mock transport that always returns a JSON "Hello, world!" response.
"""

def handle_request(self, request):
message = {"text": "Hello, world!"}
content = json.dumps(message).encode("utf-8")
stream = httpx.ByteStream(content)
headers = [(b"content-type", b"application/json")]
return httpx.Response(200, headers=headers, stream=stream)
return httpx.Response(200, json={"text": "Hello, world!"})
```

Which we can use in the same way:
Or this example, which uses a custom transport and `httpx.Mounts` to always redirect `http://` requests.

```pycon
>>> import httpx
>>> client = httpx.Client(transport=HelloWorldTransport())
>>> response = client.get("https://example.org/")
>>> response.json()
{"text": "Hello, world!"}
```python
class HTTPSRedirect(httpx.BaseTransport):
"""
A transport that always redirects to HTTPS.
"""
def handle_request(self, request):
url = request.url.copy_with(scheme="https")
return httpx.Response(303, headers={"Location": str(url)})

# A client where any `http` requests are always redirected to `https`
transport = httpx.Mounts({
'http://': HTTPSRedirect()
'https://': httpx.HTTPTransport()
})
client = httpx.Client(transport=transport)
```

A useful pattern here is custom transport classes that wrap the default HTTP implementation. For example...

```python
class DebuggingTransport(httpx.BaseTransport):
def __init__(self, **kwargs):
self._wrapper = httpx.HTTPTransport(**kwargs)

def handle_request(self, request):
print(f">>> {request}")
response = self._wrapper.handle_request(request)
print(f"<<< {response}")
return response

def close(self):
self._wrapper.close()

transport = DebuggingTransport()
client = httpx.Client(transport=transport)
```

Here's another case, where we're using a round-robin across a number of different proxies...

```python
class ProxyRoundRobin(httpx.BaseTransport):
def __init__(self, proxies, **kwargs):
self._transports = [
httpx.HTTPTransport(proxy=proxy, **kwargs)
for proxy in proxies
]
self._idx = 0

def handle_request(self, request):
transport = self._transports[self._idx]
self._idx = (self._idx + 1) % len(self._transports)
return transport.handle_request(request)

def close(self):
for transport in self._transports:
transport.close()

proxies = [
httpx.Proxy("http://127.0.0.1:8081"),
httpx.Proxy("http://127.0.0.1:8082"),
httpx.Proxy("http://127.0.0.1:8083"),
]
transport = ProxyRoundRobin(proxies=proxies)
client = httpx.Client(transport=transport)
```

## Mock transports
Expand Down

0 comments on commit 3faa4a8

Please sign in to comment.