mqtt-ccutrer 1.0.2 → 1.1.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.
- checksums.yaml +4 -4
- data/README.md +9 -2
- data/lib/mqtt/client.rb +77 -45
- data/lib/mqtt/packet.rb +113 -113
- data/lib/mqtt/proxy.rb +5 -5
- data/lib/mqtt/sn/packet.rb +80 -80
- data/lib/mqtt/version.rb +1 -1
- data/lib/mqtt-ccutrer.rb +1 -1
- data/lib/mqtt.rb +8 -8
- metadata +18 -131
- data/spec/zz_client_integration_spec.rb +0 -180
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c5586204c6a2b82ab49ccd6f6fc201612985566ac2b0a6580fc0144c83a3b06c
|
|
4
|
+
data.tar.gz: d38d819d114df28b8dbcbbd357e45fc213ba8932f46dbdcc8f368f8e69e627df
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8025eeb04ed80c513422f7ebf57b45b16298eb4c226373ea179fb9cbea87be474c2befbd48a26ba4c912bddafa6a21e7f35c6ced815a6c634a7b31355b24c15f
|
|
7
|
+
data.tar.gz: 41c23b03d0911fffee6aad9abf50df5c1ac0203a3ab44cf16354120b5b6b6687ec62b8704951be4c14f41f98db00d450e464133a983604e40f9a885ad904d55f
|
data/README.md
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
[](https://travis-ci.org/ccutrer/ruby-mqtt)
|
|
2
|
-
|
|
3
1
|
ruby-mqtt
|
|
4
2
|
=========
|
|
5
3
|
|
|
@@ -84,6 +82,15 @@ client.ca_file = path_to('root-ca.pem')
|
|
|
84
82
|
client.connect
|
|
85
83
|
~~~
|
|
86
84
|
|
|
85
|
+
The default timeout when opening a TCP Socket is 30 seconds. To specify it explicitly, use 'connect_timeout =>':
|
|
86
|
+
|
|
87
|
+
~~~ ruby
|
|
88
|
+
client = MQTT::Client.connect(
|
|
89
|
+
:host => 'myserver.example.com',
|
|
90
|
+
:connect_timeout => 15
|
|
91
|
+
)
|
|
92
|
+
~~~
|
|
93
|
+
|
|
87
94
|
The connection can either be made without the use of a block:
|
|
88
95
|
|
|
89
96
|
~~~ ruby
|
data/lib/mqtt/client.rb
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
autoload :OpenSSL,
|
|
4
|
-
autoload :SecureRandom,
|
|
5
|
-
autoload :URI,
|
|
3
|
+
autoload :OpenSSL, "openssl"
|
|
4
|
+
autoload :SecureRandom, "securerandom"
|
|
5
|
+
autoload :URI, "uri"
|
|
6
6
|
|
|
7
7
|
# Client class for talking to an MQTT server
|
|
8
8
|
module MQTT
|
|
@@ -26,6 +26,9 @@ module MQTT
|
|
|
26
26
|
# @see OpenSSL::SSL::SSLContext::METHODS
|
|
27
27
|
attr_accessor :ssl
|
|
28
28
|
|
|
29
|
+
# Set to false to skip tls hostname verification
|
|
30
|
+
attr_accessor :verify_host
|
|
31
|
+
|
|
29
32
|
# Time (in seconds) between pings to remote server (default is 15 seconds)
|
|
30
33
|
attr_accessor :keep_alive
|
|
31
34
|
|
|
@@ -38,19 +41,25 @@ module MQTT
|
|
|
38
41
|
# Number of seconds to wait for acknowledgement packets (default is 5 seconds)
|
|
39
42
|
attr_accessor :ack_timeout
|
|
40
43
|
|
|
44
|
+
# Number of seconds to connect to the server (default is 90 seconds)
|
|
45
|
+
attr_accessor :connect_timeout
|
|
46
|
+
|
|
41
47
|
# How many times to attempt re-sending packets that weren't acknowledged
|
|
42
48
|
# (default is 5) before giving up
|
|
43
49
|
attr_accessor :resend_limit
|
|
44
50
|
|
|
45
51
|
# How many attempts to re-establish a connection after it drops before
|
|
46
|
-
# giving up (default 5)
|
|
52
|
+
# giving up (default 5); nil for unlimited retries
|
|
47
53
|
attr_accessor :reconnect_limit
|
|
48
54
|
|
|
49
55
|
# How long to wait between re-connection attempts (exponential - i.e.
|
|
50
56
|
# immediately after first drop, then 5s, then 25s, then 125s, etc.
|
|
51
|
-
# when
|
|
57
|
+
# when this value defaults to 5)
|
|
52
58
|
attr_accessor :reconnect_backoff
|
|
53
59
|
|
|
60
|
+
# the longest amount of time to wait before attempting a reconnect
|
|
61
|
+
attr_accessor :reconnect_backoff_max
|
|
62
|
+
|
|
54
63
|
# Username to authenticate to the server with
|
|
55
64
|
attr_accessor :username
|
|
56
65
|
|
|
@@ -73,21 +82,24 @@ module MQTT
|
|
|
73
82
|
ATTR_DEFAULTS = {
|
|
74
83
|
host: nil,
|
|
75
84
|
port: nil,
|
|
76
|
-
version:
|
|
85
|
+
version: "3.1.1",
|
|
77
86
|
keep_alive: 15,
|
|
78
87
|
clean_session: true,
|
|
79
88
|
client_id: nil,
|
|
80
89
|
ack_timeout: 5,
|
|
90
|
+
connect_timeout: 30,
|
|
81
91
|
resend_limit: 5,
|
|
82
92
|
reconnect_limit: 5,
|
|
83
|
-
reconnect_backoff:
|
|
93
|
+
reconnect_backoff: 2,
|
|
94
|
+
reconnect_backoff_max: 30,
|
|
84
95
|
username: nil,
|
|
85
96
|
password: nil,
|
|
86
97
|
will_topic: nil,
|
|
87
98
|
will_payload: nil,
|
|
88
99
|
will_qos: 0,
|
|
89
100
|
will_retain: false,
|
|
90
|
-
ssl: false
|
|
101
|
+
ssl: false,
|
|
102
|
+
verify_host: true
|
|
91
103
|
}.freeze
|
|
92
104
|
|
|
93
105
|
# Create and connect a new MQTT Client
|
|
@@ -100,15 +112,15 @@ module MQTT
|
|
|
100
112
|
# # do stuff here
|
|
101
113
|
# end
|
|
102
114
|
#
|
|
103
|
-
def self.connect(*args, &
|
|
115
|
+
def self.connect(*args, &)
|
|
104
116
|
client = MQTT::Client.new(*args)
|
|
105
|
-
client.connect(&
|
|
117
|
+
client.connect(&)
|
|
106
118
|
client
|
|
107
119
|
end
|
|
108
120
|
|
|
109
121
|
# Generate a random client identifier
|
|
110
122
|
# (using the characters 0-9 and a-z)
|
|
111
|
-
def self.generate_client_id(prefix =
|
|
123
|
+
def self.generate_client_id(prefix = "ruby", length = 16)
|
|
112
124
|
"#{prefix}#{SecureRandom.alphanumeric(length).downcase}"
|
|
113
125
|
end
|
|
114
126
|
|
|
@@ -130,11 +142,14 @@ module MQTT
|
|
|
130
142
|
# client = MQTT::Client.new('myserver.example.com', 18830)
|
|
131
143
|
# client = MQTT::Client.new(host: 'myserver.example.com')
|
|
132
144
|
# client = MQTT::Client.new(host: 'myserver.example.com', keep_alive: 30)
|
|
145
|
+
# client = MQTT::Client.new(uri: 'mqtt://myserver.example.com', keep_alive: 30)
|
|
133
146
|
#
|
|
134
147
|
def initialize(host = nil, port = nil, **attributes)
|
|
148
|
+
host = attributes.delete(:uri) if attributes.key?(:uri)
|
|
149
|
+
|
|
135
150
|
# Set server URI from environment if present
|
|
136
|
-
if host.nil? && port.nil? && attributes.empty? && ENV[
|
|
137
|
-
attributes.merge!(parse_uri(ENV[
|
|
151
|
+
if host.nil? && port.nil? && attributes.empty? && ENV["MQTT_SERVER"]
|
|
152
|
+
attributes.merge!(parse_uri(ENV["MQTT_SERVER"]))
|
|
138
153
|
end
|
|
139
154
|
|
|
140
155
|
if host
|
|
@@ -196,13 +211,13 @@ module MQTT
|
|
|
196
211
|
# Set a path to a file containing a PEM-format client private key
|
|
197
212
|
def key_file=(*args)
|
|
198
213
|
path, passphrase = args.flatten
|
|
199
|
-
ssl_context.key = OpenSSL::PKey
|
|
214
|
+
ssl_context.key = OpenSSL::PKey.read(File.binread(path), passphrase)
|
|
200
215
|
end
|
|
201
216
|
|
|
202
217
|
# Set to a PEM-format client private key
|
|
203
218
|
def key=(*args)
|
|
204
219
|
cert, passphrase = args.flatten
|
|
205
|
-
ssl_context.key = OpenSSL::PKey
|
|
220
|
+
ssl_context.key = OpenSSL::PKey.read(cert, passphrase)
|
|
206
221
|
end
|
|
207
222
|
|
|
208
223
|
# Set a path to a file containing a PEM-format CA certificate and enable peer verification
|
|
@@ -232,13 +247,13 @@ module MQTT
|
|
|
232
247
|
end
|
|
233
248
|
|
|
234
249
|
if @client_id.nil? || @client_id.empty?
|
|
235
|
-
raise
|
|
250
|
+
raise "Must provide a client_id if clean_session is set to false" unless @clean_session
|
|
236
251
|
|
|
237
252
|
# Empty client id is not allowed for version 3.1.0
|
|
238
|
-
@client_id = MQTT::Client.generate_client_id if @version ==
|
|
253
|
+
@client_id = MQTT::Client.generate_client_id if @version == "3.1.0"
|
|
239
254
|
end
|
|
240
255
|
|
|
241
|
-
raise ArgumentError,
|
|
256
|
+
raise ArgumentError, "No MQTT server host set when attempting to connect" if @host.nil?
|
|
242
257
|
|
|
243
258
|
connect_internal
|
|
244
259
|
|
|
@@ -347,12 +362,12 @@ module MQTT
|
|
|
347
362
|
# Publish a message on a particular topic to the MQTT server.
|
|
348
363
|
def publish(topics, payload = nil, retain: false, qos: 0)
|
|
349
364
|
if topics.is_a?(Hash) && !payload.nil?
|
|
350
|
-
raise ArgumentError,
|
|
365
|
+
raise ArgumentError, "Payload cannot be passed if passing a hash for topics and payloads"
|
|
351
366
|
end
|
|
352
367
|
raise NotConnectedException unless connected?
|
|
353
368
|
|
|
354
369
|
if @batch_publish && qos != 0
|
|
355
|
-
values = @batch_publish[{ retain
|
|
370
|
+
values = @batch_publish[{ retain:, qos: }] ||= {}
|
|
356
371
|
if topics.is_a?(Hash)
|
|
357
372
|
values.merge!(topics)
|
|
358
373
|
else
|
|
@@ -366,14 +381,14 @@ module MQTT
|
|
|
366
381
|
topics = { topics => payload } unless topics.is_a?(Hash)
|
|
367
382
|
|
|
368
383
|
topics.each do |(topic, topic_payload)|
|
|
369
|
-
raise ArgumentError,
|
|
370
|
-
raise ArgumentError,
|
|
384
|
+
raise ArgumentError, "Topic name cannot be nil" if topic.nil?
|
|
385
|
+
raise ArgumentError, "Topic name cannot be empty" if topic.empty?
|
|
371
386
|
|
|
372
387
|
packet = MQTT::Packet::Publish.new(
|
|
373
388
|
id: next_packet_id,
|
|
374
|
-
qos
|
|
375
|
-
retain
|
|
376
|
-
topic
|
|
389
|
+
qos:,
|
|
390
|
+
retain:,
|
|
391
|
+
topic:,
|
|
377
392
|
payload: topic_payload
|
|
378
393
|
)
|
|
379
394
|
|
|
@@ -408,7 +423,7 @@ module MQTT
|
|
|
408
423
|
|
|
409
424
|
packet = MQTT::Packet::Subscribe.new(
|
|
410
425
|
id: next_packet_id,
|
|
411
|
-
topics:
|
|
426
|
+
topics:
|
|
412
427
|
)
|
|
413
428
|
token = register_for_ack(packet) if wait_for_ack
|
|
414
429
|
send_packet(packet)
|
|
@@ -419,10 +434,10 @@ module MQTT
|
|
|
419
434
|
def unsubscribe(*topics, wait_for_ack: false)
|
|
420
435
|
raise NotConnectedException unless connected?
|
|
421
436
|
|
|
422
|
-
topics = topics.first if topics.is_a?(Enumerable) && topics.
|
|
437
|
+
topics = topics.first if topics.is_a?(Enumerable) && topics.one?
|
|
423
438
|
|
|
424
439
|
packet = MQTT::Packet::Unsubscribe.new(
|
|
425
|
-
topics
|
|
440
|
+
topics:,
|
|
426
441
|
id: next_packet_id
|
|
427
442
|
)
|
|
428
443
|
token = register_for_ack(packet) if wait_for_ack
|
|
@@ -448,18 +463,18 @@ module MQTT
|
|
|
448
463
|
packet = @read_queue.pop
|
|
449
464
|
if packet.is_a?(Array) && packet.last >= loop_start
|
|
450
465
|
e = packet.first
|
|
451
|
-
e.set_backtrace((e.backtrace || []) + [
|
|
466
|
+
e.set_backtrace((e.backtrace || []) + ["<from MQTT worker thread>"] + caller)
|
|
452
467
|
raise e
|
|
453
468
|
end
|
|
454
469
|
next unless packet.is_a?(Packet)
|
|
455
470
|
|
|
456
471
|
unless block_given?
|
|
457
|
-
puback_packet(packet) if packet.qos
|
|
472
|
+
puback_packet(packet) if packet.qos.positive?
|
|
458
473
|
return packet
|
|
459
474
|
end
|
|
460
475
|
|
|
461
476
|
yield packet
|
|
462
|
-
puback_packet(packet) if packet.qos
|
|
477
|
+
puback_packet(packet) if packet.qos.positive?
|
|
463
478
|
end
|
|
464
479
|
end
|
|
465
480
|
|
|
@@ -481,10 +496,11 @@ module MQTT
|
|
|
481
496
|
private
|
|
482
497
|
|
|
483
498
|
PendingAck = Struct.new(:packet, :queue, :timeout_at, :send_count)
|
|
499
|
+
private_constant :PendingAck
|
|
484
500
|
|
|
485
501
|
def connect_internal
|
|
486
502
|
# Create network socket
|
|
487
|
-
tcp_socket =
|
|
503
|
+
tcp_socket = open_tcp_socket
|
|
488
504
|
|
|
489
505
|
if @ssl
|
|
490
506
|
# Set the protocol version
|
|
@@ -497,6 +513,8 @@ module MQTT
|
|
|
497
513
|
@socket.hostname = @host if @socket.respond_to?(:hostname=)
|
|
498
514
|
|
|
499
515
|
@socket.connect
|
|
516
|
+
|
|
517
|
+
@socket.post_connection_check(@host) if @verify_host
|
|
500
518
|
else
|
|
501
519
|
@socket = tcp_socket
|
|
502
520
|
end
|
|
@@ -556,13 +574,14 @@ module MQTT
|
|
|
556
574
|
|
|
557
575
|
retries = 0
|
|
558
576
|
begin
|
|
559
|
-
connect_internal unless @reconnect_limit
|
|
577
|
+
connect_internal unless @reconnect_limit.zero?
|
|
560
578
|
rescue
|
|
561
579
|
@socket&.close
|
|
562
580
|
@socket = nil
|
|
581
|
+
retries += 1
|
|
563
582
|
|
|
564
|
-
if
|
|
565
|
-
sleep @reconnect_backoff
|
|
583
|
+
if @reconnect_limit.nil? || retries < @reconnect_limit
|
|
584
|
+
sleep [@reconnect_backoff**retries, @reconnect_backoff_max].min
|
|
566
585
|
retry
|
|
567
586
|
end
|
|
568
587
|
end
|
|
@@ -582,7 +601,7 @@ module MQTT
|
|
|
582
601
|
end
|
|
583
602
|
|
|
584
603
|
begin
|
|
585
|
-
if @on_reconnect&.arity
|
|
604
|
+
if @on_reconnect&.arity&.zero?
|
|
586
605
|
@on_reconnect.call
|
|
587
606
|
else
|
|
588
607
|
@on_reconnect&.call(@connack)
|
|
@@ -624,7 +643,7 @@ module MQTT
|
|
|
624
643
|
@acks_mutex.synchronize do
|
|
625
644
|
if @acks.empty?
|
|
626
645
|
# just need to wake up the read thread to set up the timeout for this packet
|
|
627
|
-
@wake_up_pipe[1].write(
|
|
646
|
+
@wake_up_pipe[1].write("z")
|
|
628
647
|
end
|
|
629
648
|
@acks[packet.id] = PendingAck.new(packet, queue, timeout_at, 1)
|
|
630
649
|
end
|
|
@@ -683,7 +702,7 @@ module MQTT
|
|
|
683
702
|
return
|
|
684
703
|
end
|
|
685
704
|
# timed out, or simple re-send
|
|
686
|
-
@wake_up_pipe[1].write(
|
|
705
|
+
@wake_up_pipe[1].write("z") if @acks.first.first == packet.id
|
|
687
706
|
pending_ack.timeout_at = current_time + @ack_timeout
|
|
688
707
|
packet.duplicate = true
|
|
689
708
|
send_packet(packet)
|
|
@@ -711,7 +730,7 @@ module MQTT
|
|
|
711
730
|
end
|
|
712
731
|
|
|
713
732
|
def handle_keep_alives
|
|
714
|
-
return unless @keep_alive
|
|
733
|
+
return unless @keep_alive&.positive?
|
|
715
734
|
|
|
716
735
|
current_time_local = current_time
|
|
717
736
|
if current_time_local >= @last_packet_sent_at + @keep_alive && !@keep_alive_sent
|
|
@@ -756,20 +775,20 @@ module MQTT
|
|
|
756
775
|
def parse_uri(uri)
|
|
757
776
|
uri = URI.parse(uri) unless uri.is_a?(URI)
|
|
758
777
|
ssl = case uri.scheme
|
|
759
|
-
when
|
|
778
|
+
when "mqtt"
|
|
760
779
|
false
|
|
761
|
-
when
|
|
780
|
+
when "mqtts"
|
|
762
781
|
true
|
|
763
782
|
else
|
|
764
|
-
raise
|
|
783
|
+
raise "Only the mqtt:// and mqtts:// schemes are supported"
|
|
765
784
|
end
|
|
766
785
|
|
|
767
786
|
{
|
|
768
787
|
host: uri.host,
|
|
769
788
|
port: uri.port || nil,
|
|
770
|
-
username: uri.user ? URI::
|
|
771
|
-
password: uri.password ? URI::
|
|
772
|
-
ssl:
|
|
789
|
+
username: uri.user ? URI::RFC2396_PARSER.unescape(uri.user) : nil,
|
|
790
|
+
password: uri.password ? URI::RFC2396_PARSER.unescape(uri.password) : nil,
|
|
791
|
+
ssl:
|
|
773
792
|
}
|
|
774
793
|
end
|
|
775
794
|
|
|
@@ -778,5 +797,18 @@ module MQTT
|
|
|
778
797
|
@last_packet_id = 1 if @last_packet_id > 0xffff
|
|
779
798
|
@last_packet_id
|
|
780
799
|
end
|
|
800
|
+
|
|
801
|
+
def open_tcp_socket
|
|
802
|
+
return TCPSocket.new @host, @port, connect_timeout: @connect_timeout if RUBY_VERSION.to_f >= 3.0
|
|
803
|
+
|
|
804
|
+
begin
|
|
805
|
+
Timeout.timeout(@connect_timeout) do
|
|
806
|
+
return TCPSocket.new(@host, @port)
|
|
807
|
+
end
|
|
808
|
+
rescue Timeout::Error
|
|
809
|
+
raise (defined?(IO::TimeoutError) ? IO::TimeoutError : Errno::ETIMEDOUT),
|
|
810
|
+
"Connection timed out for \"#{@host}\" port #{@port}"
|
|
811
|
+
end
|
|
812
|
+
end
|
|
781
813
|
end
|
|
782
814
|
end
|