Skip to content

Commit 30099e8

Browse files
author
Stefano Tortarolo
committed
Use Socket.tcp instead of TCPSocket.new to provide a connect_timeout
This patch prevents LDAP connections to hang up for an eccessive amount of time and instead returns earlier in case of failures (e.g., packets dropped).
1 parent d93d970 commit 30099e8

File tree

4 files changed

+30
-18
lines changed

4 files changed

+30
-18
lines changed

lib/net/ldap.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -1243,7 +1243,7 @@ def new_connection
12431243
:hosts => @hosts,
12441244
:encryption => @encryption,
12451245
:instrumentation_service => @instrumentation_service
1246-
rescue Errno::ECONNREFUSED, Net::LDAP::ConnectionRefusedError => e
1246+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, Net::LDAP::ConnectionRefusedError => e
12471247
@result = {
12481248
:resultCode => 52,
12491249
:errorMessage => ResultStrings[ResultCodeUnavailable]

lib/net/ldap/connection.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
class Net::LDAP::Connection #:nodoc:
44
include Net::LDAP::Instrumentation
55

6+
# Number of seconds to wait for a socket#connect success
7+
ConnectTimeout = 1
8+
69
LdapVersion = 3
710
MaxSaslChallenges = 10
811

@@ -34,7 +37,7 @@ def open_connection(server)
3437
errors = []
3538
hosts.each do |host, port|
3639
begin
37-
prepare_socket(server.merge(socket: TCPSocket.new(host, port)))
40+
prepare_socket(server.merge(socket: Socket.tcp(host, port, connect_timeout: ConnectTimeout)))
3841
return
3942
rescue Net::LDAP::Error, SocketError, SystemCallError,
4043
OpenSSL::SSL::SSLError => e

test/test_auth_adapter.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
class TestAuthAdapter < Test::Unit::TestCase
44
def test_undefined_auth_adapter
5-
flexmock(TCPSocket).should_receive(:new).ordered.with('ldap.example.com', 379).once.and_return(nil)
5+
flexmock(Socket).should_receive(:tcp).ordered.with('ldap.example.com', 379, { connect_timeout: 1 }).once.and_return(nil)
66
conn = Net::LDAP::Connection.new(host: 'ldap.example.com', port: 379)
77
assert_raise Net::LDAP::AuthMethodUnsupportedError, "Unsupported auth method (foo)" do
88
conn.bind(method: :foo)

test/test_ldap_connection.rb

+24-15
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ def test_list_of_hosts_with_first_host_successful
1515
['test2.mocked.com', 636],
1616
['test3.mocked.com', 636],
1717
]
18-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_return(nil)
19-
flexmock(TCPSocket).should_receive(:new).ordered.never
18+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[0], { connect_timeout: 1 }).once.and_return(nil)
19+
flexmock(Socket).should_receive(:tcp).ordered.never
2020
Net::LDAP::Connection.new(:hosts => hosts)
2121
end
2222

@@ -26,9 +26,9 @@ def test_list_of_hosts_with_first_host_failure
2626
['test2.mocked.com', 636],
2727
['test3.mocked.com', 636],
2828
]
29-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
30-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_return(nil)
31-
flexmock(TCPSocket).should_receive(:new).ordered.never
29+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[0], { connect_timeout: 1 }).once.and_raise(SocketError)
30+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[1], { connect_timeout: 1 }).once.and_return(nil)
31+
flexmock(Socket).should_receive(:tcp).ordered.never
3232
Net::LDAP::Connection.new(:hosts => hosts)
3333
end
3434

@@ -38,17 +38,17 @@ def test_list_of_hosts_with_all_hosts_failure
3838
['test2.mocked.com', 636],
3939
['test3.mocked.com', 636],
4040
]
41-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
42-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_raise(SocketError)
43-
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[2]).once.and_raise(SocketError)
44-
flexmock(TCPSocket).should_receive(:new).ordered.never
41+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[0], { connect_timeout: 1 }).once.and_raise(SocketError)
42+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[1], { connect_timeout: 1 }).once.and_raise(SocketError)
43+
flexmock(Socket).should_receive(:tcp).ordered.with(*hosts[2], { connect_timeout: 1 }).once.and_raise(SocketError)
44+
flexmock(Socket).should_receive(:tcp).ordered.never
4545
assert_raise Net::LDAP::ConnectionError do
4646
Net::LDAP::Connection.new(:hosts => hosts)
4747
end
4848
end
4949

5050
def test_result_for_connection_failed_is_set
51-
flexmock(TCPSocket).should_receive(:new).and_raise(Errno::ECONNREFUSED)
51+
flexmock(Socket).should_receive(:tcp).and_raise(Errno::ECONNREFUSED)
5252

5353
ldap_client = Net::LDAP.new(host: '127.0.0.1', port: 12345)
5454

@@ -67,14 +67,14 @@ def test_unresponsive_host
6767
end
6868

6969
def test_blocked_port
70-
flexmock(TCPSocket).should_receive(:new).and_raise(SocketError)
70+
flexmock(Socket).should_receive(:tcp).and_raise(SocketError)
7171
assert_raise Net::LDAP::Error do
7272
Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
7373
end
7474
end
7575

7676
def test_connection_refused
77-
flexmock(TCPSocket).should_receive(:new).and_raise(Errno::ECONNREFUSED)
77+
flexmock(Socket).should_receive(:tcp).and_raise(Errno::ECONNREFUSED)
7878
stderr = capture_stderr do
7979
assert_raise Net::LDAP::ConnectionRefusedError do
8080
Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
@@ -83,9 +83,18 @@ def test_connection_refused
8383
assert_equal("Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead.\n", stderr)
8484
end
8585

86+
def test_connection_timedout
87+
flexmock(Socket).should_receive(:tcp).and_raise(Errno::ETIMEDOUT)
88+
stderr = capture_stderr do
89+
assert_raise Net::LDAP::Error do
90+
Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
91+
end
92+
end
93+
end
94+
8695
def test_raises_unknown_exceptions
8796
error = Class.new(StandardError)
88-
flexmock(TCPSocket).should_receive(:new).and_raise(error)
97+
flexmock(Socket).should_receive(:tcp).and_raise(error)
8998
assert_raise error do
9099
Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
91100
end
@@ -328,7 +337,7 @@ class TestLDAPConnectionErrors < Test::Unit::TestCase
328337
def setup
329338
@tcp_socket = flexmock(:connection)
330339
@tcp_socket.should_receive(:write)
331-
flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
340+
flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
332341
@connection = Net::LDAP::Connection.new(:host => 'test.mocked.com', :port => 636)
333342
end
334343

@@ -357,7 +366,7 @@ class TestLDAPConnectionInstrumentation < Test::Unit::TestCase
357366
def setup
358367
@tcp_socket = flexmock(:connection)
359368
@tcp_socket.should_receive(:write)
360-
flexmock(TCPSocket).should_receive(:new).and_return(@tcp_socket)
369+
flexmock(Socket).should_receive(:tcp).and_return(@tcp_socket)
361370

362371
@service = MockInstrumentationService.new
363372
@connection = Net::LDAP::Connection.new \

0 commit comments

Comments
 (0)