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.
MessageTooLarge
¶
Bases: Exception
Raised when a message outgrows its bound.
Sibling of :class: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.
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.