mqtt 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/mqtt/packet.rb CHANGED
@@ -1,96 +1,162 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'mqtt'
4
-
5
1
  module MQTT
6
2
 
7
3
  # Class representing a MQTT Packet
8
4
  # Performs binary encoding and decoding of headers
9
- class Packet
10
- attr_reader :dup # Duplicate delivery flag
11
- attr_reader :retain # Retain flag
12
- attr_reader :qos # Quality of Service level
13
-
14
- # Read in a packet from a socket
5
+ class MQTT::Packet
6
+ attr_reader :dup # Duplicate delivery flag
7
+ attr_reader :retain # Retain flag
8
+ attr_reader :qos # Quality of Service level
9
+ attr_reader :body_length # The length of the parsed packet body
10
+
11
+ # Deprecate this: Read in a packet from a socket
15
12
  def self.read(socket)
16
13
  # Read in the packet header and work out the class
17
14
  header = read_byte(socket)
18
15
  type_id = ((header & 0xF0) >> 4)
19
16
  packet_class = MQTT::PACKET_TYPES[type_id]
20
-
17
+
21
18
  # Create a new packet object
22
19
  packet = packet_class.new(
23
20
  :dup => ((header & 0x08) >> 3),
24
21
  :qos => ((header & 0x06) >> 1),
25
22
  :retain => ((header & 0x01) >> 0)
26
23
  )
27
-
24
+
28
25
  # Read in the packet length
29
- multiplier = 1
30
- body_len = 0
26
+ multiplier = 1
27
+ body_length = 0
31
28
  begin
32
29
  digit = read_byte(socket)
33
- body_len += ((digit & 0x7F) * multiplier)
30
+ body_length += ((digit & 0x7F) * multiplier)
34
31
  multiplier *= 0x80
35
32
  end while ((digit & 0x80) != 0x00)
36
33
  # FIXME: only allow 4 bytes?
37
34
 
35
+ # Store the expected body length in the packet
36
+ packet.instance_variable_set('@body_length', body_length)
37
+
38
38
  # Read in the packet body
39
- packet.parse_body( socket.read(body_len) )
39
+ packet.parse_body( socket.read(body_length) )
40
+
41
+ return packet
42
+ end
40
43
 
44
+ # Parse buffer into new packet object
45
+ def self.parse(buffer)
46
+ packet = parse_header(buffer)
47
+ packet.parse_body(buffer)
41
48
  return packet
42
49
  end
43
50
 
51
+ # Parse the header and create a new packet object of the correct type
52
+ # The header is removed from the buffer passed into this function
53
+ def self.parse_header(buffer)
54
+ # Work out the class
55
+ type_id = ((buffer[0] & 0xF0) >> 4)
56
+ packet_class = MQTT::PACKET_TYPES[type_id]
57
+ if packet_class.nil?
58
+ raise ProtocolException.new("Invalid packet type identifier: #{type_id}")
59
+ end
60
+
61
+ # Create a new packet object
62
+ packet = packet_class.new(
63
+ :dup => ((buffer[0] & 0x08) >> 3) == 0x01,
64
+ :qos => ((buffer[0] & 0x06) >> 1),
65
+ :retain => ((buffer[0] & 0x01) >> 0) == 0x01
66
+ )
67
+
68
+ # Parse the packet length
69
+ body_length = 0
70
+ multiplier = 1
71
+ pos = 1
72
+ begin
73
+ if buffer.length <= pos
74
+ raise ProtocolException.new("The packet length header is incomplete")
75
+ end
76
+ digit = buffer[pos]
77
+ body_length += ((digit & 0x7F) * multiplier)
78
+ multiplier *= 0x80
79
+ pos += 1
80
+ end while ((digit & 0x80) != 0x00) and pos <= 4
81
+
82
+ # Store the expected body length in the packet
83
+ packet.instance_variable_set('@body_length', body_length)
84
+
85
+ # Delete the variable length header from the raw packet passed in
86
+ buffer.slice!(0...pos)
87
+
88
+ return packet
89
+ end
90
+
91
+
44
92
  # Create a new empty packet
45
93
  def initialize(args={})
46
- self.dup = args[:dup] || false
47
- self.qos = args[:qos] || 0
48
- self.retain = args[:retain] || false
94
+ update_attributes({
95
+ :dup => false,
96
+ :qos => 0,
97
+ :retain => false,
98
+ :body_length => nil
99
+ }.merge(args))
49
100
  end
