mqtt 0.6.0 → 0.7.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/NEWS.md +26 -0
- data/README.md +13 -4
- data/lib/mqtt/client.rb +36 -17
- data/lib/mqtt/packet.rb +1 -1
- data/lib/mqtt/version.rb +1 -1
- data/lib/mqtt.rb +0 -5
- data/spec/mqtt_client_spec.rb +97 -7
- data/spec/mqtt_packet_spec.rb +2 -4
- metadata +17 -4
- data/lib/mqtt/patches/string_encoding.rb +0 -32
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 1cd5b00cfda7ebb05929c0cc4319b7400b5a8e787a7e11bfeba265017cd31cc8
         | 
| 4 | 
            +
              data.tar.gz: b08388a375e26c252ebe829ea191f51c85693cb0e2b2b986f36b567a5dd710c0
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 17a6d4df69038d28566c9c4921e82a4eb363aafc0d1f811ca94ad538337a05460ea7ca8bfc5e8769d00e958443ea72c53c56bd483dc4ebbaf5f89b5d7afc918b
         | 
| 7 | 
            +
              data.tar.gz: 71a683f038ec5df05ade1f70cf536ab1174700da82eb9ef810b59d8c06af64588bd0166f7e9012f344f4af7f1326afdd9f0f462a744e340ac3f860f89bf9b0b1
         | 
    
        data/NEWS.md
    CHANGED
    
    | @@ -1,6 +1,32 @@ | |
| 1 1 | 
             
            Ruby MQTT NEWS
         | 
| 2 2 | 
             
            ==============
         | 
| 3 3 |  | 
| 4 | 
            +
            Ruby MQTT Version 0.7.0 (2025-10-29)
         | 
