DrinkMeAlice.FrameGuard (drink_me_alice v0.1.0)

Copy Markdown

Bounded WebSocket framing guard — production adaptation of the accepted BF-02A SurrealTransport.FrameGuard.

Mint.WebSocket 1.0.6 has no bound on buffer (raw bytes of an incomplete frame) or fragment (reassembled message payload). This pure state machine walks raw socket bytes before they reach Mint.WebSocket.decode/2:

  • parses each frame header itself (14-byte maximum, held in header_acc) and rejects a declared payload length above :max_message before Mint can start buffering the body;
  • accumulates the payload total of a fragmented message (data frame + continuations) and rejects when the aggregate exceeds :max_message — the 1 MiB contract message bound, enforced before unbounded decode;
  • enforces the RFC 6455 control-frame rules Mint leaves unchecked (payload <= 125 bytes, never fragmented);
  • counts payload bytes only — a message of exactly :max_message payload bytes is accepted; frame headers never consume the allowance.

The guard retains at most a partial 14-byte header between feed/2 calls; everything else is streamed through position arithmetic, not copies.

Summary

Functions

Validate a chunk of raw inbound bytes. Returns {:ok, guard} or {:error, reason, guard}; on error the caller must close the transport.

Payload bytes accumulated for the open fragmented message.

Types

t()

@type t() :: %DrinkMeAlice.FrameGuard{
  header_acc: term(),
  max_message: term(),
  msg_bytes: term(),
  msg_open: term(),
  skip: term()
}

Functions

feed(g, bytes)

@spec feed(t(), binary()) :: {:ok, t()} | {:error, term(), t()}

Validate a chunk of raw inbound bytes. Returns {:ok, guard} or {:error, reason, guard}; on error the caller must close the transport.

message_bytes(frame_guard)

Payload bytes accumulated for the open fragmented message.

new(opts \\ [])

@spec new(keyword()) :: t()