50
-
101
+
102
+ def update_attributes(attr={})
103
+ attr.each_pair do |k,v|
104
+ send("#{k}=", v)
105
+ end
106
+ end
107
+
51
108
  # Get the identifer for this packet type
52
109
  def type_id
53
110
  index = MQTT::PACKET_TYPES.index(self.class)
54
- raise "Invalid packet type: #{self.class}" if index.nil?
111
+ if index.nil?
112
+ raise "Invalid packet type: #{self.class}"
113
+ end
55
114
  return index
56
115
  end
57
-
116
+
58
117
  # Set the dup flag (true/false)
59
118
  def dup=(arg)
60
119
  if arg.kind_of?(Integer)
61
- @dup = (arg != 0 ? true : false)
120
+ @dup = (arg != 0)
62
121
  else
63
122
  @dup = arg
64
123
  end
65
124
  end
66
-
125
+
67
126
  # Set the retain flag (true/false)
68
127
  def retain=(arg)
69
128
  if arg.kind_of?(Integer)
70
- @retain = (arg != 0 ? true : false)
129
+ @retain = (arg != 0)
71
130
  else
72
131
  @retain = arg
73
132
  end
74
133
  end
75
-
134
+
76
135
  # Set the Quality of Service level (0/1/2)
77
136
  def qos=(arg)
78
137
  @qos = arg.to_i
79
138
  end
80
139
 
140
+ # Set the length of the packet body
141
+ def body_length=(arg)
142
+ @body_length = arg.to_i
143
+ end
144
+
81
145
  # Parse the body (variable header and payload) of a packet
82
146
  def parse_body(buffer)
83
- unless buffer.size == 0
84
- raise MQTT::ProtocolException.new("Error: parse_body was not sub-classed for a packet with a payload")
147
+ if buffer.length != body_length
148
+ raise ProtocolException.new(
149
+ "Failed to parse packet - input buffer (#{buffer.length}) is not the same as the body length buffer (#{body_length})"
150
+ )
85
151
  end
86
152
  end
87
-
153
+
88
154
  # Get serialisation of packet's body (variable header and payload)
89
155
  def encode_body
90
156
  '' # No body by default
91
157
  end
92
158
 
93
-
159
+
94
160
  # Serialise the packet
95
161
  def to_s
96
162
  # Encode the fixed header
@@ -100,19 +166,19 @@ module MQTT
100
166
  ((qos.to_i & 0x03) << 1) |
101
167
  (retain ? 0x1 : 0x0)
102
168
  ]
103
-
169
+
104
170
  # Get the packet's variable header and payload
105
171
  body = self.encode_body
106
-
172
+
107
173
  # Build up the body length field bytes
108
- body_size = body.size
174
+ body_length = body.length
109
175
  begin
110
- digit = (body_size % 128)
111
- body_size = (body_size / 128)
176
+ digit = (body_length % 128)
177
+ body_length = (body_length / 128)
112
178
  # if there are more digits to encode, set the top bit of this digit
113
- digit |= 0x80 if (body_size > 0)
179
+ digit |= 0x80 if (body_length > 0)
114
180
  header.push(digit)
115
- end while (body_size > 0)
181
+ end while (body_length > 0)
116
182
 
117
183
  # Convert header to binary and add on body
118
184
  header.pack('C*') + body
@@ -120,7 +186,7 @@ module MQTT
120
186
 
121
187
 
122
188
  protected
123
-
189
+
124
190
  # Encode an array of bytes and return them
125
191
  def encode_bytes(*bytes)
126
192
  bytes.pack('C*')
@@ -135,45 +201,47 @@ module MQTT
135
201
  # (preceded by the length of the string)
136
202
  def encode_string(str)
137
203
  str = str.to_s unless str.is_a?(String)
138
- encode_short(str.size) + str
204
+ encode_short(str.length) + str
139
205
  end
140
-
206
+
141
207
  # Remove a 16-bit unsigned integer from the front of buffer
142
208
  def shift_short(buffer)
143
209
  bytes = buffer.slice!(0..1)
144
210
  bytes.unpack('n').first
145
211
  end
146
-
212
+
147
213
  # Remove one byte from the front of the string
