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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c95449b66faaba1f857dbb64e535b2f4a8565a91a8611aa3b53d1b1a3c802f7d
4
- data.tar.gz: 053ce243061cf20b3326d53e7fda9afaa82972d4029631e6e0857efc465e7bcf
3
+ metadata.gz: c5586204c6a2b82ab49ccd6f6fc201612985566ac2b0a6580fc0144c83a3b06c
4
+ data.tar.gz: d38d819d114df28b8dbcbbd357e45fc213ba8932f46dbdcc8f368f8e69e627df
5
5
  SHA512:
6
- metadata.gz: e465141738a3352c562474041f199a260496d9d499d81899613381de8521a9fb215bdde351b608ac9630a463b3fa2e2dd4cfe22046ab179926d0c657ffebaa08
7
- data.tar.gz: 0b87b975e3c9a792f17dcc663c9aa0c0be764a6866eeeedb1a88ba9d38511f661995c412920d6798e580929317d4e633fdd744f33177fb7609c30542b4394e5d
6
+ metadata.gz: 8025eeb04ed80c513422f7ebf57b45b16298eb4c226373ea179fb9cbea87be474c2befbd48a26ba4c912bddafa6a21e7f35c6ced815a6c634a7b31355b24c15f
7
+ data.tar.gz: 41c23b03d0911fffee6aad9abf50df5c1ac0203a3ab44cf16354120b5b6b6687ec62b8704951be4c14f41f98db00d450e464133a983604e40f9a885ad904d55f
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- [![Build Status](https://travis-ci.org/njh/ruby-mqtt.svg)](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, 'openssl'
4
- autoload :SecureRandom, 'securerandom'
5
- autoload :URI, '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: '3.1.1',
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, &block)
115
+ def self.connect(*args, &)
108
116
  client = MQTT::Client.new(*args)
109
- client.connect(&block)
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 = 'ruby', length = 16)
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['MQTT_SERVER']
144
- attributes.merge!(parse_uri(ENV['MQTT_SERVER']))
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::RSA.new(File.open(path), passphrase)
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::RSA.new(cert, passphrase)
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 'Must provide a client_id if clean_session is set to false' unless @clean_session
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 == '3.1.0'
253
+ @client_id = MQTT::Client.generate_client_id if @version == "3.1.0"
246
254
  end
247
255
 
248
- raise ArgumentError, 'No MQTT server host set when attempting to connect' if @host.nil?
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, 'Payload cannot be passed if passing a hash for topics and payloads'
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: retain, qos: qos }] ||= {}
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, 'Topic name cannot be nil' if topic.nil?
377
- raise ArgumentError, 'Topic name cannot be empty' if topic.empty?
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: qos,
382
- retain: retain,
383
- topic: 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: 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.count == 1
437
+ topics = topics.first if topics.is_a?(Enumerable) && topics.one?
430
438
 
431
439
  packet = MQTT::Packet::Unsubscribe.new(
432
- topics: 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 || []) + ['<from MQTT worker thread>'] + caller)
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 > 0
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 > 0
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 = TCPSocket.new(@host, @port)
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 == 0
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 ** retries, @reconnect_backoff_max].min
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 == 0
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('z')
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('z') if @acks.first.first == packet.id
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 && @keep_alive > 0
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 'mqtt'
778
+ when "mqtt"
768
779
  false
769
- when 'mqtts'
780
+ when "mqtts"
770
781
  true
771
782
  else
772
- raise 'Only the mqtt:// and mqtts:// schemes are supported'
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::Parser.new.unescape(uri.user) : nil,
779
- password: uri.password ? URI::Parser.new.unescape(uri.password) : nil,
780
- ssl: 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