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 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.

InvalidFrameLength

Bases: Exception

Raised by read_payload when the length field breaks RFC 6455 §5.2.

Two rules, both about the encoding rather than the size: the 16-bit and 64-bit forms carry only values that do not fit the shorter form, and the most significant bit of the 64-bit form is zero. The caller translates this into a protocol-error CLOSE (RFC 6455 §7.4.1 code 1002); like FramePayloadTooLarge, the codec stays protocol-agnostic.

MessageTooLarge

Bases: Exception

Raised when a message outgrows its bound.

Sibling of FramePayloadTooLarge, one layer up: that one is about a frame on the wire, this one about what the application would be handed once fragments are joined and permessage-deflate has inflated. A frame small enough to pass the frame cap can still produce a message that does not — which is the whole reason both exist. Same division of labour: the caller translates this into CLOSE 1009 (RFC 6455 §7.4.1); the codec stays protocol-agnostic.

produced is what the message had reached when the bound tripped, not what it would eventually have been — nothing here ever computes the size of a payload it refused to build.

WSFrameBits

Bases: IntEnum

Bit masks for the two-byte WebSocket frame header (RFC 6455 §5.2).

RSV1 marks a message compressed with permessage-deflate, and is set on the first frame of it only (RFC 7692 §7). RSV2 and RSV3 have no negotiated meaning, so a peer setting either is a protocol error.

WSFrameHeader

Bases: NamedTuple

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

The raw codec accepts either mask bit; WebSocketRecipient enforces endpoint roles (RFC 6455 §5.1).

WSOpcode

Bases: IntEnum

WebSocket frame opcodes (RFC 6455 §5.2).

CONTINUATION, TEXT and BINARY are data frames and may be fragmented across several frames. CLOSE, PING and PONG are control frames: §5.5 caps their payload at 125 bytes and forbids fragmenting them, and one may arrive between the fragments of a message.

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 an unmasked, final server-to-client frame header (RFC 6455 §5.2).

RSV1 belongs only on the first frame of a compressed message. Pass header and payload parts to the sender; its shared size gate chooses the write.

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 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.

A length field that breaks §5.2 raises InvalidFrameLength instead, judged the same way: before the size cap and before any body byte, so an unreadable encoding cannot be made legal by staying under a cap.