148
214
  def shift_byte(buffer)
149
215
  buffer.slice!(0...1).unpack('C').first
150
216
  end
151
-
217
+
152
218
  # Remove n bytes from the front of buffer
153
219
  def shift_data(buffer,bytes)
154
220
  buffer.slice!(0...bytes)
155
221
  end
156
-
222
+
157
223
  # Remove string from the front of buffer
158
224
  def shift_string(buffer)
159
225
  len = shift_short(buffer)
160
226
  shift_data(buffer,len)
161
227
  end
162
228
 
163
-
229
+
164
230
  private
165
-
231
+
166
232
  # Read and unpack a single byte from a socket
167
233
  def self.read_byte(socket)
168
234
  byte = socket.read(1)
169
- raise MQTT::ProtocolException if byte.nil?
235
+ if byte.nil?
236
+ raise ProtocolException
237
+ end
170
238
  byte.unpack('C').first
171
239
  end
172
240
 
173
241
 
174
242
 
175
243
  ## PACKET SUBCLASSES ##
176
-
244
+
177
245
 
178
246
  # Class representing an MQTT Publish message
179
247
  class Publish < MQTT::Packet
@@ -181,26 +249,30 @@ module MQTT
181
249
  attr_accessor :message_id
182
250
  attr_accessor :payload
183
251
 
184
- # Create a new Unsubscribe Acknowledgment packet
252
+ # Create a new Publish packet
185
253
  def initialize(args={})
186
- super(args)
187
- self.topic = args[:topic] || nil
188
- self.message_id = args[:message_id] || 0
189
- self.payload = args[:payload] || ''
254
+ super({
255
+ :topic => nil,
256
+ :message_id => 0,
257
+ :payload => ''
258
+ }.merge(args))
190
259
  end
191
-
260
+
192
261
  # Get serialisation of packet's body
193
262
  def encode_body
194
263
  body = ''
195
- raise "Invalid topic name when serialising packet" if @topic.nil?
264
+ if @topic.nil?
265
+ raise "Invalid topic name when serialising packet"
266
+ end
196
267
  body += encode_string(@topic)
197
268
  body += encode_short(@message_id) unless qos == 0
198
269
  body += payload.to_s
199
270
  return body
200
271
  end
201
-
272
+
202
273
  # Parse the body (variable header and payload) of a Publish packet
203
274
  def parse_body(buffer)
275
+ super(buffer)
204
276
  @topic = shift_string(buffer)
205
277
  @message_id = shift_short(buffer) unless qos == 0
206
278
  @payload = buffer.dup
@@ -212,52 +284,88 @@ module MQTT
212
284
  attr_accessor :protocol_name
213
285
  attr_accessor :protocol_version
214
286
  attr_accessor :client_id
215
- attr_accessor :clean_start
287
+ attr_accessor :clean_session
216
288
  attr_accessor :keep_alive
217
289
  attr_accessor :will_topic
218
290
  attr_accessor :will_qos
219
291
  attr_accessor :will_retain
220
292
  attr_accessor :will_payload
293
+ attr_accessor :username
294
+ attr_accessor :password
295
+
296
+ # OLD deprecated clean_start
297
+ alias :clean_start :clean_session
298
+ alias :clean_start= :clean_session=
221
299
 
222
300
  # Create a new Client Connect packet
223
301
  def initialize(args={})
224
- super(args)
225
- self.protocol_name = args[:protocol_name] || 'MQIsdp'
226
- self.protocol_version = args[:protocol_version] || 0x03
227
- self.client_id = args[:client_id] || nil
228
- self.clean_start = args[:clean_start] || true
229
- self.keep_alive = args[:keep_alive] || 10
230
- self.will_topic = args[:will_topic] || nil
231
- self.will_qos = args[:will_qos] || 0
232
- self.will_retain = args[:will_retain] || false
233
- self.will_payload = args[:will_payload] || ''
234
- end
235
-
302
+ super({
303
+ :protocol_name => 'MQIsdp',
304
+ :protocol_version => 0x03,
305
+ :client_id => nil,
306
+ :clean_session => true,
307
+ :keep_alive => 10,
308
+ :will_topic => nil,
309
+ :will_qos => 0,
310
+ :will_retain => false,
311
+ :will_payload => '',
312
+ :username => nil,
313
+ :password => nil,
314
+ }.merge(args))
315
+ end
316
+
236
317
  # Get serialisation of packet's body
