Module

asgi.bridge

ASGI bridge — translates between protocol events and the ASGI interface.

Builds ASGI scope dicts from protocol events, and creates the async receive/send callables that ASGI apps interact with.

Streaming-first: send() writes response chunks immediately to the transport. No buffering — each http.response.body message is compressed (if applicable) and flushed to the wire before the next one.

Phase 4 hot-path optimizations:

  • Pre-encoded ASGI spec constants (avoid per-request dict allocation)
  • Bodyless fast-path receive (skip asyncio.Queue for GET/HEAD)
  • Single write call for head+body when small
  • Reduced isinstance checks

Classes

SendState 2
Mutable holder for response metrics populated by the send callable. The worker reads these after t…

Mutable holder for response metrics populated by the send callable.

The worker reads these after the ASGI app completes to get the actual HTTP status code and byte count for access logging.

Attributes

Name Type Description
status int
bytes_sent int

Functions

_sanitize_headers 1 list[tuple[bytes, bytes]]
Strip CR/LF characters from response header names and values. Prevents CRLF in…
def _sanitize_headers(headers: list[tuple[bytes, bytes]]) -> list[tuple[bytes, bytes]]

Strip CR/LF characters from response header names and values.

Prevents CRLF injection attacks where a malicious ASGI app could inject extra headers or split the HTTP response. This is defense-in-depth — h11 also validates header content, but we guard before serialization.

Parameters
Name Type Description
headers list[tuple[bytes, bytes]]
Returns
list[tuple[bytes, bytes]]
build_scope 4 dict[str, Any]
Build an ASGI HTTP scope dict from a parsed request.
def build_scope(request: RequestReceived, config: ServerConfig, client: tuple[str, int], server: tuple[str, int]) -> dict[str, Any]
Parameters
Name Type Description
request RequestReceived

The parsed HTTP request head.

config ServerConfig

Server configuration.

client tuple[str, int]

Client (host, port) tuple.

server tuple[str, int]

Server (host, port) tuple.

Returns
dict[str, Any]
create_receive 1 Receive
Create an ASGI receive callable from a body event queue. The worker pushes Bod…
def create_receive(body_events: asyncio.Queue[BodyReceived]) -> Receive

Create an ASGI receive callable from a body event queue.

The worker pushes BodyReceived events into the queue as they arrive. The ASGI app calls receive() to consume them as http.request messages.

Parameters
Name Type Description
body_events asyncio.Queue[BodyReceived]

Queue of body events from the protocol layer.

Returns
Receive
create_empty_receive 0 Receive
Create a fast-path receive for bodyless requests (GET, HEAD, etc.). Returns a …
def create_empty_receive() -> Receive

Create a fast-path receive for bodyless requests (GET, HEAD, etc.).

Returns a static empty-body message without asyncio.Queue overhead. Called at most once per request — second call would hang, but ASGI apps should not call receive() twice for bodyless requests.

Returns
Receive
create_disconnect_receive 1 Receive
Create a receive callable that delivers ``http.disconnect``. For bodyless requ…
def create_disconnect_receive(disconnect: asyncio.Event) -> Receive

Create a receive callable that delivershttp.disconnect.

For bodyless requests (GET, HEAD, etc.): returns the empty body message on first call, then waits for the disconnect event before returning http.disconnect. This allows ASGI apps (e.g. Chirp's SSE handler) to detect client disconnects and stop producing events.

Parameters
Name Type Description
disconnect asyncio.Event

Event set by the connection monitor when the client closes the socket.

Returns
Receive
create_receive_with_disconnect 2 Receive
Create a receive callable that delivers body events then ``http.disconnect``. …
def create_receive_with_disconnect(body_events: asyncio.Queue[BodyReceived], disconnect: asyncio.Event) -> Receive

Create a receive callable that delivers body events thenhttp.disconnect.

Used when the request has a body (POST, PUT, etc.). Delivers body chunks from the queue until the body is complete, then waits for the disconnect event and returnshttp.disconnect.

Parameters
Name Type Description
body_events asyncio.Queue[BodyReceived]

Queue of body events from the protocol layer.

disconnect asyncio.Event

Event set by the connection monitor when the client closes the socket.

Returns
Receive
create_send 7 Send
Create an ASGI send callable that streams to the transport. Streaming-first: e…
def create_send(protocol: ProtocolHandler, writer: asyncio.StreamWriter, state: SendState, *, timing: ServerTiming | None = None, compressor: Compressor | None = None, request_method: bytes = b'GET', request_id: str | None = None) -> Send

Create an ASGI send callable that streams to the transport.

Streaming-first: each response.body chunk is written immediately. No buffering — the client sees data as soon as the app produces it.

Write coalescing: the response head is held back and combined with the first body chunk in a singlewriter.write()call when the total fits within_COALESCE_THRESHOLD. This halves the number of syscalls for small responses (the common case).

Parameters
Name Type Description
protocol ProtocolHandler

Protocol handler for serialization.

writer asyncio.StreamWriter

Asyncio stream writer for the connection.

state SendState

Mutable holder populated with response status and byte count.

timing ServerTiming | None

Optional Server-Timing header builder.

Default:None
compressor Compressor | None

Optional content compressor for the response.

Default:None
request_method bytes Default:b'GET'
request_id str | None Default:None
Returns
Send