mqtt 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/mqtt/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module MQTT
2
2
  # The version number of the MQTT gem
3
- VERSION = "0.4.0"
3
+ VERSION = '0.6.0'
4
4
  end
data/lib/mqtt.rb CHANGED
@@ -13,7 +13,6 @@ unless String.method_defined?(:force_encoding)
13
13
  end
14
14
 
15
15
  module MQTT
16
-
17
16
  # Default port number for unencrypted connections
18
17
  DEFAULT_PORT = 1883
19
18
 
@@ -41,7 +40,6 @@ module MQTT
41
40
 
42
41
  # MQTT-SN
43
42
  module SN
44
-
45
43
  # Default port number for unencrypted connections
46
44
  DEFAULT_PORT = 1883
47
45
 
@@ -50,6 +48,6 @@ module MQTT
50
48
  class ProtocolException < MQTT::Exception
51
49
  end
52
50
 
53
- autoload :Packet, 'mqtt/sn/packet'
51
+ autoload :Packet, 'mqtt/sn/packet'
54
52
  end
55
53
  end
@@ -23,12 +23,23 @@ describe MQTT::Client do
23
23
  end
24
24
  end
25
25
 
26
+ if Process.const_defined? :CLOCK_MONOTONIC
27
+ def now
28
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
29
+ end
30
+ else
31
+ # Support older Ruby
32
+ def now
33
+ Time.now.to_f
34
+ end
35
+ end
36
+
26
37
  describe "initializing a client" do
27
38
  it "with no arguments, it should use the defaults" do
28
39
  client = MQTT::Client.new
29
40
  expect(client.host).to eq(nil)
30
41
  expect(client.port).to eq(1883)
31
- expect(client.version).to eq('3.1.0')
42
+ expect(client.version).to eq('3.1.1')
32
43
  expect(client.keep_alive).to eq(15)
33
44
  end
34
45
 
@@ -117,6 +128,22 @@ describe MQTT::Client do
117
128
  expect(client.password).to eq('bpass')
118
129
  end
119
130
 
131
+ it "with a URI containing an escaped username and password" do
132
+ client = MQTT::Client.new(URI.parse('mqtt://foo%20bar:%40123%2B%25@mqtt.example.com'))
133
+ expect(client.host).to eq('mqtt.example.com')
134
+ expect(client.port).to eq(1883)
135
+ expect(client.username).to eq('foo bar')
136
+ expect(client.password).to eq('@123+%')
137
+ end
138
+
139
+ it "with a URI containing a double escaped username and password" do
140
+ client = MQTT::Client.new(URI.parse('mqtt://foo%2520bar:123%2525@mqtt.example.com'))
141
+ expect(client.host).to eq('mqtt.example.com')
142
+ expect(client.port).to eq(1883)
143
+ expect(client.username).to eq('foo%20bar')
144
+ expect(client.password).to eq('123%25')
145
+ end
146
+
120
147
  it "with a URI as a string" do
121
148
  client = MQTT::Client.new('mqtt://mqtt.example.com')
122
149
  expect(client.host).to eq('mqtt.example.com')
@@ -175,7 +202,7 @@ describe MQTT::Client do
175
202
  expect(client.ssl_context.cert).to be_a(OpenSSL::X509::Certificate)
176
203
  end
177
204
  end
178
-
205
+
179
206
  describe "setting a client private key file path" do
180
207
  it "should add a certificate to the SSL context" do
181
208
  expect(client.ssl_context.key).to be_nil
@@ -323,9 +350,9 @@ describe MQTT::Client do
323
350
  client.password = 'password'
324
351
  client.connect('myclient')
325
352
  expect(socket.string).to eq(
326
- "\x10\x2A"+
327
- "\x00\x06MQIsdp"+
328
- "\x03\xC2\x00\x0f"+
353
+ "\x10\x28"+
354
+ "\x00\x04MQTT"+
355
+ "\x04\xC2\x00\x0f"+
329
356
  "\x00\x08myclient"+
330
357
  "\x00\x08username"+
331
358
  "\x00\x08password"
@@ -403,6 +430,15 @@ describe MQTT::Client do
403
430
  allow(client).to receive(:receive_connack)
404
431
  client.connect
405
432
  end
433
+
434
+ it "should use set hostname on the SSL socket for SNI" do
435
+ expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
436
+ expect(ssl_socket).to receive(:hostname=).with('mqtt.example.com')
437
+
438
+ client = MQTT::Client.new('mqtts://mqtt.example.com')
439
+ allow(client).to receive(:receive_connack)
440
+ client.connect
441
+ end
406
442
  end
407
443
 
408
444
  context "with a last will and testament set" do
@@ -429,9 +465,9 @@ describe MQTT::Client do
429
465
  it "should include the will in the CONNECT message" do
430
466
  client.connect('myclient')
431
467
  expect(socket.string).to eq(
432
- "\x10\x24"+
433
- "\x00\x06MQIsdp"+
434
- "\x03\x0e\x00\x0f"+
468
+ "\x10\x22"+
469
+ "\x00\x04MQTT"+
470
+ "\x04\x0e\x00\x0f"+
435
471
  "\x00\x08myclient"+
436
472
  "\x00\x05topic\x00\x05hello"
437
473
  )
@@ -473,6 +509,7 @@ describe MQTT::Client do
473
509
  socket.write("\x20\x02\x00\x00")
474
510
  socket.rewind
475
511
  expect { client.send(:receive_connack) }.not_to raise_error
512
+ expect(socket).not_to be_closed
476
513
  end
477
514
 
478
515
  it "should raise an exception if the packet type isn't CONNACK" do
@@ -504,6 +541,13 @@ describe MQTT::Client do
504
541
  socket.rewind
505
542
  expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /connection refused/i)
