Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRY up connection handling logic #224

Merged
merged 6 commits into from
Oct 27, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 18 additions & 30 deletions lib/net/ldap/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,56 +8,43 @@ class Net::LDAP::Connection #:nodoc:

def initialize(server)
@instrumentation_service = server[:instrumentation_service]
server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil?

if server[:socket]
prepare_socket(server)
else
server[:hosts] = [[server[:host], server[:port]]] if server[:hosts].nil?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: This works even when server[:host] and/or server[:port] are nil. It raises a SocketError and matches up with the old behavior.

open_connection(server)
end

yield self if block_given?
end

def prepare_socket(server)
@conn = server[:socket]
socket = server[:socket]
encryption = server[:encryption]

if server[:encryption]
setup_encryption server[:encryption]
end
@conn = socket
setup_encryption encryption if encryption
end

def open_connection(server)
hosts = server[:hosts]
encryption = server[:encryption]

errors = []
server[:hosts].each do |host, port|
hosts.each do |host, port|
begin
return connect_to_host(host, port, server)
rescue Net::LDAP::Error
errors << $!
prepare_socket(server.merge(socket: TCPSocket.new(host, port)))
return
rescue Net::LDAP::Error, SocketError, SystemCallError,
OpenSSL::SSL::SSLError => e
# Ensure the connection is closed in the event a setup failure.
close
errors << [e, host, port]
end
end

raise errors.first if errors.size == 1
raise Net::LDAP::Error,
"Unable to connect to any given server: \n #{errors.join("\n ")}"
end

def connect_to_host(host, port, server)
begin
@conn = TCPSocket.new(host, port)
rescue SocketError
raise Net::LDAP::Error, "No such address or other socket error."
rescue Errno::ECONNREFUSED
raise Net::LDAP::ConnectionRefusedError, "Server #{host} refused connection on port #{port}."
rescue Errno::EHOSTUNREACH => error
raise Net::LDAP::Error, "Host #{host} was unreachable (#{error.message})"
rescue Errno::ETIMEDOUT
raise Net::LDAP::Error, "Connection to #{host} timed out."
end

if server[:encryption]
setup_encryption server[:encryption]
end
raise Net::LDAP::ConnectionError.new(errors)
end

module GetbyteForSSLSocket
Expand Down Expand Up @@ -156,6 +143,7 @@ def setup_encryption(args)
# have to call it, but perhaps it will come in handy someday.
#++
def close
return if @conn.nil?
@conn.close
@conn = nil
end
Expand Down
19 changes: 19 additions & 0 deletions lib/net/ldap/error.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,25 @@ def warn_deprecation_message
warn "Deprecation warning: Net::LDAP::ConnectionRefused will be deprecated. Use Errno::ECONNREFUSED instead."
end
end
class ConnectionError < Error
def self.new(errors)
error = errors.first.first
if errors.size == 1
if error.kind_of? Errno::ECONNREFUSED
return Net::LDAP::ConnectionRefusedError.new(error.message)
end

return Net::LDAP::Error.new(error.message)
end

super
end

def initialize(errors)
message = "Unable to connect to any given server: \n #{errors.map { |e, h, p| "#{e.class}: #{e.message} (#{h}:#{p})" }.join("\n ")}"
super(message)
end
end
class NoOpenSSLError < Error; end
class NoStartTLSResultError < Error; end
class NoSearchBaseError < Error; end
Expand Down
26 changes: 13 additions & 13 deletions test/test_ldap_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ def capture_stderr

def test_list_of_hosts_with_first_host_successful
hosts = [
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_return(nil)
flexmock(TCPSocket).should_receive(:new).ordered.never
Net::LDAP::Connection.new(:hosts => hosts)
end

def test_list_of_hosts_with_first_host_failure
hosts = [
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_return(nil)
flexmock(TCPSocket).should_receive(:new).ordered.never
Expand All @@ -34,15 +34,15 @@ def test_list_of_hosts_with_first_host_failure

def test_list_of_hosts_with_all_hosts_failure
hosts = [
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
['test.mocked.com', 636],
['test2.mocked.com', 636],
['test3.mocked.com', 636],
]
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[0]).once.and_raise(SocketError)
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[1]).once.and_raise(SocketError)
flexmock(TCPSocket).should_receive(:new).ordered.with(*hosts[2]).once.and_raise(SocketError)
flexmock(TCPSocket).should_receive(:new).ordered.never
assert_raise Net::LDAP::Error do
assert_raise Net::LDAP::ConnectionError do
Net::LDAP::Connection.new(:hosts => hosts)
end
end
Expand Down