Longbridge v1 socket protocol implementation.
Handles binary packet framing/deframing, handshake, and message types
for the Longbridge OpenAPI protocol. Used by Longbridge.WSConnection
(the only transport — the SDK previously supported raw TCP but is
WebSocket-only now).
Protocol Overview
Each WebSocket binary message contains one or more length-prefixed protocol packets concatenated together:
┌──────────────────────────┐
│ length (4 bytes BE u32) │
├──────────────────────────┤
│ header (5/10/11 bytes) │
├──────────────────────────┤
│ body (Protobuf-encoded) │
└──────────────────────────┘The WebSocket layer's first protocol packet after upgrade is a
2-byte handshake: <<0b00010001, 0b00001001>>
(version=1, codec=protobuf=1, platform=OpenAPI=9, reserve=0).
Packet Types
- Request (type 1) — client initiates, expects a paired response.
- Response (type 2) — server replies to a request. Status code in the header indicates success/error.
- Push (type 3) — server-sent data without a prior request.
Control Commands
0— Close (server disconnection notice)1— Heartbeat (keep-alive ping/pong)2— Auth (authenticate with token)3— Reconnect (resume with session_id)
Status Codes (response header)
0SUCCESS1SERVER_TIMEOUT2CLIENT_TIMEOUT3BAD_REQUEST4BAD_RESPONSE5UNAUTHENTICATED6PERMISSION_DENIED7SERVER_INTERNAL_ERROR
Summary
Functions
Returns true for Auth command.
Returns true for Close command.
Returns true if the command code is a control command (0-3).
Returns the 2-byte protocol handshake sent as the first packet after the WebSocket upgrade.
Returns true for Heartbeat command.
Packs a complete packet (header + body) into iodata ready to send.
Returns true for Reconnect command.
Returns the human-readable name for a status code.
Unpacks one packet from the front of data and returns any
trailing bytes unchanged.
Functions
Returns true for Auth command.
Returns true for Close command.
@spec control?(non_neg_integer()) :: boolean()
Returns true if the command code is a control command (0-3).
@spec handshake() :: binary()
Returns the 2-byte protocol handshake sent as the first packet after the WebSocket upgrade.
The same bytes are used regardless of transport (legacy TCP
connections sent them on the raw socket; the WebSocket transport
sends them as a WS binary message). Bytes are
<<0b00010001, 0b00001001>> (version=1, codec=protobuf=1,
platform=OpenAPI=9, reserve=0).
Returns true for Heartbeat command.
@spec pack(Longbridge.Protocol.Header.t(), body :: iodata()) :: [binary()]
Packs a complete packet (header + body) into iodata ready to send.
The header's :body_length field is overwritten with the actual
body size, so callers can pass a header with body_length: 0 and
trust this function to fill it in.
Returns a two-element list [header_binary, body] suitable for
IO.iodata_to_binary/1 or writing directly to a socket.
Returns true for Reconnect command.
@spec status_name(non_neg_integer()) :: String.t()
Returns the human-readable name for a status code.
@spec unpack(binary()) :: {:ok, Longbridge.Protocol.Header.t(), binary(), binary()} | {:error, term()}
Unpacks one packet from the front of data and returns any
trailing bytes unchanged.
Used by Longbridge.WSConnection to split a single WebSocket binary
message that may contain multiple length-prefixed packets
concatenated together. The returned remaining is fed back in on
the next call.
If the response header has gzip: true, the body is
transparently decompressed — Longbridge.QuoteContext and friends
can then pass the result straight to Protox.decode!/2 without
caring about compression.
Returns:
{:ok, header, body, remaining}—bodyis the raw or gunzipped body bytes;remainingis whatever was left afterbody_lengthbytes.{:error, :incomplete_body}— not enough bytes yet; buffer and try again when more data arrives.{:error, reason}— header decode failed.