| 5 | 
            +
            ------------------------------------
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            * TLS hostname verification (#125)
         | 
| 8 | 
            +
            * Supporting private keys other than `RSA` (#148)
         | 
| 9 | 
            +
            * Removed support for ruby 1.8 and 1.9 (#156)
         | 
| 10 | 
            +
            * Handle instantaneous and unsolicited PUBACKs (#158)
         | 
| 11 | 
            +
            * Prevent deadlock due to system call error (#159)
         | 
| 12 | 
            +
            * Added support for timeout when opening a TCPSocket (#163)
         | 
| 13 | 
            +
            * Switch to URI::DEFAULT_PARSER.unescape() instead of CGI.unescape()
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
            Ruby MQTT Version 0.6.0 (2023-02-17)
         | 
| 17 | 
            +
            ------------------------------------
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            * Add Rubocop and performed code cleanup
         | 
| 20 | 
            +
            * Updated yard minimum version to 
         | 
| 21 | 
            +
            * Added support for clearing the queue of waiting incoming messages (#117)
         | 
| 22 | 
            +
            * Wrap packet id after 0xffff (#118)
         | 
| 23 | 
            +
            * Use a queue to wait for Puback packets rather than polling (#120)
         | 
| 24 | 
            +
            * Filter out spec files from the coverage report
         | 
| 25 | 
            +
            * Improvements `Packet.read_byte` performance (#134)
         | 
| 26 | 
            +
            * Use monotonic clock instead of realtime for keep_alive (#132)
         | 
| 27 | 
            +
            * Switch to CGI.unescape instead of the deprecated URI.unescape (#136)
         | 
| 28 | 
            +
             | 
| 29 | 
            +
             | 
| 4 30 | 
             
            Ruby MQTT Version 0.5.0 (2017-04-16)
         | 
| 5 31 | 
             
            ------------------------------------
         | 
| 6 32 |  | 
    
        data/README.md
    CHANGED
    
    | @@ -25,16 +25,16 @@ You may get the latest stable version from [Rubygems]: | |
| 25 25 |  | 
| 26 26 | 
             
                $ gem install mqtt
         | 
| 27 27 |  | 
| 28 | 
            -
            Alternatively, to use a development snapshot from GitHub using [Bundler] | 
| 29 | 
            -
             | 
| 30 | 
            -
                gem 'mqtt', :git => 'https://github.com/njh/ruby-mqtt.git'
         | 
| 28 | 
            +
            Alternatively, to use a development snapshot from GitHub using [Bundler], add this to your `Gemfile`:
         | 
| 31 29 |  | 
| 30 | 
            +
            ~~~ ruby
         | 
| 31 | 
            +
                gem 'mqtt', :github => 'njh/ruby-mqtt'
         | 
| 32 | 
            +
            ~~~
         | 
| 32 33 |  | 
| 33 34 | 
             
            Quick Start
         | 
| 34 35 | 
             
            -----------
         | 
| 35 36 |  | 
| 36 37 | 
             
            ~~~ ruby
         | 
| 37 | 
            -
            require 'rubygems'
         | 
| 38 38 | 
             
            require 'mqtt'
         | 
| 39 39 |  | 
| 40 40 | 
             
            # Publish example
         | 
| @@ -89,6 +89,15 @@ client.ca_file   = path_to('root-ca.pem') | |
| 89 89 | 
             
            client.connect()
         | 
| 90 90 | 
             
            ~~~
         | 
| 91 91 |  | 
| 92 | 
            +
            The default timeout when opening a TCP Socket is 30 seconds. To specify it explicitly, use 'connect_timeout =>':
         | 
| 93 | 
            +
             | 
| 94 | 
            +
            ~~~ ruby
         | 
| 95 | 
            +
            client = MQTT::Client.connect(
         | 
| 96 | 
            +
              :host => 'myserver.example.com',
         | 
| 97 | 
            +
              :connect_timeout => 15
         | 
| 98 | 
            +
            )
         | 
| 99 | 
            +
            ~~~
         | 
| 100 | 
            +
             | 
| 92 101 | 
             
            The connection can either be made without the use of a block:
         | 
| 93 102 |  | 
| 94 103 | 
             
            ~~~ ruby
         | 
    
        data/lib/mqtt/client.rb
    CHANGED
    
    | @@ -1,6 +1,5 @@ | |
| 1 1 | 
             
            autoload :OpenSSL, 'openssl'
         | 
| 2 2 | 
             
            autoload :URI, 'uri'
         | 
| 3 | 
            -
            autoload :CGI, 'cgi'
         | 
| 4 3 |  | 
| 5 4 | 
             
            # Client class for talking to an MQTT server
         | 
| 6 5 | 
             
            module MQTT
         | 
| @@ -24,6 +23,9 @@ module MQTT | |
| 24 23 | 
             
                # @see OpenSSL::SSL::SSLContext::METHODS
         | 
| 25 24 | 
             
                attr_accessor :ssl
         | 
| 26 25 |  | 
| 26 | 
            +
                # Set to false to skip tls hostname verification
         | 
| 27 | 
            +
                attr_accessor :verify_host
         | 
| 28 | 
            +
             | 
| 27 29 | 
             
                # Time (in seconds) between pings to remote server (default is 15 seconds)
         | 
| 28 30 | 
             
                attr_accessor :keep_alive
         | 
| 29 31 |  | 
| @@ -36,6 +38,9 @@ module MQTT | |
| 36 38 | 
             
                # Number of seconds to wait for acknowledgement packets (default is 5 seconds)
         | 
| 37 39 | 
             
                attr_accessor :ack_timeout
         | 
| 38 40 |  | 
| 41 | 
            +
                # Number of seconds to connect to the server (default is 90 seconds)
         | 
| 42 | 
            +
                attr_accessor :connect_timeout
         | 
| 43 | 
            +
             | 
| 39 44 | 
             
                # Username to authenticate to the server with
         | 
| 40 45 | 
             
                attr_accessor :username
         | 
| 41 46 |  | 
| @@ -69,13 +74,15 @@ module MQTT | |
| 69 74 | 
             
                  :clean_session => true,
         | 
| 70 75 | 
             
                  :client_id => nil,
         | 
| 71 76 | 
             
                  :ack_timeout => 5,
         | 
| 77 | 
            +
                  :connect_timeout => 30,
         | 
| 72 78 | 
             
                  :username => nil,
         | 
| 73 79 | 
             
                  :password => nil,
         | 
| 74 80 | 
             
                  :will_topic => nil,
         | 
| 75 81 | 
             
                  :will_payload => nil,
         | 
| 76 82 | 
             
                  :will_qos => 0,
         | 
| 77 83 | 
             
                  :will_retain => false,
         | 
| 78 | 
            -
                  :ssl => false
         | 
| 84 | 
            +
                  :ssl => false,
         | 
| 85 | 
            +
                  :verify_host => true
         | 
| 79 86 | 
             
                }
         | 
| 80 87 |  | 
| 81 88 | 
             
                # Create and connect a new MQTT Client
         | 
| @@ -193,13 +200,13 @@ module MQTT | |
| 193 200 | 
             
                # Set a path to a file containing a PEM-format client private key
         | 
| 194 201 | 
             
                def key_file=(*args)
         | 
| 195 202 | 
             
                  path, passphrase = args.flatten
         | 
| 196 | 
            -
                  ssl_context.key = OpenSSL::PKey | 
| 203 | 
            +
                  ssl_context.key = OpenSSL::PKey.read(File.binread(path), passphrase)
         | 
| 197 204 | 
             
                end
         | 
| 198 205 |  | 
| 199 206 | 
             
                # Set to a PEM-format client private key
         | 
| 200 207 | 
             
                def key=(*args)
         | 
| 201 208 | 
             
                  cert, passphrase = args.flatten
         | 
| 202 | 
            -
                  ssl_context.key = OpenSSL::PKey | 
| 209 | 
            +
                  ssl_context.key = OpenSSL::PKey.read(cert, passphrase)
         | 
| 203 210 | 
             
                end
         | 
| 204 211 |  | 
| 205 212 | 
             
                # Set a path to a file containing a PEM-format CA certificate and enable peer verification
         | 
| @@ -235,7 +242,7 @@ module MQTT | |
| 235 242 |  | 
| 236 243 | 
             
                  unless connected?
         | 
| 237 244 | 
             
                    # Create network socket
         | 
| 238 | 
            -
                    tcp_socket =  | 
| 245 | 
            +
                    tcp_socket = open_tcp_socket
         | 
| 239 246 |  | 
| 240 247 | 
             
                    if @ssl
         | 
| 241 248 | 
             
                      # Set the protocol version
         | 
| @@ -248,6 +255,8 @@ module MQTT | |
| 248 255 | 
             
                      @socket.hostname = @host if @socket.respond_to?(:hostname=)
         | 
| 249 256 |  | 
| 250 257 | 
             
                      @socket.connect
         | 
| 258 | 
            +
             | 
| 259 | 
            +
                      @socket.post_connection_check(@host) if @verify_host
         | 
| 251 260 | 
             
                    else
         | 
| 252 261 | 
             
                      @socket = tcp_socket
         | 
| 253 262 | 
             
                    end
         | 
| @@ -326,14 +335,11 @@ module MQTT | |
| 326 335 | 
             
                    :payload => payload
         | 
| 327 336 | 
             
                  )
         | 
| 328 337 |  | 
| 329 | 
            -
                   | 
| 330 | 
            -
                  res = send_packet(packet)
         | 
| 331 | 
            -
             | 
| 332 | 
            -
                  return if qos.zero?
         | 
| 338 | 
            +
                  queue = qos.zero? ? nil : wait_for_puback(packet.id)
         | 
| 333 339 |  | 
| 334 | 
            -
                   | 
| 340 | 
            +
                  res = send_packet(packet)
         | 
| 335 341 |  | 
| 336 | 
            -
                   | 
| 342 | 
            +
                  return unless queue
         | 
| 337 343 |  | 
| 338 344 | 
             
                  deadline = current_time + @ack_timeout
         | 
| 339 345 |  | 
| @@ -473,7 +479,7 @@ module MQTT | |
| 473 479 | 
             
                  end
         | 
| 474 480 | 
             
                  keep_alive!
         | 
| 475 481 | 
             
                # Pass exceptions up to parent thread
         | 
| 476 | 
            -
                rescue Exception => exp
         | 
| 482 | 
            +
                rescue ::Exception => exp
         | 
| 477 483 | 
             
                  unless @socket.nil?
         | 
| 478 484 | 
             
                    @socket.close
         | 
| 479 485 | 
             
                    @socket = nil
         | 
| @@ -482,9 +488,9 @@ module MQTT | |
| 482 488 | 
             
                  Thread.current[:parent].raise(exp)
         | 
| 483 489 | 
             
                end
         | 
| 484 490 |  | 
| 485 | 
            -
                def wait_for_puback(id | 
| 491 | 
            +
                def wait_for_puback(id)
         | 
| 486 492 | 
             
                  @pubacks_semaphore.synchronize do
         | 
| 487 | 
            -
                    @pubacks[id] =  | 
| 493 | 
            +
                    @pubacks[id] = Queue.new
         | 
| 488 494 | 
             
                  end
         | 
| 489 495 | 
             
                end
         | 
| 490 496 |  | 
| @@ -496,7 +502,7 @@ module MQTT | |
| 496 502 | 
             
                    @last_ping_response = current_time
         | 
| 497 503 | 
             
                  elsif packet.class == MQTT::Packet::Puback
         | 
| 498 504 | 
             
                    @pubacks_semaphore.synchronize do
         | 
| 499 | 
            -
                      @pubacks[packet.id] << packet
         | 
| 505 | 
            +
                      @pubacks[packet.id] << packet if @pubacks[packet.id]
         | 
| 500 506 | 
             
                    end
         | 
| 501 507 | 
             
                  end
         | 
| 502 508 | 
             
                  # Ignore all other packets
         | 
| @@ -585,8 +591,8 @@ module MQTT | |
| 585 591 | 
             
                  {
         | 
| 586 592 | 
             
                    :host => uri.host,
         | 
| 587 593 | 
             
                    :port => uri.port || nil,
         | 
| 588 | 
            -
                    :username => uri.user ?  | 
| 589 | 
            -
                    :password => uri.password ?  | 
| 594 | 
            +
                    :username => uri.user ? URI::DEFAULT_PARSER.unescape(uri.user) : nil,
         | 
| 595 | 
            +
                    :password => uri.password ? URI::DEFAULT_PARSER.unescape(uri.password) : nil,
         | 
| 590 596 | 
             
                    :ssl => ssl
         | 
| 591 597 | 
             
                  }
         | 
| 592 598 | 
             
                end
         | 
| @@ -597,6 +603,19 @@ module MQTT | |
| 597 603 | 
             
                  @last_packet_id
         | 
| 598 604 | 
             
                end
         | 
| 599 605 |  | 
| 606 | 
            +
                def open_tcp_socket
         | 
| 607 | 
            +
                  return TCPSocket.new @host, @port, connect_timeout: @connect_timeout if RUBY_VERSION.to_f >= 3.0
         | 
| 608 | 
            +
             | 
| 609 | 
            +
                  begin
         | 
| 610 | 
            +
                    Timeout.timeout(@connect_timeout) do
         | 
| 611 | 
            +
                      return TCPSocket.new(@host, @port)
         | 
| 612 | 
            +
                    end
         | 
| 613 | 
            +
                  rescue Timeout::Error
         | 
| 614 | 
            +
                    raise IO::TimeoutError, "Connection timed out for \"#{@host}\" port #{@port}" if defined? IO::TimeoutError
         | 
| 615 | 
            +
                    raise Errno::ETIMEDOUT, "Connection timed out for \"#{@host}\" port #{@port}"
         | 
| 616 | 
            +
                  end
         | 
| 617 | 
            +
                end
         | 
| 618 | 
            +
             | 
| 600 619 | 
             
                # ---- Deprecated attributes and methods  ---- #
         | 
| 601 620 | 
             
                public
         | 
| 602 621 |  | 
    
        data/lib/mqtt/packet.rb
    CHANGED
    
    | @@ -445,7 +445,7 @@ module MQTT | |
| 445 445 | 
             
                  def initialize(args = {})
         | 
| 446 446 | 
             
                    super(ATTR_DEFAULTS.merge(args))
         | 
| 447 447 |  | 
| 448 | 
            -
                    if  | 
| 448 | 
            +
                    if ['3.1.0', '3.1'].include?(version)
         | 
| 449 449 | 
             
                      self.protocol_name ||= 'MQIsdp'
         | 
| 450 450 | 
             
                      self.protocol_level ||= 0x03
         | 
| 451 451 | 
             
                    elsif version == '3.1.1'
         | 
    
        data/lib/mqtt/version.rb
    CHANGED
    
    
    
        data/lib/mqtt.rb
    CHANGED
    
    | @@ -7,11 +7,6 @@ require 'timeout' | |
| 7 7 |  | 
| 8 8 | 
             
            require 'mqtt/version'
         | 
| 9 9 |  | 
| 10 | 
            -
            # String encoding monkey patch for Ruby 1.8
         | 
| 11 | 
            -
            unless String.method_defined?(:force_encoding)
         | 
| 12 | 
            -
              require 'mqtt/patches/string_encoding.rb'
         | 
| 13 | 
            -
            end
         | 
| 14 | 
            -
             | 
| 15 10 | 
             
            module MQTT
         | 
| 16 11 | 
             
              # Default port number for unencrypted connections
         | 
| 17 12 | 
             
              DEFAULT_PORT = 1883
         | 
    
        data/spec/mqtt_client_spec.rb
    CHANGED
    
    | @@ -232,10 +232,46 @@ describe MQTT::Client do | |
| 232 232 | 
             
              describe "setting an encrypted client private key, w/an incorrect passphrase" do
         | 
| 233 233 | 
             
                let(:key_pass) { 'ttqm' }
         | 
| 234 234 |  | 
| 235 | 
            -
                it "should raise an  | 
| 235 | 
            +
                it "should raise an exception" do
         | 
| 236 236 | 
             
                  expect(client.ssl_context.key).to be_nil
         | 
| 237 237 | 
             
                  expect { client.key_file = [fixture_path('client.pass.key'), key_pass] }.to(
         | 
| 238 | 
            -
                    raise_error( | 
| 238 | 
            +
                    raise_error(/Could not parse PKey/))
         | 
| 239 | 
            +
                end
         | 
| 240 | 
            +
              end
         | 
| 241 | 
            +
             | 
| 242 | 
            +
              describe "setting a client private EC key file path" do
         | 
| 243 | 
            +
                it "should add a certificate to the SSL context" do
         | 
| 244 | 
            +
                  expect(client.ssl_context.key).to be_nil
         | 
| 245 | 
            +
                  client.key_file = fixture_path('ec.key')
         | 
| 246 | 
            +
                  expect(client.ssl_context.key).to be_a(OpenSSL::PKey::EC)
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
              end
         | 
| 249 | 
            +
             | 
| 250 | 
            +
              describe "setting a client private EC key directly" do
         | 
| 251 | 
            +
                it "should add a certificate to the SSL context" do
         | 
| 252 | 
            +
                  expect(client.ssl_context.key).to be_nil
         | 
| 253 | 
            +
                  client.key = File.read(fixture_path('ec.key'))
         | 
| 254 | 
            +
                  expect(client.ssl_context.key).to be_a(OpenSSL::PKey::EC)
         | 
| 255 | 
            +
                end
         | 
| 256 | 
            +
              end
         | 
| 257 | 
            +
             | 
| 258 | 
            +
              describe "setting an encrypted client private EC key, w/the correct passphrase" do
         | 
| 259 | 
            +
                let(:key_pass) { 'mqtt' }
         | 
| 260 | 
            +
             | 
| 261 | 
            +
                it "should add the decrypted certificate to the SSL context" do
         | 
| 262 | 
            +
                  expect(client.ssl_context.key).to be_nil
         | 
| 263 | 
            +
                  client.key_file = [fixture_path('ec.pass.key'), key_pass]
         | 
| 264 | 
            +
                  expect(client.ssl_context.key).to be_a(OpenSSL::PKey::EC)
         | 
| 265 | 
            +
                end
         | 
| 266 | 
            +
              end
         | 
| 267 | 
            +
             | 
| 268 | 
            +
              describe "setting an encrypted client private EC key, w/an incorrect passphrase" do
         | 
| 269 | 
            +
                let(:key_pass) { 'ttqm' }
         | 
| 270 | 
            +
             | 
| 271 | 
            +
                it "should raise an exception" do
         | 
| 272 | 
            +
                  expect(client.ssl_context.key).to be_nil
         | 
| 273 | 
            +
                  expect { client.key_file = [fixture_path('ec.pass.key'), key_pass] }.to(
         | 
| 274 | 
            +
                    raise_error(/Could not parse PKey/))
         | 
| 239 275 | 
             
                end
         | 
| 240 276 | 
             
              end
         | 
| 241 277 |  | 
| @@ -406,6 +442,7 @@ describe MQTT::Client do | |
| 406 442 | 
             
                  it "should use ssl if it enabled using the :ssl => true parameter" do
         | 
| 407 443 | 
             
                    expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
         | 
| 408 444 | 
             
                    expect(ssl_socket).to receive(:connect)
         | 
| 445 | 
            +
                    expect(ssl_socket).to receive(:post_connection_check).with('mqtt.example.com')
         | 
| 409 446 |  | 
| 410 447 | 
             
                    client = MQTT::Client.new('mqtt.example.com', :ssl => true)
         | 
| 411 448 | 
             
                    allow(client).to receive(:receive_connack)
         | 
| @@ -415,6 +452,7 @@ describe MQTT::Client do | |
| 415 452 | 
             
                  it "should use ssl if it enabled using the mqtts:// scheme" do
         | 
| 416 453 | 
             
                    expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
         | 
| 417 454 | 
             
                    expect(ssl_socket).to receive(:connect)
         | 
| 455 | 
            +
                    expect(ssl_socket).to receive(:post_connection_check).with('mqtt.example.com')
         | 
| 418 456 |  | 
| 419 457 | 
             
                    client = MQTT::Client.new('mqtts://mqtt.example.com')
         | 
| 420 458 | 
             
                    allow(client).to receive(:receive_connack)
         | 
| @@ -424,6 +462,7 @@ describe MQTT::Client do | |
| 424 462 | 
             
                  it "should use set the SSL version, if the :ssl parameter is a symbol" do
         | 
| 425 463 | 
             
                    expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
         | 
| 426 464 | 
             
                    expect(ssl_socket).to receive(:connect)
         | 
| 465 | 
            +
                    expect(ssl_socket).to receive(:post_connection_check).with('mqtt.example.com')
         | 
| 427 466 |  | 
| 428 467 | 
             
                    client = MQTT::Client.new('mqtt.example.com', :ssl => :TLSv1)
         | 
| 429 468 | 
             
                    expect(client.ssl_context).to receive('ssl_version=').with(:TLSv1)
         | 
| @@ -434,11 +473,21 @@ describe MQTT::Client do | |
| 434 473 | 
             
                  it "should use set hostname on the SSL socket for SNI" do
         | 
| 435 474 | 
             
                    expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
         | 
| 436 475 | 
             
                    expect(ssl_socket).to receive(:hostname=).with('mqtt.example.com')
         | 
| 476 | 
            +
                    expect(ssl_socket).to receive(:post_connection_check).with('mqtt.example.com')
         | 
| 437 477 |  | 
| 438 478 | 
             
                    client = MQTT::Client.new('mqtts://mqtt.example.com')
         | 
| 439 479 | 
             
                    allow(client).to receive(:receive_connack)
         | 
| 440 480 | 
             
                    client.connect
         | 
| 441 481 | 
             
                  end
         | 
| 482 | 
            +
             | 
| 483 | 
            +
                  it "should skip host verification" do
         | 
| 484 | 
            +
                    expect(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(ssl_socket)
         | 
| 485 | 
            +
                    expect(ssl_socket).to receive(:connect)
         | 
| 486 | 
            +
             | 
| 487 | 
            +
                    client = MQTT::Client.new('mqtt.example.com', :ssl => true, :verify_host => false)
         | 
| 488 | 
            +
                    allow(client).to receive(:receive_connack)
         | 
| 489 | 
            +
                    client.connect
         | 
| 490 | 
            +
                  end
         | 
| 442 491 | 
             
                end
         | 
| 443 492 |  | 
| 444 493 | 
             
                context "with a last will and testament set" do
         | 
| @@ -592,11 +641,11 @@ describe MQTT::Client do | |
| 592 641 | 
             
                    @injected_pubacks[packet.id] = packet
         | 
| 593 642 | 
             
                  end
         | 
| 594 643 |  | 
| 595 | 
            -
                  def wait_for_puback(id | 
| 644 | 
            +
                  def wait_for_puback(id)
         | 
| 596 645 | 
             
                    packet = @injected_pubacks.fetch(id) {
         | 
| 597 646 | 
             
                      return super
         | 
| 598 647 | 
             
                    }
         | 
| 599 | 
            -
                     | 
| 648 | 
            +
                    Queue.new << packet
         | 
| 600 649 | 
             
                  end
         | 
| 601 650 | 
             
                end
         | 
| 602 651 |  | 
| @@ -606,6 +655,19 @@ describe MQTT::Client do | |
| 606 655 | 
             
                  client.instance_variable_set('@socket', socket)
         | 
| 607 656 | 
             
                end
         | 
| 608 657 |  | 
| 658 | 
            +
                it "should respect connect_timeout" do
         | 
| 659 | 
            +
                  client = MQTT::Client.new(:host => '198.51.100.1', :connect_timeout => 0.1)
         | 
| 660 | 
            +
                  expect {
         | 
| 661 | 
            +
                    client.connect
         | 
| 662 | 
            +
                  }.to raise_error( 
         | 
| 663 | 
            +
                    if defined? IO::TimeoutError
         | 
| 664 | 
            +
                      IO::TimeoutError
         | 
| 665 | 
            +
                    else
         | 
| 666 | 
            +
                      Errno::ETIMEDOUT
         | 
| 667 | 
            +
                    end
         | 
| 668 | 
            +
                  )
         | 
| 669 | 
            +
                end
         | 
| 670 | 
            +
             | 
| 609 671 | 
             
                it "should respect timeouts" do
         | 
| 610 672 | 
             
                  require "socket"
         | 
| 611 673 | 
             
                  rd, wr = UNIXSocket.pair
         | 
| @@ -696,6 +758,20 @@ describe MQTT::Client do | |
| 696 758 | 
             
                  expect(client).to receive(:send_packet) { |packet| expect(packet.id).to eq(2) }
         | 
| 697 759 | 
             
                  client.publish "topic", "message", false, 1
         | 
| 698 760 | 
             
                end
         | 
| 761 | 
            +
             | 
| 762 | 
            +
                it "does not crash when receiving a PUBACK for a packet it never sent" do
         | 
| 763 | 
            +
                  expect { client.send(:handle_packet, MQTT::Packet::Puback.new(:id => 666)) }.to_not raise_error
         | 
| 764 | 
            +
                end
         | 
| 765 | 
            +
             | 
| 766 | 
            +
                it "does not crash with QoS 1 when the broker sends the PUBACK instantly" do
         | 
| 767 | 
            +
                  allow(client).to receive(:send_packet).and_wrap_original do |send_packet, packet, *args, &block|
         | 
| 768 | 
            +
                    send_packet.call(packet, *args, &block).tap do
         | 
| 769 | 
            +
                      client.send(:handle_packet, MQTT::Packet::Puback.new(:id => packet.id))
         | 
| 770 | 
            +
                    end
         | 
| 771 | 
            +
                  end
         | 
| 772 | 
            +
             | 
| 773 | 
            +
                  expect { client.publish("topic", "message", false, 1) }.to_not raise_error
         | 
| 774 | 
            +
                end
         | 
| 699 775 | 
             
              end
         | 
| 700 776 |  | 
| 701 777 | 
             
              describe "when calling the 'subscribe' method" do
         | 
| @@ -923,15 +999,29 @@ describe MQTT::Client do | |
| 923 999 | 
             
                  expect(@read_queue.size).to eq(0)
         | 
| 924 1000 | 
             
                end
         | 
| 925 1001 |  | 
| 926 | 
            -
                it "should close the socket if there is an exception" do
         | 
| 1002 | 
            +
                it "should close the socket if there is an MQTT exception" do
         | 
| 927 1003 | 
             
                  expect(socket).to receive(:close).once
         | 
| 928 1004 | 
             
                  allow(MQTT::Packet).to receive(:read).and_raise(MQTT::Exception)
         | 
| 929 1005 | 
             
                  client.send(:receive_packet)
         | 
| 930 1006 | 
             
                end
         | 
| 931 1007 |  | 
| 1008 | 
            +
                it "should close the socket if there is a system call error" do
         | 
| 1009 | 
            +
                  expect(socket).to receive(:close).once
         | 
| 1010 | 
            +
                  allow(MQTT::Packet).to receive(:read).and_raise(Errno::ECONNRESET)
         | 
| 1011 | 
            +
                  client.send(:receive_packet)
         | 
| 1012 | 
            +
                end
         | 
| 1013 | 
            +
             | 
| 932 1014 | 
             
                it "should pass exceptions up to parent thread" do
         | 
| 933 | 
            -
                   | 
| 934 | 
            -
                   | 
| 1015 | 
            +
                  e = MQTT::Exception.new
         | 
| 1016 | 
            +
                  expect(@parent_thread).to receive(:raise).with(e).once
         | 
| 1017 | 
            +
                  allow(MQTT::Packet).to receive(:read).and_raise(e)
         | 
| 1018 | 
            +
                  client.send(:receive_packet)
         | 
| 1019 | 
            +
                end
         | 
| 1020 | 
            +
             | 
| 1021 | 
            +
                it "should pass a system call error up to parent thread" do
         | 
| 1022 | 
            +
                  e = Errno::ECONNRESET.new
         | 
| 1023 | 
            +
                  expect(@parent_thread).to receive(:raise).with(e).once
         | 
| 1024 | 
            +
                  allow(MQTT::Packet).to receive(:read).and_raise(e)
         | 
| 935 1025 | 
             
                  client.send(:receive_packet)
         | 
| 936 1026 | 
             
                end
         | 
| 937 1027 |  | 
    
        data/spec/mqtt_packet_spec.rb
    CHANGED
    
    | @@ -382,8 +382,7 @@ describe MQTT::Packet::Publish do | |
| 382 382 | 
             
                  expect(packet.topic.bytesize).to eq(8)
         | 
| 383 383 | 
             
                end
         | 
| 384 384 |  | 
| 385 | 
            -
                it "should have the correct topic string length" | 
| 386 | 
            -
                  # Ruby 1.8 doesn't support UTF-8 properly
         | 
| 385 | 
            +
                it "should have the correct topic string length" do
         | 
| 387 386 | 
             
                  expect(packet.topic.length).to eq(6)
         | 
| 388 387 | 
             
                end
         | 
| 389 388 |  | 
| @@ -391,8 +390,7 @@ describe MQTT::Packet::Publish do | |
| 391 390 | 
             
                  expect(packet.payload.bytesize).to eq(12)
         | 
| 392 391 | 
             
                end
         | 
| 393 392 |  | 
| 394 | 
            -
                it "should have the correct payload string length" | 
| 395 | 
            -
                  # Ruby 1.8 doesn't support UTF-8 properly
         | 
| 393 | 
            +
                it "should have the correct payload string length" do
         | 
| 396 394 | 
             
                  expect(packet.payload.length).to eq(10)
         | 
| 397 395 | 
             
                end
         | 
| 398 396 |  | 
    
        metadata
    CHANGED
    
    | @@ -1,15 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mqtt
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.7.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Nicholas J Humfrey
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 11 | 
            +
            date: 2025-10-29 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: logger
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - ">="
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - ">="
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 13 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 28 | 
             
              name: bundler
         | 
| 15 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -108,7 +122,6 @@ files: | |
| 108 122 | 
             
            - lib/mqtt/client.rb
         | 
| 109 123 | 
             
            - lib/mqtt/openssl_fix.rb
         | 
| 110 124 | 
             
            - lib/mqtt/packet.rb
         | 
| 111 | 
            -
            - lib/mqtt/patches/string_encoding.rb
         | 
| 112 125 | 
             
            - lib/mqtt/proxy.rb
         | 
| 113 126 | 
             
            - lib/mqtt/sn/packet.rb
         | 
| 114 127 | 
             
            - lib/mqtt/version.rb
         | 
| @@ -137,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 137 150 | 
             
                - !ruby/object:Gem::Version
         | 
| 138 151 | 
             
                  version: '0'
         | 
| 139 152 | 
             
            requirements: []
         | 
| 140 | 
            -
            rubygems_version: 3.3 | 
| 153 | 
            +
            rubygems_version: 3.5.3
         | 
| 141 154 | 
             
            signing_key:
         | 
| 142 155 | 
             
            specification_version: 4
         | 
| 143 156 | 
             
            summary: Implementation of the MQTT protocol
         | 
| @@ -1,32 +0,0 @@ | |
| 1 | 
            -
            # Monkey patch to add stubbed string encoding functions to Ruby 1.8
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            class String
         | 
| 4 | 
            -
              def force_encoding(encoding)
         | 
| 5 | 
            -
                @encoding = encoding
         | 
| 6 | 
            -
                self
         | 
| 7 | 
            -
              end
         | 
| 8 | 
            -
             | 
| 9 | 
            -
              def encoding
         | 
| 10 | 
            -
                @encoding ||= Encoding::ASCII_8BIT
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              def encode(encoding)
         | 
| 14 | 
            -
                new = dup
         | 
| 15 | 
            -
                new.force_encoding(encoding)
         | 
| 16 | 
            -
              end
         | 
| 17 | 
            -
            end
         | 
| 18 | 
            -
             | 
| 19 | 
            -
            class Encoding
         | 
| 20 | 
            -
              attr_reader :name
         | 
| 21 | 
            -
             | 
| 22 | 
            -
              def initialize(name)
         | 
| 23 | 
            -
                @name = name
         | 
| 24 | 
            -
              end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
              def to_s
         | 
| 27 | 
            -
                @name
         | 
| 28 | 
            -
              end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
              UTF_8 = Encoding.new('UTF-8')
         | 
| 31 | 
            -
              ASCII_8BIT = Encoding.new('ASCII-8BIT')
         | 
| 32 | 
            -
            end
         |