Skip to content

blackbull.server.ws_codec

blackbull.server.ws_codec

WebSocket frame codec (RFC 6455).

Shared by the server-side sender/recipient and the client. Protocol-specific callers (WebSocketSender, WebSocketRecipient, WebSocketSession) import the module-level functions directly.

FramePayloadTooLarge

Bases: Exception

Raised by :func:read_payload when max_length is set and the declared payload length exceeds it. The caller is responsible for translating this into a WebSocket-level CLOSE (RFC 6455 §7.4.1 code 1009 MESSAGE_TOO_BIG) — the codec stays protocol-agnostic.

WSFrameHeader

Bases: NamedTuple

Decoded fields from the two-byte WebSocket frame header (RFC 6455 §5.2).

Note on masking (RFC 6455 §5.1): - Client → server frames MUST be masked; WebSocketRecipient raises ValueError on an unmasked client frame. - Server → client frames MUST NOT be masked; WebSocketSender never sets the mask bit.

encode_frame(payload, opcode=WSOpcode.TEXT, *, mask=False, rsv1=False)

Encode payload as a WebSocket data frame (RFC 6455 §5).

opcode defaults to WSOpcode.TEXT; pass WSOpcode.BINARY for binary frames, WSOpcode.CLOSE for close frames, etc.

Masking (RFC 6455 §5.1): - Server → client frames MUST NOT be masked: keep mask=False (default). - Client → server frames MUST be masked: pass mask=True, which prepends a random 4-byte masking key and XORs the payload with it.

RSV1 (RFC 7692 §7): pass rsv1=True on the FIRST frame of a message whose payload has been compressed with permessage-deflate. Continuation frames in the same message keep rsv1=False.

encode_frame_header(length, opcode=WSOpcode.TEXT, *, rsv1=False)

Encode just the unmasked 2-to-10-byte WebSocket frame header (server → client, RFC 6455 §5.1/§5.2).

Server frames MUST NOT be masked, so this never sets the mask bit and takes no masking key. Callers that already hold the payload separately (e.g. WebSocketSender) write (header, payload) as a vectored writelines — avoiding the header+payload concatenation copy that encode_frame would otherwise allocate on every send.

rsv1 (RFC 7692 §7) marks the FIRST frame of a permessage-deflate compressed message.

read_frame(reader) async

Read one WebSocket frame from reader.

Returns (opcode, payload) where payload is already unmasked. Raises asyncio.IncompleteReadError on EOF.

read_frame_header(reader) async

Read the two-byte WebSocket frame header (RFC 6455 §5.2).

Returns a WSFrameHeader with all decoded flag and length fields. RSV1 signals per-message deflate (RFC 7692 §7); RSV2 and RSV3 are reserved for future extensions. Raises asyncio.IncompleteReadError on EOF.

read_payload(reader, masked, length, *, max_length=None) async

Read the payload of a WebSocket frame from reader.

If masked is True, also read the 4-byte mask and unmask the payload. Raises asyncio.IncompleteReadError on EOF.

When max_length is set and the declared payload size — resolved from the 16-bit or 64-bit extended-length field per RFC 6455 §5.2 — exceeds it, raises :class:FramePayloadTooLarge before reading any body bytes off the wire. Defends against post-handshake OOM where the peer advertises a 2**63 - 1 payload and the server tries to buffer it.