237
318
  def encode_body
238
319
  body = ''
239
- raise "Invalid client identifier when serialising packet" if @client_id.nil?
320
+ if @client_id.nil? or @client_id.length < 1 or @client_id.length > 23
321
+ raise "Invalid client identifier when serialising packet"
322
+ end
240
323
  body += encode_string(@protocol_name)
241
324
  body += encode_bytes(@protocol_version.to_i)
242
- body += encode_bytes(0) # Connect Flags
243
- body += encode_short(@keep_alive) # Keep Alive timer
325
+
326
+ # Set the Connect flags
327
+ @connect_flags = 0
328
+ @connect_flags |= 0x02 if @clean_session
329
+ @connect_flags |= 0x04 unless @will_topic.nil?
330
+ @connect_flags |= ((@will_qos & 0x03) << 3)
331
+ @connect_flags |= 0x20 if @will_retain
332
+ @connect_flags |= 0x40 unless @password.nil?
333
+ @connect_flags |= 0x80 unless @username.nil?
334
+ body += encode_bytes(@connect_flags)
335
+
336
+ body += encode_short(@keep_alive)
244
337
  body += encode_string(@client_id)
245
- # FIXME: implement Will
246
- #unless @will_topic.nil?
247
- # body += encode_string(@will_topic)
248
- # body += will_payload.to_s
249
- #end
338
+ unless will_topic.nil?
339
+ body += encode_string(@will_topic)
340
+ body += encode_string(@will_payload)
341
+ end
342
+ body += encode_string(@username) unless @username.nil?
343
+ body += encode_string(@password) unless @password.nil?
250
344
  return body
251
345
  end
252
-
346
+
253
347
  # Parse the body (variable header and payload) of a Connect packet
254
348
  def parse_body(buffer)
349
+ super(buffer)
255
350
  @protocol_name = shift_string(buffer)
256
351
  @protocol_version = shift_byte(buffer)
257
- flags = shift_byte(buffer)
352
+ @connect_flags = shift_byte(buffer)
353
+ @clean_session = ((@connect_flags & 0x02) >> 1) == 0x01
258
354
  @keep_alive = shift_short(buffer)
259
355
  @client_id = shift_string(buffer)
260
- # FIXME: implement Will
356
+ if ((@connect_flags & 0x04) >> 2) == 0x01
357
+ # Last Will and Testament
358
+ @will_qos = ((@connect_flags & 0x18) >> 3)
359
+ @will_retain = ((@connect_flags & 0x20) >> 5) == 0x01
360
+ @will_topic = shift_string(buffer)
361
+ @will_payload = shift_string(buffer)
362
+ end
363
+ if ((@connect_flags & 0x80) >> 7) == 0x01 and buffer.length > 0
364
+ @username = shift_string(buffer)
365
+ end
366
+ if ((@connect_flags & 0x40) >> 6) == 0x01 and buffer.length > 0
367
+ @password = shift_string(buffer)
368
+ end
261
369
  end
262
370
  end
263
371
 
@@ -267,10 +375,11 @@ module MQTT
267
375
 
268
376
  # Create a new Client Connect packet
269
377
  def initialize(args={})
270
- super(args)
271
- self.return_code = args[:return_code] || 0
378
+ super({
379
+ :return_code => 0x00
380
+ }.merge(args))
272
381
  end
273
-
382
+
274
383
  # Get a string message corresponding to a return code
275
384
  def return_msg
276
385
  case return_code
@@ -282,11 +391,15 @@ module MQTT
282
391
  "Connection refused: client identifier rejected"
283
392
  when 0x03
284
393
  "Connection refused: broker unavailable"
394
+ when 0x04
395
+ "Connection refused: bad user name or password"
396
+ when 0x05
397
+ "Connection refused: not authorised"
285
398
  else
286
399
  "Connection refused: error code #{return_code}"
287
400
  end
288
401
  end
289
-
402
+
290
403
  # Get serialisation of packet's body
291
404
  def encode_body
292
405
  body = ''
@@ -294,111 +407,136 @@ module MQTT
294
407
  body += encode_bytes(@return_code.to_i) # Return Code
295
408
  return body
296
409
  end
297
-
410
+
298
411
  # Parse the body (variable header and payload) of a Connect Acknowledgment packet