506
543
  end
544
+
545
+ it "should close the socket for an unsuccessful CONNACK packet" do
546
+ socket.write("\x20\x02\x00\x05")
547
+ socket.rewind
548
+ expect { client.send(:receive_connack) }.to raise_error(MQTT::ProtocolException, /not authorised/i)
549
+ expect(socket).to be_closed
550
+ end
507
551
  end
508
552
 
509
553
  describe "when calling the 'disconnect' method" do
@@ -538,10 +582,48 @@ describe MQTT::Client do
538
582
  end
539
583
 
540
584
  describe "when calling the 'publish' method" do
585
+ class ClientWithPubackInjection < MQTT::Client
586
+ def initialize
587
+ super(:host => 'localhost')
588
+ @injected_pubacks = {}
589
+ end
590
+
591
+ def inject_puback(packet)
592
+ @injected_pubacks[packet.id] = packet
593
+ end
594
+
595
+ def wait_for_puback(id, queue)
596
+ packet = @injected_pubacks.fetch(id) {
597
+ return super
598
+ }
599
+ queue << packet
600
+ end
601
+ end
602
+
603
+ let(:client) { ClientWithPubackInjection.new }
604
+
541
605
  before(:each) do
542
606
  client.instance_variable_set('@socket', socket)
543
607
  end
544
608
 
609
+ it "should respect timeouts" do
610
+ require "socket"
611
+ rd, wr = UNIXSocket.pair
612
+ client = MQTT::Client.new(:host => 'localhost', :ack_timeout => 1.0)
613
+ client.instance_variable_set('@socket', rd)
614
+ t = Thread.new {
615
+ Thread.current[:parent] = Thread.main
616
+ loop do
617
+ client.send :receive_packet
618
+ end
619
+ }
620
+ start = now
621
+ expect(client.publish('topic','payload', false, 1)).to eq(-1)
622
+ elapsed = now - start
623
+ t.kill
624
+ expect(elapsed).to be_within(0.1).of(1.0)
625
+ end
626
+
545
627
  it "should write a valid PUBLISH packet to the socket without the retain flag" do
546
628
  client.publish('topic','payload', false, 0)
547
629
  expect(socket.string).to eq("\x30\x0e\x00\x05topicpayload")
@@ -558,6 +640,19 @@ describe MQTT::Client do
558
640
  expect(socket.string).to eq("\x32\x10\x00\x05topic\x00\x01payload")
559
641
  end
560
642
 
643
+ it "should wrap the packet id after 65535" do
644
+ 0xffff.times do |n|
645
+ inject_puback(n + 1)
646
+ client.publish('topic','payload', false, 1)
647
+ end
648
+ expect(client.instance_variable_get(:@last_packet_id)).to eq(0xffff)
649
+
650
+ socket.string = ""
651
+ inject_puback(1)
652
+ client.publish('topic','payload', false, 1)
653
+ expect(socket.string).to eq("\x32\x10\x00\x05topic\x00\x01payload")
654
+ end
655
+
561
656
  it "should write a valid PUBLISH packet to the socket with the QoS set to 2" do
562
657
  inject_puback(1)
563
658
  client.publish('topic','payload', false, 2)
@@ -595,7 +690,7 @@ describe MQTT::Client do
595
690
  it "correctly assigns consecutive ids to packets with QoS 1" do
596
691
  inject_puback(1)
597
692
  inject_puback(2)
598
-
693
+
599
694
  expect(client).to receive(:send_packet) { |packet| expect(packet.id).to eq(1) }
600
695
  client.publish "topic", "message", false, 1
601
696
  expect(client).to receive(:send_packet) { |packet| expect(packet.id).to eq(2) }
@@ -657,6 +752,15 @@ describe MQTT::Client do
657
752
  end
658
753
  end
659
754
 
