Skip to content

without_asgi

without adapters that turn an ASGI app's receive/send into typed event streams and back.

without_asgi

HttpHandler

HttpHandler = Processor[Inbound, Outbound]

HttpRouter

HttpRouter = Callable[[T, HttpScope], HttpHandler]

Lifespan

WebsocketHandler

WebsocketRouter

WebsocketRouter = Callable[
    [T, WebsocketScope], WebsocketHandler
]

Inbound

Inbound = RequestBody | Disconnect

LifespanEvent

LifespanEvent = Startup | Shutdown

WebsocketInbound

LifespanReply

ConnectionScope

ConnectionScope = HttpScope | WebsocketScope

Scope

ASGIApp

ASGIApp = Callable[
    [RawScope, Receive, Send], Awaitable[None]
]

RawHeaders

RawHeaders = tuple[tuple[bytes, bytes], ...]

RawMessage

RawMessage = Mapping[str, object]

RawScope

RawScope = Mapping[str, object]

Receive

Receive = Callable[[], Awaitable[RawMessage]]

Send

Send = Callable[[RawMessage], Awaitable[None]]

WebsocketData

WebsocketData = WebsocketText | WebsocketBinary

Disconnect dataclass

Disconnect()

The client went away before the request finished.

RequestBody dataclass

RequestBody(body: bytes, more_body: bool)

body instance-attribute

body: bytes

more_body instance-attribute

more_body: bool

Shutdown dataclass

Shutdown()

Startup dataclass

Startup()

WebsocketConnect dataclass

WebsocketConnect()

The client is opening a websocket and awaiting an accept or a close.

WebsocketDisconnect dataclass

WebsocketDisconnect(code: int, reason: str)

code instance-attribute

code: int

reason instance-attribute

reason: str

WebsocketReceive dataclass

WebsocketReceive(data: WebsocketData)

data instance-attribute

EarlyHint dataclass

EarlyHint(links: tuple[bytes, ...])

A 103 Early Hints informational response (http.response.early_hint extension).

links: tuple[bytes, ...]

PathSend dataclass

PathSend(path: str)

Offload sending a file by absolute path (http.response.pathsend extension).

path instance-attribute

path: str

Response dataclass

Response(
    status: int, headers: RawHeaders = (), body: bytes = b""
)

A whole response as one value, the common case behind the event pair.

status instance-attribute

status: int

headers class-attribute instance-attribute

headers: RawHeaders = ()

body class-attribute instance-attribute

body: bytes = b''

ResponseBody dataclass

ResponseBody(body: bytes = b'', more_body: bool = False)

body class-attribute instance-attribute

body: bytes = b''

more_body class-attribute instance-attribute

more_body: bool = False

ResponseDebug dataclass

ResponseDebug(info: Mapping[str, object])

Debug information sent before the response start (http.response.debug extension).

info instance-attribute

info: Mapping[str, object]

ResponseStart dataclass

ResponseStart(
    status: int,
    headers: RawHeaders = (),
    trailers: bool = False,
)

status instance-attribute

status: int

headers class-attribute instance-attribute

headers: RawHeaders = ()

trailers class-attribute instance-attribute

trailers: bool = False

Whether the app will send a ResponseTrailers after the body; requires the http.response.trailers extension.

ResponseTrailers dataclass

ResponseTrailers(
    headers: RawHeaders, more_trailers: bool = False
)

Trailing headers sent after the final body (http.response.trailers extension).

headers instance-attribute

headers: RawHeaders

more_trailers class-attribute instance-attribute

more_trailers: bool = False

ServerPush dataclass

ServerPush(path: str, headers: RawHeaders)

An HTTP/2 server push (http.response.push extension).

path instance-attribute

path: str

headers instance-attribute

headers: RawHeaders

ShutdownComplete dataclass

ShutdownComplete()

ShutdownFailed dataclass

ShutdownFailed(message: str = '')

message class-attribute instance-attribute

message: str = ''

StartupComplete dataclass

StartupComplete()

StartupFailed dataclass

StartupFailed(message: str = '')

message class-attribute instance-attribute

message: str = ''

SupportsFileno

Bases: Protocol

An opened file object backed by a real OS file descriptor (for os.sendfile).

fileno

fileno() -> int

WebsocketAccept dataclass

WebsocketAccept(
    subprotocol: str | None = None, headers: RawHeaders = ()
)