299
412
  def parse_body(buffer)
413
+ super(buffer)
300
414
  unused = shift_byte(buffer)
301
415
  @return_code = shift_byte(buffer)
302
- end
416
+ unless buffer.empty?
417
+ raise ProtocolException.new("Extra bytes at end of Connect Acknowledgment packet")
418
+ end
419
+ end
303
420
  end
304
421
 
305
422
  # Class representing an MQTT Publish Acknowledgment packet
306
423
  class Puback < MQTT::Packet
307
424
  attr_accessor :message_id
308
-
309
- # Create a new Unsubscribe Acknowledgment packet
425
+
426
+ # Create a new Publish Acknowledgment packet
310
427
  def initialize(args={})
311
- super(args)
312
- self.message_id = args[:message_id] || 0
428
+ super({
429
+ :message_id => 0
430
+ }.merge(args))
313
431
  end
314
-
432
+
315
433
  # Get serialisation of packet's body
316
434
  def encode_body
317
435
  encode_short(@message_id)
318
436
  end
319
-
437
+
320
438
  # Parse the body (variable header and payload) of a packet
321
439
  def parse_body(buffer)
440
+ super(buffer)
322
441
  @message_id = shift_short(buffer)
442
+ unless buffer.empty?
443
+ raise ProtocolException.new("Extra bytes at end of Publish Acknowledgment packet")
444
+ end
323
445
  end
324
446
  end
325
447
 
326
448
  # Class representing an MQTT Publish Received packet
327
449
  class Pubrec < MQTT::Packet
328
450
  attr_accessor :message_id
329
-
330
- # Create a new Unsubscribe Acknowledgment packet
451
+
452
+ # Create a new Publish Recieved packet
331
453
  def initialize(args={})
332
- super(args)
333
- self.message_id = args[:message_id] || 0
454
+ super({
455
+ :message_id => 0
456
+ }.merge(args))
334
457
  end
335
-
458
+
336
459
  # Get serialisation of packet's body
337
460
  def encode_body
338
461
  encode_short(@message_id)
339
462
  end
340
-
463
+
341
464
  # Parse the body (variable header and payload) of a packet
342
465
  def parse_body(buffer)
466
+ super(buffer)
343
467
  @message_id = shift_short(buffer)
468
+ unless buffer.empty?
469
+ raise ProtocolException.new("Extra bytes at end of Publish Received packet")
470
+ end
344
471
  end
345
472
  end
346
473
 
347
474
  # Class representing an MQTT Publish Release packet
348
475
  class Pubrel < MQTT::Packet
349
476
  attr_accessor :message_id
350
-
351
- # Create a new Unsubscribe Acknowledgment packet
477
+
478
+ # Create a new Publish Release packet
352
479
  def initialize(args={})
353
- super(args)
354
- self.message_id = args[:message_id] || 0
480
+ super({
481
+ :message_id => 0
482
+ }.merge(args))
355
483
  end
356
-
484
+
357
485
  # Get serialisation of packet's body
358
486
  def encode_body
359
487
  encode_short(@message_id)
360
488
  end
361
-
489
+
362
490
  # Parse the body (variable header and payload) of a packet
363
491
  def parse_body(buffer)
492
+ super(buffer)
364
493
  @message_id = shift_short(buffer)
494
+ unless buffer.empty?
495
+ raise ProtocolException.new("Extra bytes at end of Publish Release packet")
496
+ end
365
497
  end
366
498
  end
367
499
 
368
500
  # Class representing an MQTT Publish Complete packet
369
501
  class Pubcomp < MQTT::Packet
370
502
  attr_accessor :message_id
371
-
372
- # Create a new Unsubscribe Acknowledgment packet
503
+
504
+ # Create a new Publish Complete packet
373
505
  def initialize(args={})
374
- super(args)
375
- self.message_id = args[:message_id] || 0
506
+ super({
507
+ :message_id => 0
508
+ }.merge(args))
376
509
  end
377
-
510
+
378
511
  # Get serialisation of packet's body
379
512
  def encode_body
380
513
  encode_short(@message_id)
381
514
  end
382
-
515
+
383
516
  # Parse the body (variable header and payload) of a packet
384
517
  def parse_body(buffer)
518
+ super(buffer)
385
519
  @message_id = shift_short(buffer)
