Module: Muxr::Protocol

Defined in:
lib/muxr/protocol.rb

Overview

Length-prefixed framing for client <-> server messages over a Unix socket.

Wire format per message:

[1 byte type][4 bytes BE length][length bytes payload]

Types are single ASCII letters so they’re easy to recognise in tcpdump or hex dumps:

H  hello   (client -> server, payload: "ROWS COLS")
I  input   (client -> server, payload: raw STDIN bytes)
R  resize  (client -> server, payload: "ROWS COLS")
B  bye     (either way, payload: optional reason string)
O  output  (server -> client, payload: raw bytes to write to STDOUT)

Constant Summary collapse

HELLO =
"H".freeze
INPUT =
"I".freeze
RESIZE =
"R".freeze
BYE =
"B".freeze
OUTPUT =
"O".freeze
HEADER_SIZE =
5

Class Method Summary collapse

Class Method Details

.decode_size(payload) ⇒ Object

Returns [rows, cols] or nil if malformed.



53
54
55
56
57
58
59
60
# File 'lib/muxr/protocol.rb', line 53

def self.decode_size(payload)
  parts = payload.to_s.strip.split(/\s+/)
  return nil unless parts.length == 2
  r = Integer(parts[0]) rescue nil
  c = Integer(parts[1]) rescue nil
  return nil unless r && c
  [r, c]
end

.encode_size(rows, cols) ⇒ Object

Encodes a “ROWS COLS” string for HELLO / RESIZE payloads.



48
49
50
# File 'lib/muxr/protocol.rb', line 48

def self.encode_size(rows, cols)
  "#{rows.to_i} #{cols.to_i}"
end

.read(io) ⇒ Object

Reads exactly one framed message from io. Returns [type, payload] or nil on EOF / truncated frame. Blocks until the full message arrives.



25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/muxr/protocol.rb', line 25

def self.read(io)
  header = read_exact(io, HEADER_SIZE)
  return nil unless header
  type = header[0]
  length = header.byteslice(1, 4).unpack1("N")
  payload =
    if length.zero?
      ""
    else
      read_exact(io, length)
    end
  return nil unless payload
  [type, payload]
end

.write(io, type, payload = "") ⇒ Object

Writes one framed message. payload is treated as raw bytes (binary).

Raises:

  • (ArgumentError)


41
42
43
44
45
# File 'lib/muxr/protocol.rb', line 41

def self.write(io, type, payload = "")
  raise ArgumentError, "type must be a single byte" unless type.is_a?(String) && type.bytesize == 1
  bytes = payload.to_s.b
  io.write(type + [bytes.bytesize].pack("N") + bytes)
end