Skip to content

blackbull.websocket

blackbull.websocket

Object-form WebSocket handlers.

Accept before sending. Iteration ends on disconnect; receive() raises WebSocketDisconnect with code and reason. Raw (conn, receive, send) handlers remain supported; see docs/guide/websockets.md.

WebSocket

One WebSocket connection, as an object.

Constructed by the framework and handed to handlers that ask for it by annotation (ws: WebSocket) or by name (ws / websocket). A handler that takes the raw (conn, receive, send) triplet keeps receiving exactly that, and this object is never built for it.

The handshake is explicit: the connection is not live until accept returns, and sending before that is an error. To reject a connection, call close instead of accept.

accepted property

True once accept has completed the handshake.

client property

(host, port) of the peer; the port is None on a UDS.

client_disconnected property

True once the peer's close has been observed.

Only ever set by reading — the disconnect arrives on the receive channel, so a handler that has stopped receiving will not see this flip. Use it to break out of a send-only loop that also receives.

close_code property

The close code, once either side has closed; None while open.

close_reason property

The peer's close reason, or None until a close has been seen.

connection property

The underlying Connection.

Everything the handshake carried — headers, cookies, TLS state, the client address — is reachable through it. The shortcuts below cover the fields WebSocket handlers actually reach for.

headers property

The handshake request headers. Keys are lowercase bytes.

path property

The request path the handshake arrived on.

path_params property

Path converters captured by the route that matched.

query_string property

The raw query string, undecoded, without the leading ?.

subprotocols property

Subprotocols the client offered, in its order of preference.

__aiter__()

Iterate messages until the peer disconnects.

The disconnect ends the loop rather than raising, so the common shape is just async for message in ws:.

accept(subprotocol=None, *, headers=None) async

Complete the handshake.

subprotocol names the one being accepted from subprotocols; leaving it None keeps the server's automatic negotiation, exactly as sending {'type': 'websocket.accept', 'subprotocol': None} does on the raw path. headers are extra response headers for the 101.

Raises WebSocketDisconnect if the peer abandoned the handshake before it could be completed.

When middleware already accepted for you (blackbull.middleware.websocket), a bare accept() is a no-op so the same handler body works with or without it. Asking for a specific subprotocol or extra headers in that situation raises instead of silently dropping the request — the 101 has already gone out and cannot carry them.

close(code=_NORMAL_CLOSURE, reason=None) async

Close the connection, or reject the handshake.

Called before accept, this rejects the connection — the client's connect() fails rather than opening and immediately closing. Idempotent, and a no-op once the peer has already gone, so a finally: await ws.close() is always safe.

receive() async

Await one complete message — str for text, bytes for binary.

Fragmented messages have already been reassembled, so what comes back is always a whole application message.

Raises WebSocketDisconnect when the peer closes. Prefer async for unless you need the close code.

receive_bytes() async

Await one message, requiring it to be binary.

receive_json() async

Await one message and parse it as JSON (text or binary).

receive_text() async

Await one message, requiring it to be text.

send(data) async

Send data as text or binary, chosen by its Python type.

send_bytes(data) async

Send one complete binary message.

send_json(data, *, binary=False) async

JSON-serialise data and send it as one message.

Text by default, which is what browsers and most clients expect; pass binary=True to send the UTF-8 encoding as a binary frame instead.

send_text(data) async

Send one complete text message.

WebSocketDisconnect

Bases: Exception

The peer closed the connection.

Raised by WebSocket.receive and its typed variants. Iterating with async for handles this for you — the loop simply ends — so catch it only when you call receive() directly and need the close code.

code is the RFC 6455 close code the peer sent, or 1005 when it sent none. reason is its optional UTF-8 explanation.

connect_consumed(conn)

True once the websocket.connect event has been read for conn.

handshake_accepted(conn)

True once the WebSocket handshake has been accepted for conn.

handshake_closed(conn)

True once websocket.close has been sent for conn.

mark_connect_consumed(conn)

Record that websocket.connect has been taken off the channel.

For middleware that pops the connect event without answering it — an auth layer that wants the option to reject with a close code, say. A handler-side WebSocket then knows not to wait for a handshake offer that is already gone, but still sends the accept itself.

mark_handshake_accepted(conn)

Record that websocket.accept has been sent for conn.

Implies connect_consumed — the offer must have been read before it could be answered.

mark_handshake_closed(conn)

Record that websocket.close has been sent for conn.