subprotocol class-attribute instance-attribute

subprotocol: str | None = None

headers class-attribute instance-attribute

headers: RawHeaders = ()

WebsocketClose dataclass

WebsocketClose(code: int = 1000, reason: str = '')

Close a websocket connection, or reject it when sent before WebsocketAccept.

code is a WebSocket close code; 1000 is a normal closure. If sent before the handshake is accepted, the server discards code/reason and returns an HTTP 403 instead, so these only reach the client on a close after accept.

code class-attribute instance-attribute

code: int = 1000

reason class-attribute instance-attribute

reason: str = ''

WebsocketResponseBody dataclass

WebsocketResponseBody(
    body: bytes = b"", more_body: bool = False
)

A body chunk of an HTTP denial response (websocket.http.response extension).

body class-attribute instance-attribute

body: bytes = b''

more_body class-attribute instance-attribute

more_body: bool = False

WebsocketResponseStart dataclass

WebsocketResponseStart(
    status: int, headers: RawHeaders = ()
)

The start of an HTTP denial response (websocket.http.response extension).

status instance-attribute

status: int

headers class-attribute instance-attribute

headers: RawHeaders = ()

WebsocketSend dataclass

WebsocketSend(data: WebsocketData)

data instance-attribute

ZeroCopySend dataclass

ZeroCopySend(
    file: SupportsFileno,
    offset: int | None = None,
    count: int | None = None,
    more_body: bool = False,
)

A zero-copy file-descriptor send (http.response.zerocopysend extension).

The application is responsible for closing file afterwards.

file instance-attribute

offset class-attribute instance-attribute

offset: int | None = None

count class-attribute instance-attribute

count: int | None = None

more_body class-attribute instance-attribute

more_body: bool = False

Asgi dataclass

Asgi(version: str, spec_version: str)

The ASGI version metadata carried on every scope's asgi key.

version instance-attribute

version: str

Version of the ASGI spec.

spec_version instance-attribute

spec_version: str

Version of the ASGI sub-spec this server implements.

HttpScope dataclass

HttpScope(
    asgi: Asgi,
    http_version: str,
    method: str,
    scheme: str,
    path: str,
    raw_path: bytes | None,
    query_string: bytes,
    root_path: str,
    headers: RawHeaders,
    client: tuple[str, int] | None,
    server: tuple[str, int | None] | None,
    extensions: Mapping[str, Mapping[str, object]] | None,
)

The per-request connection facts, known once when the request opens.

Field descriptions are taken from the ASGI HTTP connection scope: https://asgi.readthedocs.io/en/latest/specs/www.html#http-connection-scope

The ASGI state namespace is intentionally not surfaced: without threads lifespan-derived state to handlers explicitly through make_asgi_app, rather than reading it from the scope.

asgi instance-attribute

asgi: Asgi

Version metadata for the ASGI spec and HTTP sub-spec.

http_version instance-attribute

http_version: str

One of "1.0", "1.1" or "2".

method instance-attribute

method: str

The HTTP method name, uppercased.

scheme instance-attribute

scheme: str

URL scheme portion (likely "http" or "https"); defaults to "http".

path instance-attribute

path: str

HTTP request target excluding any query string, with percent-encoded sequences and UTF-8 byte sequences decoded into characters.

raw_path instance-attribute

raw_path: bytes | None

The original HTTP path component as the bytes the web server received, excluding any query string; None if the server cannot provide it.

query_string instance-attribute

query_string: bytes

URL portion after the ?, percent-encoded.

root_path instance-attribute

root_path: str

The root path this application is mounted at (WSGI SCRIPT_NAME); defaults to "".

headers instance-attribute

headers: RawHeaders

[name, value] byte-string header pairs, in the order received; duplicates are preserved.

client instance-attribute

client: tuple[str, int] | None

Remote [host, port]; None if not provided.

server instance-attribute

server: tuple[str, int | None] | None

Server [host, port], or [path, None] for a unix socket; None if not provided.

extensions instance-attribute

extensions: Mapping[str, Mapping[str, object]] | None

Server-advertised optional extensions, keyed by name; None if the server advertised none. See parse_tls for reading the tls extension.

LifespanScope dataclass

LifespanScope(asgi: Asgi)

The server lifecycle scope, shared across the whole event loop.

Field descriptions are taken from the ASGI lifespan scope: https://asgi.readthedocs.io/en/latest/specs/lifespan.html#scope

