Module: Plushie::Transport::Framing

Defined in:
lib/plushie/transport/framing.rb

Overview

Frame encoding/decoding for raw byte stream transports.

Used for IoStream adapters (SSH, TCP, WebSocket) where the transport doesn't provide built-in message framing. Not needed for Erlang Ports (which handle framing via {:packet, 4}) or the Connection class (which handles framing internally).

Two modes:

  • MessagePack: 4-byte big-endian length prefix
  • JSON: newline-delimited (JSONL)

Both modes raise BufferOverflowError when a frame would exceed MAX_MESSAGE_SIZE.

Constant Summary collapse

MAX_MESSAGE_SIZE =

Per-message size cap in bytes (64 MiB). Matches the renderer's cap so both ends reject the same threshold.

64 * 1024 * 1024

Class Method Summary collapse

Class Method Details

.decode_lines(buffer) ⇒ Array(Array<String>, String)

Extract complete newline-delimited lines from a buffer. Returns an array of complete lines and the remaining (incomplete) buffer.

Parameters:

  • buffer (String)

    accumulated bytes

Returns:

  • (Array(Array<String>, String))

    [lines, remaining_buffer]

Raises:



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/plushie/transport/framing.rb', line 97

def decode_lines(buffer)
  lines = []
  while (idx = buffer.index("\n"))
    line = buffer[0, idx]
    raise BufferOverflowError.new(size: line.bytesize, limit: MAX_MESSAGE_SIZE) if line.bytesize > MAX_MESSAGE_SIZE

    lines << line
    buffer = buffer[(idx + 1)..]
  end
  raise BufferOverflowError.new(size: buffer.bytesize, limit: MAX_MESSAGE_SIZE) if buffer.bytesize > MAX_MESSAGE_SIZE

  [lines, buffer]
end

.decode_packets(buffer) ⇒ Array(Array<String>, String)

Extract complete length-prefixed frames from a buffer. Returns an array of complete messages and the remaining (incomplete) buffer.

Parameters:

  • buffer (String)

    accumulated bytes

Returns:

  • (Array(Array<String>, String))

    [messages, remaining_buffer]

Raises:



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/plushie/transport/framing.rb', line 62

def decode_packets(buffer)
  buffer = buffer.b if buffer.encoding != Encoding::BINARY
  messages = []

  while buffer.bytesize >= 4
    length = buffer[0, 4].unpack1("N")
    raise BufferOverflowError.new(size: length, limit: MAX_MESSAGE_SIZE) if length > MAX_MESSAGE_SIZE
    break if buffer.bytesize < 4 + length

    messages << buffer[4, length]
    buffer = buffer[(4 + length)..]
  end

  [messages, buffer]
end

.encode_line(data) ⇒ String

Encode a message as a newline-terminated line (JSONL).

Parameters:

  • data (String)

    message content (should not contain newlines)

Returns:

  • (String)

    newline-terminated line

Raises:



83
84
85
86
87
# File 'lib/plushie/transport/framing.rb', line 83

def encode_line(data)
  raise BufferOverflowError.new(size: data.bytesize, limit: MAX_MESSAGE_SIZE) if data.bytesize > MAX_MESSAGE_SIZE

  "#{data}\n"
end

.encode_packet(data) ⇒ String

Encode a message with a 4-byte big-endian length prefix.

Parameters:

  • data (String)

    raw message bytes

Returns:

  • (String)

    length-prefixed frame

Raises:



47
48
49
50
51
52
# File 'lib/plushie/transport/framing.rb', line 47

def encode_packet(data)
  data = data.b if data.encoding != Encoding::BINARY
  raise BufferOverflowError.new(size: data.bytesize, limit: MAX_MESSAGE_SIZE) if data.bytesize > MAX_MESSAGE_SIZE

  [data.bytesize].pack("N") + data
end