Module: Protocol::MQTT::Property

Defined in:
lib/protocol/mqtt/property.rb

Overview

v5 property identifiers and property-block codec (§2.2.2).

A property block is: VBI-length-prefixed sequence of { identifier (VBI), value } pairs. Value type is fixed per identifier. All identifiers appear at most once per packet except 0x26 User Property, which may repeat.

Constant Summary collapse

BYTE =

Value types

:byte
U16 =
:u16
U32 =
:u32
VBI_TYPE =
:vbi
UTF8 =
:utf8
BINARY =
:binary
STRING_PAIR =
:string_pair
TABLE =

Identifier → [symbolic_name, type]

{
  0x01 => [:payload_format_indicator,          BYTE],
  0x02 => [:message_expiry_interval,           U32],
  0x03 => [:content_type,                      UTF8],
  0x08 => [:response_topic,                    UTF8],
  0x09 => [:correlation_data,                  BINARY],
  0x0B => [:subscription_identifier,           VBI_TYPE],
  0x11 => [:session_expiry_interval,           U32],
  0x12 => [:assigned_client_identifier,        UTF8],
  0x13 => [:server_keep_alive,                 U16],
  0x15 => [:authentication_method,             UTF8],
  0x16 => [:authentication_data,               BINARY],
  0x17 => [:request_problem_information,       BYTE],
  0x18 => [:will_delay_interval,               U32],
  0x19 => [:request_response_information,      BYTE],
  0x1A => [:response_information,              UTF8],
  0x1C => [:server_reference,                  UTF8],
  0x1F => [:reason_string,                     UTF8],
  0x21 => [:receive_maximum,                   U16],
  0x22 => [:topic_alias_maximum,               U16],
  0x23 => [:topic_alias,                       U16],
  0x24 => [:maximum_qos,                       BYTE],
  0x25 => [:retain_available,                  BYTE],
  0x26 => [:user_property,                     STRING_PAIR],
  0x27 => [:maximum_packet_size,               U32],
  0x28 => [:wildcard_subscription_available,   BYTE],
  0x29 => [:subscription_identifier_available, BYTE],
  0x2A => [:shared_subscription_available,     BYTE],
}.freeze
NAME_TO_ID =
TABLE.to_h { |id, (name, _t)| [name, id] }.freeze
REPEATABLE =

Every identifier allows zero-or-one occurrence EXCEPT user_property (repeatable) and subscription_identifier (repeatable in PUBLISH only, for multiple matching subscriptions).

{
  0x26 => true,  # user_property
  0x0B => true,  # subscription_identifier
}.freeze

Class Method Summary collapse

Class Method Details

.decode(reader) ⇒ Object

Decode a property block starting at the current position of a Codec::Reader. Consumes VBI length + that many bytes. Returns a Hash keyed by name symbol. Unknown ids raise MalformedPacket.

Raises:



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/protocol/mqtt/property.rb', line 90

def self.decode(reader)
  length = reader.read_vbi
  raise MalformedPacket, "property block length exceeds remaining" if length > reader.remaining
  end_pos = reader.pos + length
  props = {}
  while reader.pos < end_pos
    id = reader.read_vbi
    entry = TABLE[id] or raise MalformedPacket, "unknown property id 0x#{id.to_s(16)}"
    name, type = entry
    value = decode_value(reader, type)
    if name == :user_property
      (props[:user_property] ||= []) << value
    elsif REPEATABLE[id]
      (props[name] ||= []) << value
    else
      raise MalformedPacket, "duplicate property #{name}" if props.key?(name)
      props[name] = value
    end
  end
  raise MalformedPacket, "property block overrun" if reader.pos != end_pos
  props
end

.encode(properties) ⇒ Object

Encode a Hash of { name_symbol => value, user_property: [[k,v], …] } into a full property block (VBI length prefix + body). Returns a frozen BINARY String.



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/protocol/mqtt/property.rb', line 68

def self.encode(properties)
  body = Codec::Writer.new
  properties.each do |name, value|
    id = NAME_TO_ID[name] or raise ArgumentError, "unknown property: #{name}"
    _, type = TABLE[id]
    values = REPEATABLE[id] ? Array(value) : [value]
    values.each do |v|
      body.write_vbi(id)
      encode_value(body, type, v)
    end
  end

  out = Codec::Writer.new
  out.write_vbi(body.bytesize)
  out.write(body.bytes)
  out.bytes
end