The ASGI state namespace is intentionally not surfaced: without threads lifespan-derived state to handlers explicitly through make_asgi_app (which holds it in a _Cell and passes it per request), rather than via the scope.

asgi instance-attribute

asgi: Asgi

Version metadata for the ASGI spec and lifespan sub-spec.

Tls dataclass

Tls(
    server_cert: str | None,
    client_cert_chain: tuple[str, ...],
    client_cert_name: str | None,
    client_cert_error: str | None,
    tls_version: int | None,
    cipher_suite: int | None,
)

The tls extension's connection info, present only on TLS connections.

Field descriptions are taken from the ASGI TLS extension: https://asgi.readthedocs.io/en/latest/specs/tls.html

server_cert instance-attribute

server_cert: str | None

PEM-encoded server certificate; None if the server cannot provide it.

client_cert_chain instance-attribute

client_cert_chain: tuple[str, ...]

PEM-encoded client certificate chain (client cert first); empty if none.

client_cert_name instance-attribute

client_cert_name: str | None

RFC4514 Distinguished Name of the client certificate subject; None if no client certificate.

client_cert_error instance-attribute

client_cert_error: str | None

Verification error message if a client certificate failed validation; None if it verified or none was provided.

tls_version instance-attribute

tls_version: int | None

TLS version number (e.g. 0x0304 for TLS 1.3); None if not in use.

cipher_suite instance-attribute

cipher_suite: int | None

16-bit cipher suite identifier in network byte order; None if not provided.

WebsocketScope dataclass

WebsocketScope(
    asgi: Asgi,
    http_version: str,
    scheme: str,
    path: str,
    raw_path: bytes | None,
    query_string: bytes,
    root_path: str,
    headers: RawHeaders,
    client: tuple[str, int] | None,
    server: tuple[str, int | None] | None,
    subprotocols: tuple[str, ...],
    extensions: Mapping[str, Mapping[str, object]] | None,
)

The handshake facts of a websocket connection, known when it opens.

Field descriptions are taken from the ASGI WebSocket connection scope: https://asgi.readthedocs.io/en/latest/specs/www.html#websocket-connection-scope

The ASGI state namespace is intentionally not surfaced: without threads lifespan-derived state to handlers explicitly through make_asgi_app, rather than reading it from the scope.

asgi instance-attribute

asgi: Asgi

Version metadata for the ASGI spec and WebSocket sub-spec.

http_version instance-attribute

http_version: str

One of "1.1" or "2"; defaults to "1.1".

scheme instance-attribute

scheme: str

URL scheme portion (likely "ws" or "wss"); defaults to "ws".

path instance-attribute

path: str

HTTP request target excluding any query string, with percent-encoded sequences and UTF-8 byte sequences decoded into characters.

raw_path instance-attribute

raw_path: bytes | None

The original HTTP path component as the bytes the web server received, excluding any query string; None if the server cannot provide it.

query_string instance-attribute

query_string: bytes

URL portion after the ?; defaults to empty.

root_path instance-attribute

root_path: str

The root path this application is mounted at (WSGI SCRIPT_NAME); defaults to "".

headers instance-attribute

headers: RawHeaders

[name, value] byte-string header pairs, in the order received; duplicates are preserved.

client instance-attribute

client: tuple[str, int] | None

Remote [host, port]; None if not provided.

server instance-attribute

server: tuple[str, int | None] | None

Server [host, port], or [path, None] for a unix socket; None if not provided.

subprotocols instance-attribute

subprotocols: tuple[str, ...]

Subprotocols the client advertised; defaults to empty.

extensions instance-attribute

extensions: Mapping[str, Mapping[str, object]] | None

Server-advertised optional extensions, keyed by name; None if the server advertised none. See parse_tls for reading the tls extension.

ClientDisconnect

Bases: Exception

The client disconnected before its request body was fully received.

WebsocketBinary dataclass

WebsocketBinary(data: bytes)

data instance-attribute

data: bytes

WebsocketText dataclass

WebsocketText(text: str)

text instance-attribute

text: str

make_asgi_app

make_asgi_app(
    lifespan: Lifespan[T],
    http: HttpRouter[T] = refuse_http,
    websocket: WebsocketRouter[T] = refuse_websocket,
) -> ASGIApp

Build the ASGI app that drives lifespan and runs a per-connection Processor over each connection's event stream.