520
+ unless buffer.empty?
521
+ raise ProtocolException.new("Extra bytes at end of Publish Complete packet")
522
+ end
386
523
  end
387
524
  end
388
525
 
389
526
  # Class representing an MQTT Client Subscribe packet
390
527
  class Subscribe < MQTT::Packet
391
- attr_reader :topics
392
528
  attr_accessor :message_id
393
-
394
- # Create a new Unsubscribe Acknowledgment packet
529
+ attr_reader :topics
530
+
531
+ # Create a new Subscribe packet
395
532
  def initialize(args={})
396
- super(args)
397
- self.topics = args[:topics] || []
398
- self.message_id = args[:message_id] || 0
533
+ super({
534
+ :topics => [],
535
+ :message_id => 0
536
+ }.merge(args))
399
537
  self.qos = 1 # Force a QOS of 1
400
538
  end
401
-
539
+
402
540
  # Set one or more topics for the Subscrible packet
403
541
  # The topics parameter should be one of the following:
404
542
  # * String: subscribe to one topic with QOS 0
@@ -418,9 +556,9 @@ module MQTT
418
556
  else
419
557
  input = [value]
420
558
  end
421
-
559
+
422
560
  @topics = []
423
- while(input.size>0)
561
+ while(input.length>0)
424
562
  item = input.shift
425
563
  if item.is_a?(Hash)
426
564
  # Convert hash into an ordered array of arrays
@@ -440,10 +578,12 @@ module MQTT
440
578
  end
441
579
  @topics
442
580
  end
443
-
581
+
444
582
  # Get serialisation of packet's body
445
583
  def encode_body
446
- raise "no topics given when serialising packet" if @topics.empty?
584
+ if @topics.empty?
585
+ raise "no topics given when serialising packet"
586
+ end
447
587
  body = encode_short(@message_id)
448
588
  topics.each do |item|
449
589
  body += encode_string(item[0])
@@ -451,12 +591,13 @@ module MQTT
451
591
  end
452
592
  return body
453
593
  end
454
-
594
+
455
595
  # Parse the body (variable header and payload) of a packet
456
596
  def parse_body(buffer)
597
+ super(buffer)
457
598
  @message_id = shift_short(buffer)
458
599
  @topics = []
459
- while(buffer.size>0)
600
+ while(buffer.length>0)
460
601
  topic_name = shift_string(buffer)
461
602
  topic_qos = shift_byte(buffer)
462
603
  @topics << [topic_name,topic_qos]
@@ -468,31 +609,37 @@ module MQTT
468
609
  class Suback < MQTT::Packet
469
610
  attr_accessor :message_id
470
611
  attr_reader :granted_qos
471
-
472
- # Create a new Unsubscribe Acknowledgment packet
612
+
613
+ # Create a new Subscribe Acknowledgment packet
473
614
  def initialize(args={})
474
- super(args)
475
- self.message_id = args[:message_id] || 0
476
- self.granted_qos = args[:granted_qos] || []
615
+ super({
616
+ :granted_qos => [],
617
+ :message_id => 0
618
+ }.merge(args))
477
619
  end
478
-
620
+
479
621
  def granted_qos=(value)
480
- raise "granted QOS should be an array of arrays" unless value.is_a?(Array)
622
+ unless value.is_a?(Array)
623
+ raise "granted QOS should be an array of arrays"
624
+ end
481
625
  @granted_qos = value
482
626
  end
483
-
627
+
484
628
  # Get serialisation of packet's body
485
629
  def encode_body
486
- raise "no granted QOS given when serialising packet" if @granted_qos.empty?
630
+ if @granted_qos.empty?
631
+ raise "no granted QOS given when serialising packet"
632
+ end
487
633
  body = encode_short(@message_id)
488
634
  granted_qos.flatten.each { |qos| body += encode_bytes(qos) }
489
635
  return body
490
636
  end
491
-
637
+
492
638
  # Parse the body (variable header and payload) of a packet
493
639
  def parse_body(buffer)
640
+ super(buffer)
494
641
  @message_id = shift_short(buffer)
495
- while(buffer.size>0)
642
+ while(buffer.length>0)
496
643
  @granted_qos << [shift_byte(buffer),shift_byte(buffer)]
497
644
  end
498
645
  end
