Module: TriCoreDB::Frame

Defined in:
lib/tricoredb/frame.rb

Overview

The native frame format: version:u8 | tag:u8 | payload_len:u32be | payload.

Constant Summary collapse

VERSION =

The frame format version this driver writes.

1
MAX_SUPPORTED_VERSION =

The highest frame format version this driver can read.

1
HEADER_SIZE =
6
MAX_DATA_PAYLOAD =

Ceiling for REQUEST and RESPONSE payloads.

16 * 1024 * 1024
MAX_CONTROL_PAYLOAD =

Ceiling for every other frame.

64 * 1024
HELLO =
0
AUTH =
1
REQUEST =
2
RESPONSE =
3
PING =
4
PONG =
5
ERROR =
6
CLOSE =
7
HELLO_OK =
8
AUTH_OK =
9
BYE =
10
CANCEL =
11
CANCEL_OK =
12
NAMES =
{
  HELLO => "HELLO", AUTH => "AUTH", REQUEST => "REQUEST", RESPONSE => "RESPONSE",
  PING => "PING", PONG => "PONG", ERROR => "ERROR", CLOSE => "CLOSE",
  HELLO_OK => "HELLO_OK", AUTH_OK => "AUTH_OK", BYE => "BYE",
  CANCEL => "CANCEL", CANCEL_OK => "CANCEL_OK"
}.freeze

Class Method Summary collapse

Class Method Details

.decode_body(body) ⇒ Object?

Returns the parsed JSON, or nil for an empty payload.

Parameters:

  • body (String)

Returns:

  • (Object, nil) —

    the parsed JSON, or nil for an empty payload



98
99
100
101
102
103
104
# File 'lib/tricoredb/frame.rb', line 98

def decode_body(body)
  return nil if body.empty?

  JSON.parse(body.dup.force_encoding(Encoding::UTF_8))
rescue JSON::ParserError => e
  raise ProtocolError, "frame payload is not valid JSON: #{e.message}"
end

.decode_header(header) ⇒ Array(Integer, Integer)

Decode and validate a six-byte header before any payload byte is read.

Parameters:

Returns:

  • (Array(Integer, Integer)) —

    [tag, payload_length]

Raises:



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/tricoredb/frame.rb', line 76

def decode_header(header)
  raise ProtocolError, "short frame header (#{header.bytesize} bytes)" if header.bytesize != HEADER_SIZE

  version, tag, length = header.unpack("CCN")
  if version > MAX_SUPPORTED_VERSION
    raise ProtocolError.new(
      "frame header version #{version} is newer than this driver can read (max #{MAX_SUPPORTED_VERSION})",
      code: "frame_version"
    )
  end
  limit = max_payload_for(tag)
  if length > limit
    raise ProtocolError.new(
      "#{name(tag)} frame declares a #{length}-byte payload, above the #{limit}-byte limit; refusing to buffer it",
      code: "frame_too_large"
    )
  end
  [tag, length]
end

.encode(tag, payload) ⇒ String

Encode one frame.

Parameters:

  • tag (Integer)
  • payload (Object, nil) —

    JSON-serialisable; nil sends an empty payload

Returns:

  • (String) —

    binary frame bytes

Raises:

  • (ProtocolError) —

    when the payload exceeds the ceiling for its tag



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/tricoredb/frame.rb', line 59

def encode(tag, payload)
  body = payload.nil? ? "".b : JSON.generate(payload).b
  limit = max_payload_for(tag)
  if body.bytesize > limit
    raise ProtocolError.new(
      "refusing to send a #{body.bytesize}-byte #{name(tag)} payload; the protocol caps it at #{limit} bytes",
      code: "frame_too_large"
    )
  end
  [VERSION, tag, body.bytesize].pack("CCN") + body
end

.max_payload_for(tag) ⇒ Integer

Returns the payload ceiling for that tag; an unknown tag gets the tighter one.

Parameters:

  • tag (Integer)

Returns:

  • (Integer) —

    the payload ceiling for that tag; an unknown tag gets the tighter one



43
44
45
# File 'lib/tricoredb/frame.rb', line 43

def max_payload_for(tag)
  tag == REQUEST || tag == RESPONSE ? MAX_DATA_PAYLOAD : MAX_CONTROL_PAYLOAD
end

.name(tag) ⇒ String

Parameters:

  • tag (Integer)

Returns:

  • (String)


49
50
51
# File 'lib/tricoredb/frame.rb', line 49

def name(tag)
  NAMES.fetch(tag) { "tag #{tag}" }
end