755
+ describe "when calling the 'clear_queue' method" do
756
+ it "should clear the waiting incoming messages" do
757
+ inject_packet(:topic => 'topic0', :payload => 'payload0', :qos => 0)
758
+ expect(client.queue_length).to eq(1)
759
+ client.clear_queue
760
+ expect(client.queue_length).to eq(0)
761
+ end
762
+ end
763
+
660
764
  describe "when calling the 'get' method" do
661
765
  before(:each) do
662
766
  client.instance_variable_set('@socket', socket)
@@ -677,6 +781,15 @@ describe MQTT::Client do
677
781
  expect(client.queue_empty?).to be_truthy
678
782
  end
679
783
 
784
+ it "should successfully receive a valid PUBLISH packet, but not return it, if omit_retained is set" do
785
+ inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1, :retain => 1)
786
+ inject_packet(:topic => 'topic1', :payload => 'payload2', :qos => 1)
787
+ topic,payload = client.get(nil, :omit_retained => true)
788
+ expect(topic).to eq('topic1')
789
+ expect(payload).to eq('payload2')
790
+ expect(client.queue_empty?).to be_truthy
791
+ end
792
+
680
793
  it "acks calling #get_packet and qos=1" do
681
794
  inject_packet(:topic => 'topic1', :payload => 'payload1', :qos => 1)
682
795
  expect(client).to receive(:send_packet).with(an_instance_of(MQTT::Packet::Puback))
@@ -712,6 +825,18 @@ describe MQTT::Client do
712
825
  break if payloads.size > 1
713
826
  end
714
827
  end
828
+
829
+ it "should ignore a PUBLISH message when it is marked as retained and omit_retained is set" do
830
+ inject_packet(:topic => 'topic0', :payload => 'payload0', :retain => 1)
831
+ inject_packet(:topic => 'topic1', :payload => 'payload1')
832
+ payloads = []
833
+ client.get(nil, :omit_retained => true) do |topic,payload|
834
+ payloads << payload
835
+ break if payloads.size > 0
836
+ end
837
+ expect(payloads.size).to eq(1)
838
+ expect(payloads).to eq(['payload1'])
839
+ end
715
840
  end
716
841
  end
717
842
 
@@ -814,7 +939,7 @@ describe MQTT::Client do
814
939
  allow(MQTT::Packet).to receive(:read).and_return MQTT::Packet::Pingresp.new
815
940
  client.instance_variable_set '@last_ping_response', Time.at(0)
816
941
  client.send :receive_packet
817
- expect(client.last_ping_response).to be_within(1).of Time.now
942
+ expect(client.last_ping_response).to be_within(1).of now
818
943
  end
819
944
  end
820
945
 
@@ -824,20 +949,20 @@ describe MQTT::Client do
824
949
  end
825
950
 
826
951
  it "should send a ping packet if one is due" do
827
- client.instance_variable_set('@last_ping_request', Time.at(0))
952
+ client.instance_variable_set('@last_ping_request', 0.0)
828
953
  client.send('keep_alive!')
829
954
  expect(socket.string).to eq("\xC0\x00")
830
955
  end
831
956
 
832
957
  it "should update the time a ping was last sent" do
833
- client.instance_variable_set('@last_ping_request', Time.at(0))
958
+ client.instance_variable_set('@last_ping_request', 0.0)
834
959
  client.send('keep_alive!')
835
- expect(client.instance_variable_get('@last_ping_request')).not_to eq(0)
960
+ expect(client.instance_variable_get('@last_ping_request')).to be_within(0.01).of(now)
836
961
  end
837
962
 
838
963
  it "should raise an exception if no ping response has been received" do
839
- client.instance_variable_set('@last_ping_request', Time.now)
840
- client.instance_variable_set('@last_ping_response', Time.at(0))
964
+ client.instance_variable_set('@last_ping_request', now)
965
+ client.instance_variable_set('@last_ping_response', 0.0)
841
966
  expect {
842
967
  client.send('keep_alive!')
843
968
  }.to raise_error(
@@ -845,6 +970,13 @@ describe MQTT::Client do
845
970
  /No Ping Response received for \d+ seconds/
846
971
  )
847
972
  end
973
+
974
+ it "should not raise an exception if no ping response received and client is disconnected" do
975
+ client.instance_variable_set('@last_ping_request', Time.now)
976
+ client.instance_variable_set('@last_ping_response', Time.at(0))
977
+ client.disconnect(false)
978
+ client.send('keep_alive!')
979
+ end
848
980
  end
849
981
 
850
982
  describe "generating a client identifier" do
@@ -890,7 +1022,7 @@ describe MQTT::Client do
890
1022
 
891
1023
  def inject_puback(packet_id)
892
1024
  packet = MQTT::Packet::Puback.new(:id => packet_id)
