Skip to content

Custom transports

The clients do their I/O through a transport. Two protocols are defined in the core:

class HttpTransport(Protocol):
    def send(self, request: HttpRequest) -> HttpResponse: ...


class AsyncHttpTransport(Protocol):
    async def send(self, request: HttpRequest) -> HttpResponse: ...

HttpRequest has method, url, headers, body (bytes) and timeout. HttpResponse has status, headers, body (bytes) and a case-insensitive header(name) helper.

NatcashClient defaults to UrllibTransport, and AsyncNatcashClient to AsyncUrllibTransport, which runs it in asyncio.to_thread. UrllibTransport refuses redirects: a redirected POST would carry credentials to another host. Both raise TransportError on network failures.

Plugging in httpx

import httpx

from natcashify import AsyncNatcashClient, HttpRequest, HttpResponse, TransportError


class HttpxTransport:
    def __init__(self) -> None:
        self._client = httpx.AsyncClient(follow_redirects=False)

    async def send(self, request: HttpRequest) -> HttpResponse:
        try:
            response = await self._client.request(
                request.method,
                request.url,
                headers=dict(request.headers),
                content=request.body,
                timeout=request.timeout,
            )
        except httpx.HTTPError as error:
            raise TransportError("NatCash request failed") from error
        return HttpResponse(response.status_code, dict(response.headers), response.content)

    async def aclose(self) -> None:
        await self._client.aclose()


client = AsyncNatcashClient(config, transport=HttpxTransport())

If the transport has a close() (sync) or aclose() (async) method, the client calls it from its own close() or aclose().

Follow the same rules as the built-in transport: do not follow redirects, honour request.timeout, and wrap network failures in TransportError. Do not log request bodies, they contain credentials.

Fakes for tests

natcashify.core.testing provides FakeTransport, AsyncFakeTransport and json_response, see Getting started.