@@ -502,15 +649,16 @@ module MQTT
502
649
  class Unsubscribe < MQTT::Packet
503
650
  attr_reader :topics
504
651
  attr_accessor :message_id
505
-
506
- # Create a new Unsubscribe Acknowledgment packet
652
+
653
+ # Create a new Unsubscribe packet
507
654
  def initialize(args={})
508
- super(args)
509
- self.topics = args[:topics] || []
510
- self.message_id = args[:message_id] || 0
655
+ super({
656
+ :topics => [],
657
+ :message_id => 0
658
+ }.merge(args))
511
659
  self.qos = 1 # Force a QOS of 1
512
660
  end
513
-
661
+
514
662
  def topics=(value)
515
663
  if value.is_a?(Array)
516
664
  @topics = value
@@ -518,19 +666,22 @@ module MQTT
518
666
  @topics = [value]
519
667
  end
520
668
  end
521
-
669
+
522
670
  # Get serialisation of packet's body
523
671
  def encode_body
524
- raise "no topics given when serialising packet" if @topics.empty?
672
+ if @topics.empty?
673
+ raise "no topics given when serialising packet"
674
+ end
525
675
  body = encode_short(@message_id)
526
676
  topics.each { |topic| body += encode_string(topic) }
527
677
  return body
528
678
  end
529
-
679
+
530
680
  # Parse the body (variable header and payload) of a packet
531
681
  def parse_body(buffer)
682
+ super(buffer)
532
683
  @message_id = shift_short(buffer)
533
- while(buffer.size>0)
684
+ while(buffer.length>0)
534
685
  @topics << shift_string(buffer)
535
686
  end
536
687
  end
@@ -539,21 +690,26 @@ module MQTT
539
690
  # Class representing an MQTT Unsubscribe Acknowledgment packet
540
691
  class Unsuback < MQTT::Packet
541
692
  attr_accessor :message_id
542
-
693
+
543
694
  # Create a new Unsubscribe Acknowledgment packet
544
695
  def initialize(args={})
545
- super(args)
546
- self.message_id = args[:message_id] || 0
696
+ super({
697
+ :message_id => 0
698
+ }.merge(args))
547
699
  end
548
-
700
+
549
701
  # Get serialisation of packet's body
550
702
  def encode_body
551
703
  encode_short(@message_id)
552
704
  end
553
-
705
+
554
706
  # Parse the body (variable header and payload) of a packet
555
707
  def parse_body(buffer)
708
+ super(buffer)
556
709
  @message_id = shift_short(buffer)
710
+ unless buffer.empty?
711
+ raise ProtocolException.new("Extra bytes at end of Unsubscribe Acknowledgment packet")
712
+ end
557
713
  end
558
714
  end
559
715
 
@@ -563,6 +719,14 @@ module MQTT
563
719
  def initialize(args={})
564
720
  super(args)
565
721
  end
722
+
723
+ # Check the body
724
+ def parse_body(buffer)
725
+ super(buffer)
726
+ unless buffer.empty?
727
+ raise ProtocolException.new("Extra bytes at end of Ping Request packet")
728
+ end
729
+ end
566
730
  end
567
731
 
568
732
  # Class representing an MQTT Ping Response packet
@@ -571,6 +735,14 @@ module MQTT
571
735
  def initialize(args={})
572
736
  super(args)
573
737
  end
738
+
739
+ # Check the body
740
+ def parse_body(buffer)
741
+ super(buffer)
742
+ unless buffer.empty?
743
+ raise ProtocolException.new("Extra bytes at end of Ping Response packet")
744
+ end
745
+ end
574
746
  end
575
747
 
576
748
  # Class representing an MQTT Client Disconnect packet
@@ -579,8 +751,16 @@ module MQTT
579
751
  def initialize(args={})
580
752
  super(args)
581
753
  end
754
+
755
+ # Check the body
756
+ def parse_body(buffer)
757
+ super(buffer)
758
+ unless buffer.empty?
759
+ raise ProtocolException.new("Extra bytes at end of Disconnect packet")
760
+ end
761
+ end
582
762
  end
583
-
763
+
584
764
  end
585
765
 
586
766
 
@@ -603,5 +783,5 @@ module MQTT
603
783
  MQTT::Packet::Disconnect,
604
784
  nil
605
785
  ]
606
-
786
+
607
787
  end