893
- client.instance_variable_get('@pubacks')[packet_id] = packet
1025
+ client.inject_puback packet
894
1026
  end
895
1027
 
896
1028
  end
@@ -49,6 +49,14 @@ describe MQTT::Packet do
49
49
  expect(data.encoding.to_s).to eq("ASCII-8BIT")
50
50
  end
51
51
 
52
+ it "should raise an error if too big argument for encode_short" do
53
+ expect {
54
+ data = packet.send(:encode_short, 0x10000)
55
+ }.to raise_error(
56
+ 'Value too big for short'
57
+ )
58
+ end
59
+
52
60
  it "should provide a add_string method to get a string preceeded by its length" do
53
61
  data = packet.send(:encode_string, 'quack')
54
62
  expect(data).to eq("\x00\x05quack")
@@ -24,9 +24,13 @@ describe "a client talking to a server" do
24
24
  end
25
25
 
26
26
  context "connecting and publishing a packet" do
27
- def connect_and_publish
27
+ def connect_and_publish(options = {})
28
28
  @client.connect
29
- @client.publish('test', 'foobar')
29
+
30
+ retain = options.fetch(:retain) { false }
31
+ qos = options.fetch(:qos) { 0 }
32
+
33
+ @client.publish('test', 'foobar', retain, qos)
30
34
  @client.disconnect
31
35
  @server.thread.join(1)
32
36
  end
@@ -50,6 +54,13 @@ describe "a client talking to a server" do
50
54
  connect_and_publish
51
55
  expect(@error_log.string).to be_empty
52
56
  end
57
+
58
+ context "with qos > 0" do
59
+ it "the server should have received a packet without timeout" do
60
+ connect_and_publish(:qos => 1)
61
+ expect(@server.last_publish).not_to be_nil
62
+ end
63
+ end
53
64
  end
54
65
 
55
66
  context "connecting, subscribing to a topic and getting a message" do
metadata CHANGED
@@ -1,85 +1,99 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mqtt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicholas J Humfrey
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-06-27 00:00:00.000000000 Z
11
+ date: 2023-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ! '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: 1.11.2
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ! '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: 1.11.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.9.6
33
+ version: 10.2.2
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: 0.9.6
40
+ version: 10.2.2
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: yard
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ~>
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: 0.8.0
47
+ version: 0.9.11
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ~>
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: 0.8.0
54
+ version: 0.9.11
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ~>
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: 3.0.0
61
+ version: 3.5.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ~>
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: 3.0.0
68
+ version: 3.5.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - ~>
73
+ - - ">="
74
74
  - !ruby/object:Gem::Version
75
75
  version: 0.9.2
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - ~>
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: 0.9.2
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.45'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '1.45'
83
97
  description: Pure Ruby gem that implements the MQTT protocol, a lightweight protocol
84
98
  for publish/subscribe messaging.
85
99
  email: njh@aelius.com
@@ -92,6 +106,7 @@ files:
92
106
  - README.md
93
107
  - lib/mqtt.rb
94
108
  - lib/mqtt/client.rb
109
+ - lib/mqtt/openssl_fix.rb
95
110
  - lib/mqtt/packet.rb
96
111
  - lib/mqtt/patches/string_encoding.rb
97
112
  - lib/mqtt/proxy.rb
@@ -103,28 +118,27 @@ files:
103
118
  - spec/mqtt_sn_packet_spec.rb
104
119
  - spec/mqtt_version_spec.rb
105
120
  - spec/zz_client_integration_spec.rb
106
- homepage: http://github.com/njh/ruby-mqtt
121
+ homepage: https://github.com/njh/ruby-mqtt
107
122
  licenses:
108
123
  - MIT
109
124
  metadata: {}
110
- post_install_message:
125
+ post_install_message:
111
126
  rdoc_options: []
112
127
  require_paths:
113
128
  - lib
114
129
  required_ruby_version: !ruby/object:Gem::Requirement
115
130
  requirements:
116
- - - ! '>='
131
+ - - ">="
117
132
  - !ruby/object:Gem::Version
118
133
  version: '0'
119
134
  required_rubygems_version: !ruby/object:Gem::Requirement
120
135
  requirements:
121
- - - ! '>='
136
+ - - ">="
122
137
  - !ruby/object:Gem::Version
123
138
  version: '0'
124
139
  requirements: []
125
- rubyforge_project: mqtt
126
- rubygems_version: 2.5.2
127
- signing_key:
140
+ rubygems_version: 3.3.7
141
+ signing_key:
128
142
  specification_version: 4
129
143
  summary: Implementation of the MQTT protocol
130
144
  test_files:
@@ -134,4 +148,3 @@ test_files:
134
148
  - spec/mqtt_sn_packet_spec.rb
135
149
  - spec/mqtt_version_spec.rb
136
150
  - spec/zz_client_integration_spec.rb
137
- has_rdoc: