without_asgi¶
without adapters that turn an ASGI app's receive/send into typed event streams and back.
without_asgi
¶
LifespanReply
¶
LifespanReply = (
StartupComplete
| ShutdownComplete
| StartupFailed
| ShutdownFailed
)
Outbound
¶
Outbound = (
ResponseStart
| ResponseBody
| ServerPush
| ZeroCopySend
| PathSend
| EarlyHint
| ResponseTrailers
| ResponseDebug
)
WebsocketOutbound
¶
WebsocketOutbound = (
WebsocketAccept
| WebsocketSend
| WebsocketClose
| WebsocketResponseStart
| WebsocketResponseBody
)
RequestBody
dataclass
¶
WebsocketConnect
dataclass
¶
The client is opening a websocket and awaiting an accept or a close.
WebsocketDisconnect
dataclass
¶
EarlyHint
dataclass
¶
Response
dataclass
¶
Response(
status: int, headers: RawHeaders = (), body: bytes = b""
)
ResponseBody
dataclass
¶
ResponseDebug
dataclass
¶
ResponseStart
dataclass
¶
ResponseStart(
status: int,
headers: RawHeaders = (),
trailers: bool = False,
)
ResponseTrailers
dataclass
¶
ResponseTrailers(
headers: RawHeaders, more_trailers: bool = False
)
Trailing headers sent after the final body (http.response.trailers extension).
ServerPush
dataclass
¶
ServerPush(path: str, headers: RawHeaders)
An HTTP/2 server push (http.response.push extension).
SupportsFileno
¶
WebsocketAccept
dataclass
¶
WebsocketAccept(
subprotocol: str | None = None, headers: RawHeaders = ()
)
WebsocketClose
dataclass
¶
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.
WebsocketResponseBody
dataclass
¶
WebsocketResponseStart
dataclass
¶
WebsocketResponseStart(
status: int, headers: RawHeaders = ()
)
The start of an HTTP denial response (websocket.http.response extension).
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.
Asgi
dataclass
¶
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.
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.
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
¶
Remote [host, port]; None if not provided.
server
instance-attribute
¶
Server [host, port], or [path, None] for a unix socket; None if not
provided.
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.
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
¶
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.
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.
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.
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
¶
Remote [host, port]; None if not provided.
server
instance-attribute
¶
Server [host, port], or [path, None] for a unix socket; None if not
provided.
subprotocols
instance-attribute
¶
Subprotocols the client advertised; defaults to empty.
ClientDisconnect
¶
Bases: Exception
The client disconnected before its request body was fully received.
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
¶
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
¶
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
¶
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
¶
Read an http scope into the typed connection facts, validating at the boundary.
parse_scope
¶
Classify any scope by its type discriminator. An unknown type is a protocol fault, so it raises.
parse_tls
¶
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
¶
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
¶
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.