lib/mqtt/packet.rb in mqtt-0.5.0 vs lib/mqtt/packet.rb in mqtt-0.6.0

- old
+ new

@@ -1,12 +1,11 @@ # encoding: BINARY module MQTT - # Class representing a MQTT Packet # Performs binary encoding and decoding of headers - class MQTT::Packet + class Packet # The version number of the MQTT protocol to use (default 3.1.0) attr_accessor :version # Identifier to link related control packets together attr_accessor :id @@ -34,110 +33,113 @@ # Read in the packet length multiplier = 1 body_length = 0 pos = 1 - begin + + loop do digit = read_byte(socket) body_length += ((digit & 0x7F) * multiplier) multiplier *= 0x80 pos += 1 - end while ((digit & 0x80) != 0x00) and pos <= 4 + break if (digit & 0x80).zero? || pos > 4 + end # Store the expected body length in the packet packet.instance_variable_set('@body_length', body_length) # Read in the packet body - packet.parse_body( socket.read(body_length) ) + packet.parse_body(socket.read(body_length)) - return packet + packet end # Parse buffer into new packet object def self.parse(buffer) packet = parse_header(buffer) packet.parse_body(buffer) - return packet + packet end # Parse the header and create a new packet object of the correct type # The header is removed from the buffer passed into this function def self.parse_header(buffer) # Check that the packet is a long as the minimum packet size if buffer.bytesize < 2 - raise ProtocolException.new("Invalid packet: less than 2 bytes long") + raise ProtocolException, 'Invalid packet: less than 2 bytes long' end # Create a new packet object - bytes = buffer.unpack("C5") + bytes = buffer.unpack('C5') packet = create_from_header(bytes.first) packet.validate_flags # Parse the packet length body_length = 0 multiplier = 1 pos = 1 - begin + + loop do if buffer.bytesize <= pos - raise ProtocolException.new("The packet length header is incomplete") + raise ProtocolException, 'The packet length header is incomplete' end + digit = bytes[pos] body_length += ((digit & 0x7F) * multiplier) multiplier *= 0x80 pos += 1 - end while ((digit & 0x80) != 0x00) and pos <= 4 + break if (digit & 0x80).zero? || pos > 4 + end # Store the expected body length in the packet packet.instance_variable_set('@body_length', body_length) # Delete the fixed header from the raw packet passed in buffer.slice!(0...pos) - return packet + packet end # Create a new packet object from the first byte of a MQTT packet def self.create_from_header(byte) # Work out the class type_id = ((byte & 0xF0) >> 4) packet_class = MQTT::PACKET_TYPES[type_id] if packet_class.nil? - raise ProtocolException.new("Invalid packet type identifier: #{type_id}") + raise ProtocolException, "Invalid packet type identifier: #{type_id}" end # Convert the last 4 bits of byte into array of true/false - flags = (0..3).map { |i| byte & (2 ** i) != 0 } + flags = (0..3).map { |i| byte & (2**i) != 0 } # Create a new packet object packet_class.new(:flags => flags) end # Create a new empty packet - def initialize(args={}) + def initialize(args = {}) # We must set flags before the other values @flags = [false, false, false, false] update_attributes(ATTR_DEFAULTS.merge(args)) end # Set packet attributes from a hash of attribute names and values - def update_attributes(attr={}) - attr.each_pair do |k,v| - if v.is_a?(Array) or v.is_a?(Hash) + def update_attributes(attr = {}) + attr.each_pair do |k, v| + if v.is_a?(Array) || v.is_a?(Hash) send("#{k}=", v.dup) else send("#{k}=", v) end end end # Get the identifer for this packet type def type_id index = MQTT::PACKET_TYPES.index(self.class) - if index.nil? - raise "Invalid packet type: #{self.class}" - end - return index + raise "Invalid packet type: #{self.class}" if index.nil? + index end # Get the name of the packet type as a string in capitals # (like the MQTT specification uses) # @@ -156,83 +158,90 @@ @body_length = arg.to_i end # Parse the body (variable header and payload) of a packet def parse_body(buffer) - if buffer.bytesize != body_length - raise ProtocolException.new( - "Failed to parse packet - input buffer (#{buffer.bytesize}) is not the same as the body length header (#{body_length})" - ) - end + return if buffer.bytesize == body_length + + raise ProtocolException, "Failed to parse packet - input buffer (#{buffer.bytesize}) is not the same as the body length header (#{body_length})" end # Get serialisation of packet's body (variable header and payload) def encode_body '' # No body by default end - # Serialise the packet def to_s # Encode the fixed header header = [ ((type_id.to_i & 0x0F) << 4) | - (flags[3] ? 0x8 : 0x0) | - (flags[2] ? 0x4 : 0x0) | - (flags[1] ? 0x2 : 0x0) | - (flags[0] ? 0x1 : 0x0) + (flags[3] ? 0x8 : 0x0) | + (flags[2] ? 0x4 : 0x0) | + (flags[1] ? 0x2 : 0x0) | + (flags[0] ? 0x1 : 0x0) ] # Get the packet's variable header and payload - body = self.encode_body + body = encode_body # Check that that packet isn't too big body_length = body.bytesize - if body_length > 268435455 - raise "Error serialising packet: body is more than 256MB" + if body_length > 268_435_455 + raise 'Error serialising packet: body is more than 256MB' end # Build up the body length field bytes - begin + loop do digit = (body_length % 128) body_length = body_length.div(128) # if there are more digits to encode, set the top bit of this digit - digit |= 0x80 if (body_length > 0) + digit |= 0x80 if body_length > 0 header.push(digit) - end while (body_length > 0) + break if body_length <= 0 + end # Convert header to binary and add on body header.pack('C*') + body end # Check that fixed header flags are valid for types that don't use the flags # @private def validate_flags - if flags != [false, false, false, false] - raise ProtocolException.new("Invalid flags in #{type_name} packet header") - end + return if flags == [false, false, false, false] + + raise ProtocolException, "Invalid flags in #{type_name} packet header" end # Returns a human readable string def inspect "\#<#{self.class}>" end + # Read and unpack a single byte from a socket + def self.read_byte(socket) + byte = socket.getbyte + raise ProtocolException, 'Failed to read byte from socket' if byte.nil? + + byte + end + protected # Encode an array of bytes and return them def encode_bytes(*bytes) bytes.pack('C*') end # Encode an array of bits and return them def encode_bits(bits) - [bits.map{|b| b ? '1' : '0'}.join].pack('b*') + [bits.map { |b| b ? '1' : '0' }.join].pack('b*') end # Encode a 16-bit unsigned integer and return it def encode_short(val) + raise 'Value too big for short' if val > 0xffff [val.to_i].pack('n') end # Encode a UTF-8 string and return it # (preceded by the length of the string) @@ -255,46 +264,30 @@ buffer.slice!(0...1).unpack('C').first end # Remove 8 bits from the front of buffer def shift_bits(buffer) - buffer.slice!(0...1).unpack('b8').first.split('').map {|b| b == '1'} + buffer.slice!(0...1).unpack('b8').first.split('').map { |b| b == '1' } end # Remove n bytes from the front of buffer - def shift_data(buffer,bytes) + def shift_data(buffer, bytes) buffer.slice!(0...bytes) end # Remove string from the front of buffer def shift_string(buffer) len = shift_short(buffer) - str = shift_data(buffer,len) + str = shift_data(buffer, len) # Strings in MQTT v3.1 are all UTF-8 str.force_encoding('UTF-8') end - - private - - # Read and unpack a single byte from a socket - def self.read_byte(socket) - byte = socket.read(1) - if byte.nil? - raise ProtocolException.new("Failed to read byte from socket") - end - byte.unpack('C').first - end - - - ## PACKET SUBCLASSES ## - # Class representing an MQTT Publish message class Publish < MQTT::Packet - # Duplicate delivery flag attr_accessor :duplicate # Retain flag attr_accessor :retain @@ -313,102 +306,88 @@ :topic => nil, :payload => '' } # Create a new Publish packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) end def duplicate @flags[3] end # Set the DUP flag (true/false) def duplicate=(arg) - if arg.kind_of?(Integer) - @flags[3] = (arg == 0x1) - else - @flags[3] = arg - end + @flags[3] = arg.is_a?(Integer) ? (arg == 0x1) : arg end def retain @flags[0] end # Set the retain flag (true/false) def retain=(arg) - if arg.kind_of?(Integer) - @flags[0] = (arg == 0x1) - else - @flags[0] = arg - end + @flags[0] = arg.is_a?(Integer) ? (arg == 0x1) : arg end def qos (@flags[1] ? 0x01 : 0x00) | (@flags[2] ? 0x02 : 0x00) end # Set the Quality of Service level (0/1/2) def qos=(arg) @qos = arg.to_i - if @qos < 0 or @qos > 2 - raise "Invalid QoS value: #{@qos}" - else - @flags[1] = (arg & 0x01 == 0x01) - @flags[2] = (arg & 0x02 == 0x02) - end + raise "Invalid QoS value: #{@qos}" if @qos < 0 || @qos > 2 + + @flags[1] = (arg & 0x01 == 0x01) + @flags[2] = (arg & 0x02 == 0x02) end # Get serialisation of packet's body def encode_body body = '' - if @topic.nil? or @topic.to_s.empty? - raise "Invalid topic name when serialising packet" + if @topic.nil? || @topic.to_s.empty? + raise 'Invalid topic name when serialising packet' end body += encode_string(@topic) - body += encode_short(@id) unless qos == 0 + body += encode_short(@id) unless qos.zero? body += payload.to_s.dup.force_encoding('ASCII-8BIT') - return body + body end # Parse the body (variable header and payload) of a Publish packet def parse_body(buffer) super(buffer) @topic = shift_string(buffer) - @id = shift_short(buffer) unless qos == 0 + @id = shift_short(buffer) unless qos.zero? @payload = buffer end # Check that fixed header flags are valid for this packet type # @private def validate_flags - if qos == 3 - raise ProtocolException.new("Invalid packet: QoS value of 3 is not allowed") - end - if qos == 0 and duplicate - raise ProtocolException.new("Invalid packet: DUP cannot be set for QoS 0") - end + raise ProtocolException, 'Invalid packet: QoS value of 3 is not allowed' if qos == 3 + raise ProtocolException, 'Invalid packet: DUP cannot be set for QoS 0' if qos.zero? && duplicate end # Returns a human readable string, summarising the properties of the packet def inspect - "\#<#{self.class}: " + - "d#{duplicate ? '1' : '0'}, " + - "q#{qos}, " + - "r#{retain ? '1' : '0'}, " + - "m#{id}, " + - "'#{topic}', " + - "#{inspect_payload}>" + "\#<#{self.class}: " \ + "d#{duplicate ? '1' : '0'}, " \ + "q#{qos}, " \ + "r#{retain ? '1' : '0'}, " \ + "m#{id}, " \ + "'#{topic}', " \ + "#{inspect_payload}>" end protected def inspect_payload str = payload.to_s - if str.bytesize < 16 and str =~ /^[ -~]*$/ + if str.bytesize < 16 && str =~ /^[ -~]*$/ "'#{str}'" else "... (#{str.bytesize} bytes)" end end @@ -457,43 +436,42 @@ :will_topic => nil, :will_qos => 0, :will_retain => false, :will_payload => '', :username => nil, - :password => nil, + :password => nil } # Create a new Client Connect packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) - if version == '3.1.0' or version == '3.1' + if version == '3.1.0' || version == '3.1' self.protocol_name ||= 'MQIsdp' self.protocol_level ||= 0x03 elsif version == '3.1.1' self.protocol_name ||= 'MQTT' self.protocol_level ||= 0x04 else - raise ArgumentError.new("Unsupported protocol version: #{version}") + raise ArgumentError, "Unsupported protocol version: #{version}" end end # Get serialisation of packet's body def encode_body body = '' + if @version == '3.1.0' - if @client_id.nil? or @client_id.bytesize < 1 - raise "Client identifier too short while serialising packet" - elsif @client_id.bytesize > 23 - raise "Client identifier too long when serialising packet" - end + raise 'Client identifier too short while serialising packet' if @client_id.nil? || @client_id.bytesize < 1 + raise 'Client identifier too long when serialising packet' if @client_id.bytesize > 23 end + body += encode_string(@protocol_name) body += encode_bytes(@protocol_level.to_i) if @keep_alive < 0 - raise "Invalid keep-alive value: cannot be less than 0" + raise 'Invalid keep-alive value: cannot be less than 0' end # Set the Connect flags @connect_flags = 0 @connect_flags |= 0x02 if @clean_session @@ -511,26 +489,24 @@ # The MQTT v3.1 specification says that the payload is a UTF-8 string body += encode_string(@will_payload) end body += encode_string(@username) unless @username.nil? body += encode_string(@password) unless @password.nil? - return body + body end # Parse the body (variable header and payload) of a Connect packet def parse_body(buffer) super(buffer) @protocol_name = shift_string(buffer) @protocol_level = shift_byte(buffer).to_i - if @protocol_name == 'MQIsdp' and @protocol_level == 3 + if @protocol_name == 'MQIsdp' && @protocol_level == 3 @version = '3.1.0' - elsif @protocol_name == 'MQTT' and @protocol_level == 4 + elsif @protocol_name == 'MQTT' && @protocol_level == 4 @version = '3.1.1' else - raise ProtocolException.new( - "Unsupported protocol: #{@protocol_name}/#{@protocol_level}" - ) + raise ProtocolException, "Unsupported protocol: #{@protocol_name}/#{@protocol_level}" end @connect_flags = shift_byte(buffer) @clean_session = ((@connect_flags & 0x02) >> 1) == 0x01 @keep_alive = shift_short(buffer) @@ -541,31 +517,30 @@ @will_retain = ((@connect_flags & 0x20) >> 5) == 0x01 @will_topic = shift_string(buffer) # The MQTT v3.1 specification says that the payload is a UTF-8 string @will_payload = shift_string(buffer) end - if ((@connect_flags & 0x80) >> 7) == 0x01 and buffer.bytesize > 0 + if ((@connect_flags & 0x80) >> 7) == 0x01 && buffer.bytesize > 0 @username = shift_string(buffer) end - if ((@connect_flags & 0x40) >> 6) == 0x01 and buffer.bytesize > 0 + if ((@connect_flags & 0x40) >> 6) == 0x01 && buffer.bytesize > 0 # rubocop: disable Style/GuardClause @password = shift_string(buffer) end end # Returns a human readable string, summarising the properties of the packet def inspect - str = "\#<#{self.class}: " - str += "keep_alive=#{keep_alive}" - str += ", clean" if clean_session + str = "\#<#{self.class}: " \ + "keep_alive=#{keep_alive}" + str += ', clean' if clean_session str += ", client_id='#{client_id}'" str += ", username='#{username}'" unless username.nil? - str += ", password=..." unless password.nil? - str += ">" + str += ', password=...' unless password.nil? + str + '>' end # ---- Deprecated attributes and methods ---- # - public # @deprecated Please use {#protocol_level} instead def protocol_version protocol_level end @@ -583,14 +558,14 @@ # The return code (defaults to 0 for connection accepted) attr_accessor :return_code # Default attribute values - ATTR_DEFAULTS = {:return_code => 0x00} + ATTR_DEFAULTS = { :return_code => 0x00 } # Create a new Client Connect packet - def initialize(args={}) + def initialize(args = {}) # We must set flags before other attributes @connack_flags = [false, false, false, false, false, false, false, false] super(ATTR_DEFAULTS.merge(args)) end @@ -599,56 +574,52 @@ @connack_flags[0] end # Set the Session Present flag def session_present=(arg) - if arg.kind_of?(Integer) - @connack_flags[0] = (arg == 0x1) - else - @connack_flags[0] = arg - end + @connack_flags[0] = arg.is_a?(Integer) ? (arg == 0x1) : arg end # Get a string message corresponding to a return code def return_msg case return_code - when 0x00 - "Connection Accepted" - when 0x01 - "Connection refused: unacceptable protocol version" - when 0x02 - "Connection refused: client identifier rejected" - when 0x03 - "Connection refused: server unavailable" - when 0x04 - "Connection refused: bad user name or password" - when 0x05 - "Connection refused: not authorised" - else - "Connection refused: error code #{return_code}" + when 0x00 + 'Connection Accepted' + when 0x01 + 'Connection refused: unacceptable protocol version' + when 0x02 + 'Connection refused: client identifier rejected' + when 0x03 + 'Connection refused: server unavailable' + when 0x04 + 'Connection refused: bad user name or password' + when 0x05 + 'Connection refused: not authorised' + else + "Connection refused: error code #{return_code}" end end # Get serialisation of packet's body def encode_body body = '' body += encode_bits(@connack_flags) body += encode_bytes(@return_code.to_i) - return body + body end # Parse the body (variable header and payload) of a Connect Acknowledgment packet def parse_body(buffer) super(buffer) @connack_flags = shift_bits(buffer) - unless @connack_flags[1,7] == [false, false, false, false, false, false, false] - raise ProtocolException.new("Invalid flags in Connack variable header") + unless @connack_flags[1, 7] == [false, false, false, false, false, false, false] + raise ProtocolException, 'Invalid flags in Connack variable header' end @return_code = shift_byte(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Connect Acknowledgment packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Connect Acknowledgment packet' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % return_code @@ -664,13 +635,13 @@ # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Publish Acknowledgment packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Publish Acknowledgment packet' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % id @@ -686,31 +657,30 @@ # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Publish Received packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Publish Received packet' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % id end end # Class representing an MQTT Publish Release packet class Pubrel < MQTT::Packet - # Default attribute values ATTR_DEFAULTS = { - :flags => [false, true, false, false], + :flags => [false, true, false, false] } # Create a new Pubrel packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) end # Get serialisation of packet's body def encode_body @@ -719,21 +689,20 @@ # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Publish Release packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Publish Release packet' end # Check that fixed header flags are valid for this packet type # @private def validate_flags - if @flags != [false, true, false, false] - raise ProtocolException.new("Invalid flags in PUBREL packet header") - end + return if @flags == [false, true, false, false] + raise ProtocolException, 'Invalid flags in PUBREL packet header' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % id @@ -749,13 +718,13 @@ # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Publish Complete packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Publish Complete packet' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % id @@ -768,15 +737,15 @@ attr_accessor :topics # Default attribute values ATTR_DEFAULTS = { :topics => [], - :flags => [false, true, false, false], + :flags => [false, true, false, false] } # Create a new Subscribe packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) end # Set one or more topic filters for the Subscribe packet # The topics parameter should be one of the following: @@ -790,29 +759,25 @@ # packet.topics = [['a/b',0], ['c/d',1]] # packet.topics = {'a/b' => 0, 'c/d' => 1} # def topics=(value) # Get input into a consistent state - if value.is_a?(Array) - input = value.flatten - else - input = [value] - end + input = value.is_a?(Array) ? value.flatten : [value] @topics = [] - while(input.length>0) + until input.empty? item = input.shift if item.is_a?(Hash) # Convert hash into an ordered array of arrays @topics += item.sort elsif item.is_a?(String) # Peek at the next item in the array, and remove it if it is an integer if input.first.is_a?(Integer) qos = input.shift - @topics << [item,qos] + @topics << [item, qos] else - @topics << [item,0] + @topics << [item, 0] end else # Meh? raise "Invalid topics input: #{value.inspect}" end @@ -820,46 +785,43 @@ @topics end # Get serialisation of packet's body def encode_body - if @topics.empty? - raise "no topics given when serialising packet" - end + raise 'no topics given when serialising packet' if @topics.empty? body = encode_short(@id) topics.each do |item| body += encode_string(item[0]) body += encode_bytes(item[1]) end - return body + body end # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) @topics = [] - while(buffer.bytesize>0) + while buffer.bytesize > 0 topic_name = shift_string(buffer) topic_qos = shift_byte(buffer) - @topics << [topic_name,topic_qos] + @topics << [topic_name, topic_qos] end end # Check that fixed header flags are valid for this packet type # @private def validate_flags - if @flags != [false, true, false, false] - raise ProtocolException.new("Invalid flags in SUBSCRIBE packet header") - end + return if @flags == [false, true, false, false] + raise ProtocolException, 'Invalid flags in SUBSCRIBE packet header' end # Returns a human readable string, summarising the properties of the packet def inspect _str = "\#<#{self.class}: 0x%2.2X, %s>" % [ id, - topics.map {|t| "'#{t[0]}':#{t[1]}"}.join(', ') + topics.map { |t| "'#{t[0]}':#{t[1]}" }.join(', ') ] end end # Class representing an MQTT Subscribe Acknowledgment packet @@ -867,15 +829,15 @@ # An array of return codes, ordered by the topics that were subscribed to attr_accessor :return_codes # Default attribute values ATTR_DEFAULTS = { - :return_codes => [], + :return_codes => [] } # Create a new Subscribe Acknowledgment packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) end # Set the granted QoS value for each of the topics that were subscribed to # Can either be an integer or an array or integers. @@ -883,40 +845,37 @@ if value.is_a?(Array) @return_codes = value elsif value.is_a?(Integer) @return_codes = [value] else - raise "return_codes should be an integer or an array of return codes" + raise 'return_codes should be an integer or an array of return codes' end end # Get serialisation of packet's body def encode_body if @return_codes.empty? - raise "no granted QoS given when serialising packet" + raise 'no granted QoS given when serialising packet' end body = encode_short(@id) return_codes.each { |qos| body += encode_bytes(qos) } - return body + body end # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - while(buffer.bytesize>0) - @return_codes << shift_byte(buffer) - end + @return_codes << shift_byte(buffer) while buffer.bytesize > 0 end # Returns a human readable string, summarising the properties of the packet def inspect - "\#<#{self.class}: 0x%2.2X, rc=%s>" % [id, return_codes.map{|rc| "0x%2.2X" % rc}.join(',')] + "\#<#{self.class}: 0x%2.2X, rc=%s>" % [id, return_codes.map { |rc| '0x%2.2X' % rc }.join(',')] end # ---- Deprecated attributes and methods ---- # - public # @deprecated Please use {#return_codes} instead def granted_qos return_codes end @@ -933,67 +892,58 @@ attr_accessor :topics # Default attribute values ATTR_DEFAULTS = { :topics => [], - :flags => [false, true, false, false], + :flags => [false, true, false, false] } # Create a new Unsubscribe packet - def initialize(args={}) + def initialize(args = {}) super(ATTR_DEFAULTS.merge(args)) end # Set one or more topic paths to unsubscribe from def topics=(value) - if value.is_a?(Array) - @topics = value - else - @topics = [value] - end + @topics = value.is_a?(Array) ? value : [value] end # Get serialisation of packet's body def encode_body - if @topics.empty? - raise "no topics given when serialising packet" - end + raise 'no topics given when serialising packet' if @topics.empty? body = encode_short(@id) topics.each { |topic| body += encode_string(topic) } - return body + body end # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - while(buffer.bytesize>0) - @topics << shift_string(buffer) - end + @topics << shift_string(buffer) while buffer.bytesize > 0 end # Check that fixed header flags are valid for this packet type # @private def validate_flags - if @flags != [false, true, false, false] - raise ProtocolException.new("Invalid flags in UNSUBSCRIBE packet header") - end + return if @flags == [false, true, false, false] + raise ProtocolException, 'Invalid flags in UNSUBSCRIBE packet header' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X, %s>" % [ id, - topics.map {|t| "'#{t}'"}.join(', ') + topics.map { |t| "'#{t}'" }.join(', ') ] end end # Class representing an MQTT Unsubscribe Acknowledgment packet class Unsuback < MQTT::Packet # Create a new Unsubscribe Acknowledgment packet - def initialize(args={}) + def initialize(args = {}) super(args) end # Get serialisation of packet's body def encode_body @@ -1002,13 +952,13 @@ # Parse the body (variable header and payload) of a packet def parse_body(buffer) super(buffer) @id = shift_short(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Unsubscribe Acknowledgment packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Unsubscribe Acknowledgment packet' end # Returns a human readable string, summarising the properties of the packet def inspect "\#<#{self.class}: 0x%2.2X>" % id @@ -1016,56 +966,55 @@ end # Class representing an MQTT Ping Request packet class Pingreq < MQTT::Packet # Create a new Ping Request packet - def initialize(args={}) + def initialize(args = {}) super(args) end # Check the body def parse_body(buffer) super(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Ping Request packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Ping Request packet' end end # Class representing an MQTT Ping Response packet class Pingresp < MQTT::Packet # Create a new Ping Response packet - def initialize(args={}) + def initialize(args = {}) super(args) end # Check the body def parse_body(buffer) super(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Ping Response packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Ping Response packet' end end # Class representing an MQTT Client Disconnect packet class Disconnect < MQTT::Packet # Create a new Client Disconnect packet - def initialize(args={}) + def initialize(args = {}) super(args) end # Check the body def parse_body(buffer) super(buffer) - unless buffer.empty? - raise ProtocolException.new("Extra bytes at end of Disconnect packet") - end + + return if buffer.empty? + raise ProtocolException, 'Extra bytes at end of Disconnect packet' end end - # ---- Deprecated attributes and methods ---- # public # @deprecated Please use {#id} instead def message_id @@ -1076,11 +1025,10 @@ def message_id=(args) self.id = args end end - # An enumeration of the MQTT packet types PACKET_TYPES = [ nil, MQTT::Packet::Connect, MQTT::Packet::Connack, @@ -1096,7 +1044,6 @@ MQTT::Packet::Pingreq, MQTT::Packet::Pingresp, MQTT::Packet::Disconnect, nil ] - end