Guava.Socket.Reliable (Guava v0.34.0)

Copy Markdown View Source

Pure state machine for the GuavaSocket reliable-messaging layer.

This models everything the Python GuavaSocket does at the frame level — sequence numbering, per-message acks, the retransmission buffer, inbound dedup, and the open/open-ack handshake — as pure transitions, so it can be tested exhaustively without any transport. The transport (Guava.Socket) owns timers, reconnection/backoff, and the actual WebSocket.

Each transition returns {state, actions} where an action is one of:

  • {:send, frame} — send this frame struct to the peer
  • {:deliver, payload} — hand this decoded payload map to the owner
  • :ready — the socket completed its handshake and is open
  • {:closed, reason, description} — the peer closed the socket

Summary

Functions

Mark the socket closed by the client. The first close reason wins.

Whether the socket is permanently closed.

Handle an inbound frame (now_ms is used to stamp pong replies).

Handle the server's open-ack: retransmit buffered messages the peer hasn't seen, prune the retransmission buffer, and mark the socket open/ready.

Create a new reliable-protocol state.

Whether the socket is currently open.

Produce a ping frame (sent when the connection is idle).

Produce the open frame to send after a WebSocket connection is established.

Enqueue an outbound payload: assign the next sequence, buffer it for retransmission, and produce the message frame to send.

Types

action()

@type action() ::
  {:send, struct()}
  | {:deliver, map()}
  | :ready
  | {:closed, String.t(), String.t()}

status()

@type status() :: :never_opened | :connecting | :open | :closed

t()

@type t() :: %Guava.Socket.Reliable{
  close_description: term(),
  close_reason: term(),
  connection_id: term(),
  last_seen_sequence: term(),
  last_sent_sequence: term(),
  name: term(),
  opened_once: term(),
  rtx_buffer: term(),
  status: term()
}

Functions

close(s, reason, description)

@spec close(t(), String.t(), String.t()) :: t()

Mark the socket closed by the client. The first close reason wins.

closed?(reliable)

@spec closed?(t()) :: boolean()

Whether the socket is permanently closed.

handle_frame(s, ack, now)

@spec handle_frame(t(), struct(), integer()) :: {t(), [action()]}

Handle an inbound frame (now_ms is used to stamp pong replies).

Returns {state, actions}.

handle_open_ack(s, open_ack)

@spec handle_open_ack(t(), Guava.Socket.Protocol.OpenAck.t()) :: {t(), [action()]}

Handle the server's open-ack: retransmit buffered messages the peer hasn't seen, prune the retransmission buffer, and mark the socket open/ready.

new(name, connection_id)

@spec new(String.t(), String.t()) :: t()

Create a new reliable-protocol state.

open?(reliable)

@spec open?(t()) :: boolean()

Whether the socket is currently open.

ping(reliable, now)

@spec ping(t(), integer()) :: Guava.Socket.Protocol.Ping.t()

Produce a ping frame (sent when the connection is idle).

prepare_open(s)

@spec prepare_open(t()) :: {t(), Guava.Socket.Protocol.Open.t()}

Produce the open frame to send after a WebSocket connection is established.

is_reopen is true on any connection after the first successful open, matching the Python behavior of retaining open state across reconnects.

send_payload(s, payload)

@spec send_payload(t(), map()) :: {t(), Guava.Socket.Protocol.Message.t()}

Enqueue an outbound payload: assign the next sequence, buffer it for retransmission, and produce the message frame to send.