This is the ASGI entrypoint: it parses each raw scope into its typed value and owns all the receive/send wiring. The lifespan scope is set up once on startup and torn down on shutdown, with boot failures reported as lifespan.startup.failed / lifespan.shutdown.failed. For a connection scope it calls the matching handler with the state threaded in, wraps receive into the inbound event stream, runs the returned Processor, and drains its outbound stream into send: the handler only ever sees streams. The inbound stream is closed when the handler exits, so a handler that abandons the request body early does not leave it dangling for GC.

Each protocol's router defaults to one that refuses the connection, so an app serves a protocol only by passing its own router (an HTTP-only app passes http, a WebSocket-only app passes websocket). The default refusal never reaches app code: an HTTP scope gets a 501 Not Implemented response, a WebSocket scope is closed before accept (which the server turns into a 403). Drilling under this driver, e.g. to build a handler that needs the raw receive/send, is parse_scope plus the http_inbound / http_outbound (and websocket) shell functions this wires together.

refuse_http

refuse_http(state: object, head: HttpScope) -> HttpHandler

An HttpRouter that refuses every request with 501 Not Implemented.

refuse_websocket

refuse_websocket(
    state: object, head: WebsocketScope
) -> WebsocketHandler

A WebsocketRouter that refuses every connection by closing before accept (a 403).

file_response async

file_response(
    path: Path,
    *,
    status: int = 200,
    content_type: str | None = None,
    headers: RawHeaders = (),
    chunk_size: int = _DEFAULT_CHUNK_SIZE,
) -> AsyncIterator[Outbound]

Stream a file as the ResponseStart + ResponseBody event stream a handler yields, with Content-Type and Content-Length filled in: guess the content type, compute the length, and chunk the bytes off the event loop into the Outbound events the framework already streams to send, so a large file is never slurped into one bytes.

file_response is a coroutine, not an async generator: awaiting it does the stat up front, so a missing file raises FileNotFoundError (or stat's other OSErrors) before any ResponseStart is emitted. Nothing has been committed to the wire yet, so a handler can still turn the miss into a clean 404 (the parse-don't-validate move). Hand the returned stream back from a handler:

async def download(state, match) -> Reply:
    try:
        return await file_response(Path("/srv/report.pdf"))
    except FileNotFoundError:
        return Response(status=404, ...)

Content-Type is guessed from the file suffix with mimetypes.guess_file_type, falling back to application/octet-stream; pass content_type to override it. Any headers given are prepended, for things like content-disposition. The body is read in chunk_size pieces via asyncio.to_thread, so neither the open nor the reads block the event loop. The file is opened only once streaming begins and is closed when the stream is exhausted, errored, or closed early (make_asgi_app closes an abandoned outbound stream, e.g. on a client disconnect mid-download).

encode_inbound

encode_inbound(event: Inbound) -> RawMessage

Render one inbound http event as the raw dict an ASGI receive returns.

The server-direction dual of parse_inbound: a transport that owns the wire (without-http) builds typed Inbound events and hands them to the app as the dicts ASGI receive yields.

encode_lifespan_event

encode_lifespan_event(event: LifespanEvent) -> RawMessage

Render one lifespan event as the raw dict an ASGI receive returns.

encode_websocket_inbound

encode_websocket_inbound(
    event: WebsocketInbound,
) -> RawMessage

Render one inbound websocket event as the raw dict an ASGI receive returns.

parse_inbound

parse_inbound(message: RawMessage) -> Inbound

Classify one inbound http event. An unknown event is a protocol fault, so it raises.

parse_lifespan_event

parse_lifespan_event(message: RawMessage) -> LifespanEvent

Classify one lifespan event. An unknown event is a protocol fault, so it raises.

parse_websocket_inbound

parse_websocket_inbound(
    message: RawMessage,
) -> WebsocketInbound

Classify one inbound websocket event. An unknown event is a protocol fault, so it raises.

encode_lifespan_reply

encode_lifespan_reply(reply: LifespanReply) -> RawMessage

Render one lifespan reply as the raw dict an ASGI send expects.

encode_outbound

encode_outbound(event: Outbound) -> RawMessage

Render one outbound http event as the raw dict an ASGI send expects.

encode_response

encode_response(response: Response) -> tuple[Outbound, ...]

Split a whole Response into its ResponseStart then final ResponseBody.

encode_websocket_outbound

