WebSockets & Server-Sent Events¶
The WebSocket connection object and the server-sent-event response types.
WebSocket
¶
WebSocket connection handler.
Usage::
from veloce import Veloce, WebSocket
app = Veloce()
@app.websocket("/ws")
async def chat(ws: WebSocket):
async with ws:
await ws.accept()
async for message in ws.iter_text():
await ws.send_text(message)
Using async with ws: closes the connection on a clean exit with a
normal-closure 1000. If the block exits via an exception, __aexit__
leaves the close to the dispatcher's error handling, which sends the
mapped close code (e.g. 1008 for a policy violation, 1011 for an
unhandled error) before the exception propagates.
Pass idle_timeout=<seconds> (default None -> disabled) to bound
how long a blocking receive (receive/receive_text/
receive_bytes/receive_json and the iter_* loops) waits for
the next message. When no message arrives within idle_timeout
seconds the connection performs a clean RFC 6455 close with
1001 Going Away and the receive raises WebSocketDisconnect, so
the handler loop unwinds exactly as it would on a peer-initiated close.
A per-call timeout still applies; whichever deadline is smaller
wins. Set it at construction via from_asgi(idle_timeout=...) or from
inside the handler with set_idle_timeout. The window bounds each
complete message (in production, ASGI delivers complete messages and
owns ping/pong; the raw-transport path measures it the same way).
Pass heartbeat=<seconds> (raw-transport mode only, default None
-> disabled) to proactively probe a silent peer. After accept() a
timer sends an application PING carrying a token every heartbeat
seconds; the peer must answer with a PONG (or send any other frame)
before the next tick, otherwise the connection is dropped with a
1006 close code recorded on ws.close_code. Any inbound byte
defers the next probe, so busy connections send no needless pings. In
ASGI mode the server owns ping/pong, so the value is accepted for API
symmetry but never starts a timer.
query_params
property
¶
Parsed query string of the WebSocket handshake URL.
Read it as ws.query_params["token"]. Backed by
QueryParams (multi-value, getlist-aware). Empty when the
scope carries no query_string.
url
property
¶
The WebSocket handshake URL path - ASGI-style shape.
Returns path plus ?query when a query string is present.
client
property
¶
The connecting peer as an Address(host, port).
Reads scope["client"] (the ASGI (host, port) pair).
Returns None when the scope carries no client info.
state
property
¶
Per-connection scratch namespace.
Lazily-created State (a dict subclass) supporting both
ws.state.user = ... attribute access and ws.state["user"].
cookies
property
¶
Cookies sent with the WebSocket handshake.
Parses the handshake Cookie header into {name: value}.
Empty when no cookie header was present.
application_state
property
¶
Server-side state of the WebSocket.
CONNECTINGbefore the app sendsaccept().CONNECTEDafteraccept()and untilclose()is sent.DISCONNECTEDafterclose()(locally) or after the peer half-closes (observed viaWebSocketDisconnect).
client_state
property
¶
Client-side state of the WebSocket.
Veloce does not distinguish the two halves at the protocol level
beyond the close flag, so this mirrors application_state once
the peer disconnects and otherwise stays CONNECTED once the
handshake completes.
origin
property
¶
The client-supplied Origin header, or None if absent.
WebSocket handshakes carry Origin per RFC 6455 Sec. 10.2 / Sec. 4.1.
Browsers always send it; non-browser clients may omit it. The
header is the application's primary defence against Cross-Site
WebSocket Hijacking - CSWSH bypasses CORS because the handshake
is plain HTTP/1.1 and Same-Origin Policy does not apply to it.
Pair this accessor with check_origin(allowed) before accept().
requested_subprotocols
property
¶
Subprotocols the client offered in Sec-WebSocket-Protocol.
Returns them in client preference order (RFC 6455 Sec. 1.9). Empty list when the header is absent. Whitespace around each token is stripped; the comparison the negotiator performs is case-sensitive per the spec.
from_asgi
classmethod
¶
from_asgi(scope: dict[str, Any], receive: Any, send: Any, idle_timeout: float | None = None, heartbeat: float | None = None) -> WebSocket
Construct an ASGI-driven WebSocket (no asyncio.Transport).
Used by Veloce.__call__ for scope["type"] == "websocket".
Headers come from scope["headers"] (list of (bytes, bytes)),
decoded latin-1 per ASGI. accept/send_*/receive_*/close
all dispatch through send/receive instead of the raw frame
writer used by the asyncio.Transport mode.
idle_timeout (default None -> disabled) bounds how long a
blocking receive waits for the next frame before performing a
clean 1001 Going Away close; see the class docstring. heartbeat
is accepted for signature symmetry with the raw-transport
constructor but is inert here - the ASGI server owns ping/pong.
from_transport
classmethod
¶
from_transport(transport: Transport, headers: dict[str, str], scope: dict[str, Any], *, path_params: dict[str, Any] | None = None, idle_timeout: float | None = None, recv_queue_maxsize: int | None = None) -> WebSocket
Construct a raw-transport WebSocket whose 101 was already sent.
Used by the native HttpProtocol upgrade path. The protocol writes the
RFC 6455 Sec. 4.2.2 101 response synchronously (to switch the byte
stream) before building this object, so _handshake_sent is set: a later
accept() validates state but does not emit a second handshake. The
connection is otherwise a normal raw-mode WebSocket - transport is
set, _asgi_send stays None (so _is_asgi is False), and inbound
bytes flow through feed_data/_parse_frame exactly as for a directly
constructed instance.
headers are the lowercased handshake headers (latin-1 decoded by the
protocol). scope mirrors the ASGI websocket scope shape so the same
path/query_params/client/cookies accessors work unchanged.
check_origin
¶
Return True when the handshake's Origin is in allowed.
Pass a single origin string or an iterable of allowed origins
(e.g. ["https://app.example.com", "https://admin.example.com"]).
Normalisation matches WebSocketOriginMiddleware: each side is
lowercased and has any trailing slash stripped, so allow-lists
written for one API are interchangeable with the other.
- Wildcard.
"*"inallowedaccepts any origin and is the opt-in "I have my own check elsewhere" escape hatch - the symmetric behaviour toWebSocketOriginMiddleware'sallowed_origins=["*"]. - Missing
Origin(no header at all, or a literalOrigin: nullfrom a sandboxed iframe /file://page) is a non-match and returnsFalse. Non-browser clients legitimately omit the header - if you want to allow them, branch onws.origin is Noneexplicitly. TheWebSocketOriginMiddlewaremiddleware path also offers anallow_missing=Trueswitch; this in-handler helper is deliberately strict-by-default.
Usage
@app.websocket("/ws") async def chat(ws: WebSocket): if not ws.check_origin("https://app.example.com"): await ws.close(code=WS_1008_POLICY_VIOLATION) # policy violation return await ws.accept() ...
For the middleware-style check (registered once, runs before
the handler) reach for veloce.SecurityHeadersMiddleware's
sibling WebSocketOriginMiddleware.
negotiate_subprotocol
¶
Pick the first client-offered subprotocol that the server supports.
Per RFC 6455 Sec. 4.1, the server picks ONE protocol from the client's list. Most servers prefer to honour the client's preference order (first match wins), which is what we do.
accept
async
¶
Complete the WebSocket handshake.
Records the chosen subprotocol on accepted_subprotocol.
Raises:
| Type | Description |
|---|---|
RuntimeError
|
if the connection is already accepted or already
closed, or if a |
send_json
async
¶
Send JSON data.
mode="text" (default) wraps the JSON in a text frame (opcode 0x1).
mode="binary" sends the raw JSON bytes as a binary frame (0x2).
receive
async
¶
Receive a raw ASGI WebSocket message.
Returns the message dict as the ASGI server delivered it
({"type": "websocket.receive", "text"/"bytes": ...}). A
websocket.disconnect message raises WebSocketDisconnect.
ASGI-mode only - raw asyncio-transport connections don't carry
ASGI message envelopes.
The same handshake state machine the typed receive_* helpers
enforce: the raw escape hatch must not be a way around
receive-before-accept or receive-after-close (which would
consume the websocket.connect envelope and corrupt the next
accept()).
send
async
¶
Send a raw ASGI WebSocket message.
message is forwarded straight to the ASGI send callable,
e.g. {"type": "websocket.send", "text": "..."}.
set_send_drain
¶
Install the native write-side backpressure hook (raw transport only).
drain is an awaitable-returning callable (HttpProtocol.drain) that
blocks while the transport's outgoing buffer is over its high-water
mark. The async send_* wrappers await it before writing each frame, so
a slow-reading client suspends the producing handler instead of letting
the transport buffer grow without bound. The native upgrade path
(HttpProtocol) calls this once; ASGI mode leaves it unset.
set_idle_timeout
¶
Set the idle-receive timeout in seconds (None disables it).
Applies to every subsequent blocking receive on this connection.
Call it inside the handler (typically right after accept()) to
enable or adjust the window; passing idle_timeout= to
WebSocket.from_asgi sets the same value at construction.
receive_text
async
¶
Receive a text message. Raises asyncio.TimeoutError if timeout exceeded.
When idle_timeout is configured, a wait longer than the idle
window closes the connection with 1001 Going Away and raises
WebSocketDisconnect instead of asyncio.TimeoutError.
receive_bytes
async
¶
Receive binary data. Raises asyncio.TimeoutError if timeout exceeded.
When idle_timeout is configured, a wait longer than the idle
window closes the connection with 1001 Going Away and raises
WebSocketDisconnect instead of asyncio.TimeoutError.
iter_text
async
¶
Async-iterate over incoming text frames until the peer closes.
Usage
async for msg in ws.iter_text(): ...
Terminates cleanly on WebSocketDisconnect. Other exceptions
propagate.
iter_bytes
async
¶
Async-iterate over incoming binary frames until the peer closes.
iter_json
async
¶
Async-iterate over incoming JSON-decoded frames until peer closes.
close
async
¶
Send a close frame and complete the RFC 6455 close handshake.
Per RFC 6455 Sec. 5.5.1 the close-frame payload is a 2-byte big-endian status code optionally followed by a UTF-8 reason of at most 123 bytes (so the whole payload fits in the 125-byte control-frame budget). Reasons longer than 123 bytes are truncated to a clean UTF-8 boundary.
On the raw-transport path the close is a full handshake (Sec. 5.5.1,
Sec. 7.1.1): the close frame is sent, then a server-initiated close
waits for the peer's reply close frame (bounded by
CLOSE_HANDSHAKE_TIMEOUT) before dropping the TCP connection. A
peer-initiated close already carries the peer's frame, so the reply is
sent and the transport closed without waiting.
feed_data
¶
Feed raw bytes from the transport (called by the protocol).
The transport delivers byte runs that need not align with frame boundaries: a single frame may be split across two reads, and one read may carry several frames. Bytes are appended to a persistent receive buffer and complete frames are parsed off the front in a loop - partial frames are kept for the next call.
Handles fragmented messages (RFC 6455 Sec. 5.4): a data frame with
FIN=0 opens a message that subsequent continuation frames
(opcode 0x0) extend, and the FIN=1 continuation completes it.
Control frames (close / ping / pong) are never fragmented and may
be interleaved within a fragmented message without disturbing the
reassembly buffer.
start_heartbeat
¶
Arm the heartbeat timer for a raw-transport connection.
Idempotent and a no-op in ASGI mode or when heartbeat was not
configured. accept() calls this automatically once the raw
handshake completes, so handlers rarely call it directly; it is
public so a handler that builds a WebSocket by hand can start the
probe after wiring its own transport.
EventSourceResponse
¶
Bases: Response
SSE streaming response - sends events over a long-lived connection.
Usage::
@app.get("/events")
async def events(request: Request):
async def generate():
for i in range(10):
yield ServerSentEvent(data=f"Event {i}")
await asyncio.sleep(1)
return EventSourceResponse(generate())
Pass ping=<seconds> to emit a keep-alive comment frame whenever no
event is produced within that interval - useful for holding idle
connections open through proxies that close silent sockets.
is_json
property
¶
True when Content-Type is JSON.
Matches application/json and any application/*+json
structured suffix (RFC 6839 Sec. 3.1).
mimetype
property
writable
¶
The bare media type - Content-Type without parameters.
text/html; charset=utf-8 -> text/html. Lower-cased and
stripped per RFC 9110 Sec. 8.3 (media types are case-insensitive).
status
property
writable
¶
Full HTTP status line, e.g. "200 OK".
Assignable: accepts an int (200), a bare numeric string
("200"), or a full status line ("200 OK" / "404 Not
Found"). The leading integer is parsed into status_code.
content_length
property
¶
Length of the response body in bytes.
Always derived from len(body). Streaming responses (which
don't materialise the body) return 0 here; see is_streamed.
charset
property
writable
¶
Response charset from Content-Type.
Falls back to "utf-8" when no charset parameter is present.
Assignable: setting it rewrites the charset= parameter on the
existing Content-Type (the bare media type is preserved).
mimetype_params
property
¶
Parameters of the Content-Type header.
Everything after the bare media type, as a dict of lower-cased
parameter names to their (unquoted) values. For
text/html; charset=utf-8 this is {"charset": "utf-8"}.
Returns an empty dict when no parameters are present.
last_modified
property
writable
¶
Parsed Last-Modified header -> UTC datetime or None.
Accepts the three RFC 9110 Sec. 5.6.7 HTTP-date
forms. Returns None on missing/unparseable.
expires
property
writable
¶
Parsed Expires header -> UTC datetime or None (RFC 9111 Sec. 5.3).
cookies
property
¶
Parsed cookie jar from this response's Set-Cookie header(s).
Walks every Set-Cookie entry (Q44 separator \r\nSet-Cookie:
respected) and returns {name: value}. Values are percent-decoded,
so a cookie reads back as the string set_cookie() was given rather
than its wire form. Multiple cookies with the same name resolve to
the last set - matches the wire behaviour where the client also keeps
the most-recent value. Caller introspection only; mutation goes
through set_cookie().
headerlist
property
¶
Headers flattened to the (name, value) tuple list the wire emit sends.
Each Set-Cookie (Q44 multi-cookie join) expands to its own tuple, so
the caller gets the per-cookie view ASGI requires. Two spellings of one
field name collapse to a single entry carrying the last name and value
seen, at the position of the first (RFC 9110 Sec. 5.1 makes field names
case-insensitive), and a CR/LF/NUL anywhere in a name or value raises
ValueError rather than being handed back - both matching the emit
paths. Response.headers remains the raw, unfolded view.
data
property
writable
¶
Body bytes alias for Response.body.
Read returns the current body; writing through the setter
replaces the body, invalidates any cached HTTP/1.1 encoded
bytes (_encoded), and updates Content-Length on the
headers if it was previously set.
vary
property
writable
¶
The Vary header as a HeaderSet.
Returns a fresh HeaderSet parsed from the current header.
Assign a HeaderSet, iterable of strings, or a comma-separated
string to replace it. Mutating the returned object does not
write back - call add_vary(...) or reassign for that.
allow
property
writable
¶
The Allow header as a HeaderSet.
Lists the HTTP methods the resource supports (RFC 9110 Sec. 10.2.1).
Assign a HeaderSet, iterable, or comma-separated string.
www_authenticate
property
writable
¶
The WWW-Authenticate challenge header - RFC 9110 Sec. 11.6.1.
Sent on 401 Unauthorized to tell the client which auth
scheme(s) to use. None when unset.
content_encoding
property
writable
¶
The Content-Encoding header - RFC 9110 Sec. 8.4. None when unset.
content_language
property
writable
¶
The Content-Language header - RFC 9110 Sec. 8.5. None when unset.
accept_ranges
property
writable
¶
The Accept-Ranges header - RFC 9110 Sec. 14.3.
Typically bytes (range requests supported) or none
(explicitly unsupported). None when the header is unset.
content_range
property
¶
The raw Content-Range header - RFC 9110 Sec. 14.4. None if unset.
date
property
writable
¶
The Date header as a tz-aware UTC datetime - RFC 9110 Sec. 6.6.1.
Returns None when unset or unparseable. Assign a datetime
or POSIX timestamp to set it; assign None to remove it.
location
property
writable
¶
The Location header - RFC 9110 Sec. 10.2.2. None when unset.
content_location
property
writable
¶
The Content-Location header - RFC 9110 Sec. 8.7. None when unset.
retry_after
property
writable
¶
The Retry-After header - RFC 9110 Sec. 10.2.3.
Returns an int (delay in seconds) when the header is numeric,
a tz-aware datetime when it's an HTTP-date, or None when
unset. Assign an int / timedelta / datetime to set it;
assign None to remove it.
age
property
writable
¶
The Age header in seconds - RFC 9110 Sec. 5.1. None when unset.
cache_control
property
¶
Parsed Cache-Control header (read-only view).
For setting directives, prefer set_cache_control(...) which
writes the header directly. This property is convenient for
introspection: resp.cache_control.max_age,
resp.cache_control.no_store, etc.
get_json
¶
Parse the response body as JSON.
Returns None for an empty body. Useful in tests to inspect a
JSON response without re-decoding body by hand. Raises if the
body is non-empty and not valid JSON.
set_cookie
¶
set_cookie(key: str, value: str, max_age: Any = None, expires: Any = None, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = 'Lax', partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None
Build a Set-Cookie header per RFC 6265.
The cookie name must be a valid RFC 6265 token (no spaces, separators,
or control characters) and must not collide with a cookie-attribute
keyword (Path, Max-Age, ...); a violation raises ValueError.
samesite defaults to "Lax" - a CSRF-resistant default that
matches modern browser behaviour. Pass samesite="None" (with
secure=True) for a cookie that must travel on cross-site
requests, or samesite=None/"" to omit the attribute.
expires= accepts a datetime, a Unix timestamp int|float,
or an already-formatted IMF-fixdate str. When both max_age
and expires are set, both are emitted (RFC 6265 Sec. 5.2.2: clients
prefer Max-Age when supported, falling back to Expires on
legacy IE).
partitioned=True adds the CHIPS Partitioned attribute
(Cookies Having Independent Partitioned State) - a partitioned
cookie is keyed to the top-level site, so embedded third-party
contexts each get an isolated jar. Partitioned requires
Secure, so it is only emitted when secure=True.
prefix="host" / prefix="secure" add the RFC 6265bis Sec. 4.1.3
name prefix (__Host- / __Secure-) and enforce its invariants:
"secure" requires secure=True; "host" also requires path="/"
and no domain. A violation raises ValueError.
The cookie name and value are rejected if they contain CR, LF, or
NUL - untrusted data must not be able to inject additional cookies
or response headers. dump_cookie performs that CRLF check on all
five fields (name, value, domain, path, samesite), so set_cookie
does not repeat it.
calculate_content_length
¶
Set Content-Length from len(body) and return the value.
Useful when a caller mutates body directly and wants the
header to follow. The ASGI emit path computes Content-Length
from body on the fly anyway; this helper is for callers that
want it locked into self.headers ahead of time.
set_data
¶
Replace the response body.
Accepts bytes or str (UTF-8 encoded). Invalidates the cached
HTTP/1.1 encode so the new body wire-out on the next emit.
Refreshes Content-Length when previously set on the headers.
set_cache_control
¶
set_cache_control(max_age: int | None = None, public: bool = False, private: bool = False, no_cache: bool = False, no_store: bool = False, must_revalidate: bool = False, immutable: bool = False, s_maxage: int | None = None) -> str
Build and set the Cache-Control header - RFC 9111 Sec. 5.2.
Combines the standard directives in the order RFC 9111 Sec. 5.2
documents. Values that are False / None are omitted, so a plain
resp.set_cache_control(max_age=3600, public=True) produces
Cache-Control: public, max-age=3600. Returns the value set.
add_vary
¶
Append header names to the Vary response header - RFC 9110 Sec. 12.5.5.
Merges with any existing Vary value (de-duplicates,
case-insensitive). Returns the resulting header value.
Useful when middleware wants to communicate "this response
depends on the named request headers" without clobbering
existing entries.
set_basic_auth_challenge
¶
Write a Basic WWW-Authenticate challenge - RFC 7617.
Convenience for the common 401 case:
WWW-Authenticate: Basic realm="<realm>", charset="UTF-8".
Returns the header value written.
set_content_range
¶
set_content_range(start: int | None, stop: int | None, length: int | None, unit: str = HEADER_VALUE_BYTES) -> str
Write a Content-Range header - RFC 9110 Sec. 14.4.
set_content_range(0, 499, 1234)->bytes 0-499/1234.start/stopbothNone-> an unsatisfied-range response:bytes */1234(length required in that form).lengthNone-> unknown total:bytes 0-499/*.
Returns the header value written.
set_etag
¶
Set the ETag header from an explicit value.
Quotes the value if the caller passed it bare. Prepends W/
when weak=True. Use add_etag() for body-derived MD5
ETags; set_etag is for callers that already have an
authoritative tag (DB revision, commit hash, version
counter).
get_etag
¶
Return (etag, is_weak) parsed from the ETag header.
(None, False) when unset. Returned tag keeps its quotes so
it compares directly with If-None-Match values.
freeze
¶
Pre-compute the cached HTTP/1.1 encode.
For buffered responses, populates _encoded so subsequent
access pays no encode cost. For streaming responses, no-op.
Used by response caching layers that want immutable bytes.
iter_encoded
¶
Yield the response body.
Return type is mode-dependent and the two modes are NOT interchangeable:
- Buffered response (
is_streamed is False) -> returns a synchronous iterator yieldingbytes. Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator (AsyncIterator[bytes]). Drain withasync for.
Callers must branch on response.is_streamed (or use
inspect.isasyncgen / hasattr(it, "__aiter__")) to pick
the right loop, e.g.:
it = response.iter_encoded()
if response.is_streamed:
async for chunk in it:
...
else:
for chunk in it:
...
The return shape is mode-dependent: a buffered response yields a
synchronous iterator of bytes, a streaming response yields the
underlying AsyncIterator[bytes]. Branch on response.is_streamed
to drain with the right loop.
iter_chunked
¶
Yield the response body in fixed-size chunks.
Return type is mode-dependent and the two modes are NOT interchangeable:
- Buffered response (
is_streamed is False) -> returns a synchronous generator yieldingbytesslices of lengthsize(the final slice may be shorter). Drain withfor. - Streaming response (
is_streamed is True) -> returns the underlying async iterator unchanged (AsyncIterator[bytes]);sizeis ignored because chunk boundaries are controlled by the source generator, not the caller. Drain withasync for.
Pick the loop based on response.is_streamed:
it = response.iter_chunked(4096)
if response.is_streamed:
async for chunk in it:
...
else:
for chunk in it:
...
size must be positive. The return shape is mode-dependent: branch
on response.is_streamed to drain with the right loop.
add_etag
¶
Compute and attach an ETag derived from the body.
Uses MD5 of the response body, opaque-quoted per RFC 9110 Sec. 8.8.3.
weak=True prepends W/ so the validator is treated as a
weak match (matching content but possibly different
byte-for-byte). Sets ETag even if one was already set; pass
the explicit ETag in __init__(headers=...) to skip this.
Returns the value set.
make_conditional
¶
make_conditional(request: Any) -> Response
Downgrade this response to 304 when the request's preconditions match the response's ETag / Last-Modified.
Checks If-None-Match first (per RFC 9110 Sec. 13.2 precedence),
then If-Modified-Since. On a match, mutates self to status
304 with no body. Returns self so callers can use it inline:
return resp.make_conditional(request).
Handles If-None-Match: * (matches any current representation
of the resource) and the weak/strong ETag comparison rules.
check_preconditions
¶
check_preconditions(request: Any) -> Response
Enforce the write-side If-Match precondition (RFC 9110 Sec. 13.1.1).
Raises PreconditionFailed (412) when the request carries an
If-Match header that the response's current ETag does not satisfy
under the strong comparison (Sec. 8.8.3.1) - the lost-update guard.
If-Match: * is satisfied whenever a current representation exists,
approximated here by the presence of an ETag header. With no
If-Match header the response is returned unchanged. Returns self
so it can be chained: return resp.check_preconditions(request).
Invoke this inside a handler (where HTTPException is converted to a
response); it raises rather than mutating the status.
set_content_disposition
¶
set_content_disposition(disposition: str = HEADER_VALUE_ATTACHMENT, filename: str | None = None) -> str
Write a Content-Disposition header - RFC 6266.
disposition is "attachment" (force download) or "inline"
(render in-browser). When filename is given, an ASCII quotable
name uses filename="..." (spaces and punctuation preserved, only
\ and " escaped); a non-ASCII or non-quotable name uses only
the RFC 5987 filename*=UTF-8''... form, with no lossy legacy slot.
Returns the header value written.
delete_cookie
¶
delete_cookie(key: str, path: str = '/', domain: str | None = None, secure: bool = False, httponly: bool = False, samesite: str | None = None, partitioned: bool = False, prefix: Literal['host', 'secure'] | None = None) -> None
Delete a cookie by overwriting it with an empty value + Max-Age=0.
The browser only treats the new cookie as a replacement for the
existing one if Path, Domain, and the Secure / SameSite
/ Partitioned attributes match - otherwise it stores both. So a
session cookie originally set with Secure; SameSite=None (or with
Partitioned) will not be deleted by a plain delete_cookie(key)
call. Pass the same flags here. prefix deletes the cookie under
its true __Host-/__Secure- wire name and enforces the same
invariants on the deletion's attributes.
stream_to
async
¶
Stream SSE events to transport.
When drain is supplied (the raw serving protocol passes its write-side
flow-control awaitable) it is awaited after each event, so a fast event
producer feeding a slow client is throttled at the transport buffer
instead of growing it without bound. It is a no-op until the buffer
crosses the high-water mark.
ServerSentEvent
¶
A single SSE event.
json
classmethod
¶
json(payload: Any, *, event: str | None = None, id: str | None = None, retry: int | None = None) -> ServerSentEvent
Build an event whose data field is payload serialized to JSON.
Serialization runs once here, off the per-event stream loop, and the
result is stored in the plain data field - so encode() stays the
same branch-free path it is for a raw data= string. Use the regular
constructor when the payload is already a formatted string.