Longbridge.Protocol (longbridge v0.1.0)

Copy Markdown View Source

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)

  • 0 SUCCESS
  • 1 SERVER_TIMEOUT
  • 2 CLIENT_TIMEOUT
  • 3 BAD_REQUEST
  • 4 BAD_RESPONSE
  • 5 UNAUTHENTICATED
  • 6 PERMISSION_DENIED
  • 7 SERVER_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

auth?(cmd)

Returns true for Auth command.

close?(cmd)

Returns true for Close command.

cmd_auth()

cmd_close()

cmd_heartbeat()

cmd_reconnect()

control?(cmd)

@spec control?(non_neg_integer()) :: boolean()

Returns true if the command code is a control command (0-3).

handshake()

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

heartbeat?(cmd)

Returns true for Heartbeat command.

pack(header, body)

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

reconnect?(cmd)

Returns true for Reconnect command.

status_bad_request()

status_name(arg1)

@spec status_name(non_neg_integer()) :: String.t()

Returns the human-readable name for a status code.

status_server_internal_error()

status_server_timeout()

status_success()

status_unauthenticated()

unpack(data)

@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}body is the raw or gunzipped body bytes; remaining is whatever was left after body_length bytes.
  • {:error, :incomplete_body} — not enough bytes yet; buffer and try again when more data arrives.
  • {:error, reason} — header decode failed.