encode_websocket_outbound(
    event: WebsocketOutbound,
) -> RawMessage

Render one outbound websocket event as the raw dict an ASGI send expects.

parse_lifespan_reply

parse_lifespan_reply(message: RawMessage) -> LifespanReply

Classify one lifespan reply from the raw dict an app passed to send.

parse_outbound

parse_outbound(message: RawMessage) -> Outbound

Classify one outbound http event from the raw dict an app passed to send.

The server-direction dual of encode_outbound: a transport that owns the wire (without-http) reads the dicts the app sends and parses them into typed Outbound events at the boundary. An unknown event is a protocol fault, so it raises.

parse_websocket_outbound

parse_websocket_outbound(
    message: RawMessage,
) -> WebsocketOutbound

Classify one outbound websocket event from the raw dict an app passed to send.

encode_http_scope

encode_http_scope(scope: HttpScope) -> RawScope

Render a typed HttpScope as the raw http scope dict an ASGI app expects.

The server-direction dual of parse_http_scope: a transport that owns the wire (without-http) builds the typed scope from the request line and renders it back to the dict the ASGI interface hands an app.

encode_scope

encode_scope(scope: Scope) -> RawScope

Render any typed scope as its raw ASGI dict, the dual of parse_scope.

encode_websocket_scope

encode_websocket_scope(scope: WebsocketScope) -> RawScope

Render a typed WebsocketScope as the raw websocket scope dict an ASGI app expects.

extension

extension(
    extensions: Mapping[str, Mapping[str, object]] | None,
    name: str,
) -> Mapping[str, object] | None

The named extension's advertised options, or None when it is absent.

Parse, don't validate: this returns the options mapping itself (often empty, as for http.response.trailers) rather than a bool, so a caller needing the options has them and one needing only presence checks is not None. Server-advertised extensions are optional per-connection capabilities; an app negotiates by looking one up before using it and falling back when it is None (and parse_tls reads the tls extension through this).

parse_http_scope

parse_http_scope(scope: RawScope) -> HttpScope

Read an http scope into the typed connection facts, validating at the boundary.

parse_scope

parse_scope(scope: RawScope) -> Scope

Classify any scope by its type discriminator. An unknown type is a protocol fault, so it raises.

parse_tls

parse_tls(
    extensions: Mapping[str, Mapping[str, object]] | None,
) -> Tls | None

Read the tls extension's connection info from a scope's extensions.

Returns None when the connection is not over TLS (the extension is absent), which is how an application distinguishes TLS from plaintext connections.

parse_websocket_scope

parse_websocket_scope(scope: RawScope) -> WebsocketScope

Read a websocket scope into the typed handshake facts, validating at the boundary.

http_inbound async

http_inbound(receive: Receive) -> AsyncGenerator[Inbound]

An http request's inbound events as a stream.

The stream ends when the request is fully received (the last body chunk, or a disconnect), so a downstream processor's input runs dry exactly when the request does: the request's lifecycle is this stream's lifecycle.

A handler that abandons the body early leaves this generator suspended at its yield; make_asgi_app wraps it in aclosing, so closing it there runs any finally deterministically rather than deferring to garbage collection.

http_outbound

http_outbound(send: Send) -> Sink[Outbound]

A sink that writes each outbound event to ASGI send, encoding at the boundary.

lifespan_inbound async

lifespan_inbound(
    receive: Receive,
) -> AsyncGenerator[LifespanEvent]

The lifespan protocol's events as a stream, ending after shutdown.

lifespan_outbound

lifespan_outbound(send: Send) -> Sink[LifespanReply]

A sink that writes each lifespan reply to ASGI send, encoding at the boundary.

read_body async

read_body(events: Stream[Inbound]) -> bytes

Accumulate an http request's body across its RequestBody chunks.

Raises ClientDisconnect if the client goes away before the final chunk, so a truncated body fails loudly rather than passing for a complete one.

websocket_inbound async

websocket_inbound(
    receive: Receive,
) -> AsyncGenerator[WebsocketInbound]

A websocket connection's inbound events as a stream.

The stream ends on WebsocketDisconnect, so the connection's lifecycle is this stream's lifecycle, the same shape as http_inbound.

websocket_outbound

websocket_outbound(send: Send) -> Sink[WebsocketOutbound]

A sink that writes each outbound websocket event to ASGI send, encoding at the boundary.