mqtt-ccutrer 1.0.3 → 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 +65 -41
- 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,6 +41,9 @@ 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
|
|
@@ -76,11 +82,12 @@ module MQTT
|
|
|
76
82
|
ATTR_DEFAULTS = {
|
|
77
83
|
host: nil,
|
|
78
84
|
port: nil,
|
|
79
|
-
version:
|
|
85
|
+
version: "3.1.1",
|
|
80
86
|
keep_alive: 15,
|
|
81
87
|
clean_session: true,
|
|
82
88
|
client_id: nil,
|
|
83
89
|
ack_timeout: 5,
|
|
90
|
+
connect_timeout: 30,
|
|
84
91
|
resend_limit: 5,
|
|
85
92
|
reconnect_limit: 5,
|
|
86
93
|
reconnect_backoff: 2,
|
|
@@ -91,7 +98,8 @@ module MQTT
|
|
|
91
98
|
will_payload: nil,
|
|
92
99
|
will_qos: 0,
|
|
93
100
|
will_retain: false,
|
|
94
|
-
ssl: false
|
|
101
|
+
ssl: false,
|
|
102
|
+
verify_host: true
|
|
95
103
|
}.freeze
|
|
96
104
|
|
|
97
105
|
# Create and connect a new MQTT Client
|
|
@@ -104,15 +112,15 @@ module MQTT
|
|
|
104
112
|
# # do stuff here
|
|
105
113
|
# end
|
|
106
114
|
#
|
|
107
|
-
def self.connect(*args, &
|
|
115
|
+
def self.connect(*args, &)
|
|
108
116
|
client = MQTT::Client.new(*args)
|
|
109
|
-
client.connect(&
|
|
117
|
+
client.connect(&)
|
|
110
118
|
client
|
|
111
119
|
end
|
|
112
120
|
|
|
113
121
|
# Generate a random client identifier
|
|
114
122
|
# (using the characters 0-9 and a-z)
|
|
115
|
-
def self.generate_client_id(prefix =
|
|
123
|
+
def self.generate_client_id(prefix = "ruby", length = 16)
|
|
116
124
|
"#{prefix}#{SecureRandom.alphanumeric(length).downcase}"
|
|
117
125
|
end
|
|
118
126
|
|
|
@@ -140,8 +148,8 @@ module MQTT
|
|
|
140
148
|
host = attributes.delete(:uri) if attributes.key?(:uri)
|
|
141
149
|
|
|
142
150
|
# Set server URI from environment if present
|
|
143
|
-
if host.nil? && port.nil? && attributes.empty? && ENV[
|
|
144
|
-
attributes.merge!(parse_uri(ENV[
|
|
151
|
+
if host.nil? && port.nil? && attributes.empty? && ENV["MQTT_SERVER"]
|
|
152
|
+
attributes.merge!(parse_uri(ENV["MQTT_SERVER"]))
|
|
145
153
|
end
|
|
146
154
|
|
|
147
155
|
if host
|
|
@@ -203,13 +211,13 @@ module MQTT
|
|
|
203
211
|
# Set a path to a file containing a PEM-format client private key
|
|
204
212
|
def key_file=(*args)
|
|
205
213
|
path, passphrase = args.flatten
|
|
206
|
-
ssl_context.key = OpenSSL::PKey
|
|
214
|
+
ssl_context.key = OpenSSL::PKey.read(File.binread(path), passphrase)
|
|
207
215
|
end
|
|
208
216
|
|
|
209
217
|
# Set to a PEM-format client private key
|
|
210
218
|
def key=(*args)
|
|
211
219
|
cert, passphrase = args.flatten
|
|
212
|
-
ssl_context.key = OpenSSL::PKey
|
|
220
|
+
ssl_context.key = OpenSSL::PKey.read(cert, passphrase)
|
|
213
221
|
end
|
|
214
222
|
|
|
215
223
|
# Set a path to a file containing a PEM-format CA certificate and enable peer verification
|
|
@@ -239,13 +247,13 @@ module MQTT
|
|
|
239
247
|
end
|
|
240
248
|
|
|
241
249
|
if @client_id.nil? || @client_id.empty?
|
|
242
|
-
raise
|
|
250
|
+
raise "Must provide a client_id if clean_session is set to false" unless @clean_session
|
|
243
251
|
|
|
244
252
|
# Empty client id is not allowed for version 3.1.0
|
|
245
|
-
@client_id = MQTT::Client.generate_client_id if @version ==
|
|
253
|
+
@client_id = MQTT::Client.generate_client_id if @version == "3.1.0"
|
|
246
254
|
end
|
|
247
255
|
|
|
248
|
-
raise ArgumentError,
|
|
256
|
+
raise ArgumentError, "No MQTT server host set when attempting to connect" if @host.nil?
|
|
249
257
|
|
|
250
258
|
connect_internal
|
|
251
259
|
|
|
@@ -354,12 +362,12 @@ module MQTT
|
|
|
354
362
|
# Publish a message on a particular topic to the MQTT server.
|
|
355
363
|
def publish(topics, payload = nil, retain: false, qos: 0)
|
|
356
364
|
if topics.is_a?(Hash) && !payload.nil?
|
|
357
|
-
raise ArgumentError,
|
|
365
|
+
raise ArgumentError, "Payload cannot be passed if passing a hash for topics and payloads"
|
|
358
366
|
end
|
|
359
367
|
raise NotConnectedException unless connected?
|
|
360
368
|
|
|
361
369
|
if @batch_publish && qos != 0
|
|
362
|
-
values = @batch_publish[{ retain
|
|
370
|
+
values = @batch_publish[{ retain:, qos: }] ||= {}
|
|
363
371
|
if topics.is_a?(Hash)
|
|
364
372
|
values.merge!(topics)
|
|
365
373
|
else
|
|
@@ -373,14 +381,14 @@ module MQTT
|
|
|
373
381
|
topics = { topics => payload } unless topics.is_a?(Hash)
|
|
374
382
|
|
|
375
383
|
topics.each do |(topic, topic_payload)|
|
|
376
|
-
raise ArgumentError,
|
|
377
|
-
raise ArgumentError,
|
|
384
|
+
raise ArgumentError, "Topic name cannot be nil" if topic.nil?
|
|
385
|
+
raise ArgumentError, "Topic name cannot be empty" if topic.empty?
|
|
378
386
|
|
|
379
387
|
packet = MQTT::Packet::Publish.new(
|
|
380
388
|
id: next_packet_id,
|
|
381
|
-
qos
|
|
382
|
-
retain
|
|
383
|
-
topic
|
|
389
|
+
qos:,
|
|
390
|
+
retain:,
|
|
391
|
+
topic:,
|
|
384
392
|
payload: topic_payload
|
|
385
393
|
)
|
|
386
394
|
|
|
@@ -415,7 +423,7 @@ module MQTT
|
|
|
415
423
|
|
|
416
424
|
packet = MQTT::Packet::Subscribe.new(
|
|
417
425
|
id: next_packet_id,
|
|
418
|
-
topics:
|
|
426
|
+
topics:
|
|
419
427
|
)
|
|
420
428
|
token = register_for_ack(packet) if wait_for_ack
|
|
421
429
|
send_packet(packet)
|
|
@@ -426,10 +434,10 @@ module MQTT
|
|
|
426
434
|
def unsubscribe(*topics, wait_for_ack: false)
|
|
427
435
|
raise NotConnectedException unless connected?
|
|
428
436
|
|
|
429
|
-
topics = topics.first if topics.is_a?(Enumerable) && topics.
|
|
437
|
+
topics = topics.first if topics.is_a?(Enumerable) && topics.one?
|
|
430
438
|
|
|
431
439
|
packet = MQTT::Packet::Unsubscribe.new(
|
|
432
|
-
topics
|
|
440
|
+
topics:,
|
|
433
441
|
id: next_packet_id
|
|
434
442
|
)
|
|
435
443
|
token = register_for_ack(packet) if wait_for_ack
|
|
@@ -455,18 +463,18 @@ module MQTT
|
|
|
455
463
|
packet = @read_queue.pop
|
|
456
464
|
if packet.is_a?(Array) && packet.last >= loop_start
|
|
457
465
|
e = packet.first
|
|
458
|
-
e.set_backtrace((e.backtrace || []) + [
|
|
466
|
+
e.set_backtrace((e.backtrace || []) + ["<from MQTT worker thread>"] + caller)
|
|
459
467
|
raise e
|
|
460
468
|
end
|
|
461
469
|
next unless packet.is_a?(Packet)
|
|
462
470
|
|
|
463
471
|
unless block_given?
|
|
464
|
-
puback_packet(packet) if packet.qos
|
|
472
|
+
puback_packet(packet) if packet.qos.positive?
|
|
465
473
|
return packet
|
|
466
474
|
end
|
|
467
475
|
|
|
468
476
|
yield packet
|
|
469
|
-
puback_packet(packet) if packet.qos
|
|
477
|
+
puback_packet(packet) if packet.qos.positive?
|
|
470
478
|
end
|
|
471
479
|
end
|
|
472
480
|
|
|
@@ -488,10 +496,11 @@ module MQTT
|
|
|
488
496
|
private
|
|
489
497
|
|
|
490
498
|
PendingAck = Struct.new(:packet, :queue, :timeout_at, :send_count)
|
|
499
|
+
private_constant :PendingAck
|
|
491
500
|
|
|
492
501
|
def connect_internal
|
|
493
502
|
# Create network socket
|
|
494
|
-
tcp_socket =
|
|
503
|
+
tcp_socket = open_tcp_socket
|
|
495
504
|
|
|
496
505
|
if @ssl
|
|
497
506
|
# Set the protocol version
|
|
@@ -504,6 +513,8 @@ module MQTT
|
|
|
504
513
|
@socket.hostname = @host if @socket.respond_to?(:hostname=)
|
|
505
514
|
|
|
506
515
|
@socket.connect
|
|
516
|
+
|
|
517
|
+
@socket.post_connection_check(@host) if @verify_host
|
|
507
518
|
else
|
|
508
519
|
@socket = tcp_socket
|
|
509
520
|
end
|
|
@@ -563,14 +574,14 @@ module MQTT
|
|
|
563
574
|
|
|
564
575
|
retries = 0
|
|
565
576
|
begin
|
|
566
|
-
connect_internal unless @reconnect_limit
|
|
577
|
+
connect_internal unless @reconnect_limit.zero?
|
|
567
578
|
rescue
|
|
568
579
|
@socket&.close
|
|
569
580
|
@socket = nil
|
|
570
581
|
retries += 1
|
|
571
582
|
|
|
572
583
|
if @reconnect_limit.nil? || retries < @reconnect_limit
|
|
573
|
-
sleep [@reconnect_backoff
|
|
584
|
+
sleep [@reconnect_backoff**retries, @reconnect_backoff_max].min
|
|
574
585
|
retry
|
|
575
586
|
end
|
|
576
587
|
end
|
|
@@ -590,7 +601,7 @@ module MQTT
|
|
|
590
601
|
end
|
|
591
602
|
|
|
592
603
|
begin
|
|
593
|
-
if @on_reconnect&.arity
|
|
604
|
+
if @on_reconnect&.arity&.zero?
|
|
594
605
|
@on_reconnect.call
|
|
595
606
|
else
|
|
596
607
|
@on_reconnect&.call(@connack)
|
|
@@ -632,7 +643,7 @@ module MQTT
|
|
|
632
643
|
@acks_mutex.synchronize do
|
|
633
644
|
if @acks.empty?
|
|
634
645
|
# just need to wake up the read thread to set up the timeout for this packet
|
|
635
|
-
@wake_up_pipe[1].write(
|
|
646
|
+
@wake_up_pipe[1].write("z")
|
|
636
647
|
end
|
|
637
648
|
@acks[packet.id] = PendingAck.new(packet, queue, timeout_at, 1)
|
|
638
649
|
end
|
|
@@ -691,7 +702,7 @@ module MQTT
|
|
|
691
702
|
return
|
|
692
703
|
end
|
|
693
704
|
# timed out, or simple re-send
|
|
694
|
-
@wake_up_pipe[1].write(
|
|
705
|
+
@wake_up_pipe[1].write("z") if @acks.first.first == packet.id
|
|
695
706
|
pending_ack.timeout_at = current_time + @ack_timeout
|
|
696
707
|
packet.duplicate = true
|
|
697
708
|
send_packet(packet)
|
|
@@ -719,7 +730,7 @@ module MQTT
|
|
|
719
730
|
end
|
|
720
731
|
|
|
721
732
|
def handle_keep_alives
|
|
722
|
-
return unless @keep_alive
|
|
733
|
+
return unless @keep_alive&.positive?
|
|
723
734
|
|
|
724
735
|
current_time_local = current_time
|
|
725
736
|
if current_time_local >= @last_packet_sent_at + @keep_alive && !@keep_alive_sent
|
|
@@ -764,20 +775,20 @@ module MQTT
|
|
|
764
775
|
def parse_uri(uri)
|
|
765
776
|
uri = URI.parse(uri) unless uri.is_a?(URI)
|
|
766
777
|
ssl = case uri.scheme
|
|
767
|
-
when
|
|
778
|
+
when "mqtt"
|
|
768
779
|
false
|
|
769
|
-
when
|
|
780
|
+
when "mqtts"
|
|
770
781
|
true
|
|
771
782
|
else
|
|
772
|
-
raise
|
|
783
|
+
raise "Only the mqtt:// and mqtts:// schemes are supported"
|
|
773
784
|
end
|
|
774
785
|
|
|
775
786
|
{
|
|
776
787
|
host: uri.host,
|
|
777
788
|
port: uri.port || nil,
|
|
778
|
-
username: uri.user ? URI::
|
|
779
|
-
password: uri.password ? URI::
|
|
780
|
-
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:
|
|
781
792
|
}
|
|
782
793
|
end
|
|
783
794
|
|
|
@@ -786,5 +797,18 @@ module MQTT
|
|
|
786
797
|
@last_packet_id = 1 if @last_packet_id > 0xffff
|
|
787
798
|
@last_packet_id
|
|
788
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
|
|
789
813
|
end
|
|
790
814
|
end
|