diff --git a/README.md b/README.md index 3c2eafb..108c6f6 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,15 @@ client.ca_file = path_to('root-ca.pem') client.connect() ~~~ +The default timeout when opening a TCP Socket is 30 seconds. To specify it explicitly, use 'connect_timeout =>': + +~~~ ruby +client = MQTT::Client.connect( + :host => 'myserver.example.com', + :connect_timeout => 15 +) +~~~ + The connection can either be made without the use of a block: ~~~ ruby diff --git a/lib/mqtt/client.rb b/lib/mqtt/client.rb index f5f3b9f..4ceeffc 100644 --- a/lib/mqtt/client.rb +++ b/lib/mqtt/client.rb @@ -39,6 +39,9 @@ class Client # Number of seconds to wait for acknowledgement packets (default is 5 seconds) attr_accessor :ack_timeout + # Number of seconds to connect to the server (default is 90 seconds) + attr_accessor :connect_timeout + # Username to authenticate to the server with attr_accessor :username @@ -72,6 +75,7 @@ class Client :clean_session => true, :client_id => nil, :ack_timeout => 5, + :connect_timeout => 30, :username => nil, :password => nil, :will_topic => nil, @@ -239,7 +243,7 @@ def connect(clientid = nil) unless connected? # Create network socket - tcp_socket = TCPSocket.new(@host, @port) + tcp_socket = open_tcp_socket if @ssl # Set the protocol version @@ -600,6 +604,19 @@ def next_packet_id @last_packet_id end + def open_tcp_socket + return TCPSocket.new @host, @port, connect_timeout: @connect_timeout if RUBY_VERSION.to_f >= 3.0 + + begin + Timeout.timeout(@connect_timeout) do + return TCPSocket.new(@host, @port) + end + rescue Timeout::Error + raise IO::TimeoutError, "Connection timed out for \"#{@host}\" port #{@port}" if defined? IO::TimeoutError + raise Errno::ETIMEDOUT, "Connection timed out for \"#{@host}\" port #{@port}" + end + end + # ---- Deprecated attributes and methods ---- # public diff --git a/spec/mqtt_client_spec.rb b/spec/mqtt_client_spec.rb index 28a701f..c50aaff 100644 --- a/spec/mqtt_client_spec.rb +++ b/spec/mqtt_client_spec.rb @@ -655,6 +655,19 @@ def wait_for_puback(id) client.instance_variable_set('@socket', socket) end + it "should respect connect_timeout" do + client = MQTT::Client.new(:host => '198.51.100.1', :connect_timeout => 0.1) + expect { + client.connect + }.to raise_error( + if defined? IO::TimeoutError + IO::TimeoutError + else + Errno::ETIMEDOUT + end + ) + end + it "should respect timeouts" do require "socket" rd, wr = UNIXSocket.pair