Browse files

Refactor socket creation.

This is the first part of a refactoring after I saw a better pattern for
handling socket creation which didn't require us to do the DNS lookups
twice.

The first DNS lookup is done in the address class, and then split out,
so it made more sense to generate the sockets from the address itself,
which cleaned up all the ugly handle_connect code that was still around.
  • Loading branch information...
1 parent 38d0974 commit 4507e2d30cc650d698dad7b2e92b8ccc87748f42 @durran durran committed Feb 26, 2014
View
7 lib/mongo/pool.rb
@@ -165,12 +165,7 @@ def create_pool(server)
size: server.options[:pool_size],
timeout: server.options[:pool_timeout]
) do
- Connection.new(
- server.address.ip,
- server.address.port,
- server.options[:timeout],
- server.options
- )
+ Connection.new(server.address, server.options[:timeout], server.options)
end
end
View
19 lib/mongo/pool/connection.rb
@@ -25,11 +25,8 @@ class Connection
# @since 3.0.0
TIMEOUT = 5
- # @return [ String ] host The host to connect to.
- attr_reader :host
-
- # @return [ Integer ] port The port to connect on.
- attr_reader :port
+ # @return [ Mongo::Server::Address ] address The address to connect to.
+ attr_reader :address
# @return [ Float ] timeout The connection timeout.
attr_reader :timeout
@@ -46,7 +43,7 @@ class Connection
#
# @since 3.0.0
def connect!
- @socket = Socket.create(host, port, timeout, ssl_opts) unless socket
+ @socket = address.socket(timeout, ssl_opts) unless socket
socket.connect! and true
end
@@ -72,17 +69,15 @@ def disconnect!
# Initialize a new socket connection from the client to the server.
#
# @example Create the connection.
- # Connection.new('127.0.0.1', 27017, 10)
+ # Connection.new(address, 10)
#
- # @param [ String ] host The host to connect to.
- # @param [ Integer ] port The port to connect to.
+ # @param [ Mongo::Server::Address ] address The address to connect to.
# @param [ Float ] timeout The connection timeout.
# @param [ Hash ] options The connection options.
#
# @since 3.0.0
- def initialize(host, port, timeout = nil, options = {})
- @host = host
- @port = port
+ def initialize(address, timeout = nil, options = {})
+ @address = address
@timeout = timeout || TIMEOUT
@ssl_opts = options.reject { |k, v| !k.to_s.start_with?('ssl') }
@socket = nil
View
40 lib/mongo/pool/socket.rb
@@ -16,43 +16,3 @@
require 'mongo/pool/socket/tcp'
require 'mongo/pool/socket/ssl'
require 'mongo/pool/socket/unix'
-
-module Mongo
- class Pool
- module Socket
-
- class << self
-
- # Factory method to create a specific type of socket based on the
- # provided options.
- #
- # @example Create a TCP socket.
- # Socket.create('127.0.0.1', 27017, 5)
- #
- # @example Create an SSL socket.
- # Socket.create('127.0.0.1', 27017, 5, :ssl => true)
- #
- # @example Create a Unix socket.
- # Socket.create('/path/to/socket.sock', nil, 5)
- #
- # @param [ String ] host The host to connect to.
- # @param [ String, nil ] port The port to connect to.
- # @param [ Integer ] timeout The connection timeout.
- # @param [ Hash ] options The ssl options.
- #
- # @return [ Socket ] The socket.
- #
- # @since 3.0.0
- def create(host, port, timeout, options = {})
- if !options.empty?
- Socket::SSL.new(host, port, timeout, options)
- elsif port.nil?
- Socket::Unix.new(host, timeout)
- else
- Socket::TCP.new(host, port, timeout)
- end
- end
- end
- end
- end
-end
View
23 lib/mongo/pool/socket/connectable.rb
@@ -24,6 +24,8 @@ module Socket
module Connectable
include ::Socket::Constants
+ attr_reader :family
+
# @return [ String ] host The host to connect to.
attr_reader :host
@@ -123,28 +125,19 @@ def write(*args)
private
def handle_connect
- error = nil
- addr_info = ::Socket.getaddrinfo(host, nil, AF_UNSPEC, SOCK_STREAM)
- addr_info.each do |info|
- begin
- sock = create_socket(info[4])
- socket_addr = ::Socket.pack_sockaddr_in(port, info[3])
- sock.connect(socket_addr)
- return sock
- rescue IOError, SystemCallError => e
- error = e
- end
- end
- raise error
+ create_socket
end
- def create_socket(family)
+ def create_socket
sock = ::Socket.new(family, SOCK_STREAM, 0)
+ timeout_value = [ timeout, 0 ].pack('l_2')
+
sock.set_encoding('binary') if sock.respond_to?(:set_encoding)
sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) if family != AF_UNIX
- timeout_value = [timeout, 0].pack('l_2')
sock.setsockopt(SOL_SOCKET, SO_RCVTIMEO, timeout_value)
sock.setsockopt(SOL_SOCKET, SO_SNDTIMEO, timeout_value)
+
+ sock.connect(::Socket.pack_sockaddr_in(port, host))
sock
end
View
3 lib/mongo/pool/socket/ssl.rb
@@ -86,10 +86,11 @@ def connect!
# from the other end of the socket connection. Implies :ssl_verify.
#
# @since 3.0.0
- def initialize(host, port, timeout, opts = {})
+ def initialize(host, port, timeout, family, opts = {})
@host = host
@port = port
@timeout = timeout
+ @family = family
@context = Context.create(opts)
end
View
3 lib/mongo/pool/socket/tcp.rb
@@ -54,10 +54,11 @@ def connect!
# @param timeout [ Integer ] The socket timeout value.
#
# @since 3.0.0
- def initialize(host, port, timeout)
+ def initialize(host, port, timeout, family)
@host = host
@port = port
@timeout = timeout
+ @family = family
end
end
end
View
10 lib/mongo/server.rb
@@ -49,6 +49,16 @@ def ==(other)
address == other.address
end
+ # Instantiate a new server object. Will start the background refresh and
+ # subscribe to the appropriate events.
+ #
+ # @example Initialize the server.
+ # Mongo::Server.new('127.0.0.1:27017')
+ #
+ # @param [ String ] address The host:port address to connect to.
+ # @param [ Hash ] options The server options.
+ #
+ # @since 3.0.0
def initialize(address, options = {})
@address = Address.new(address)
@options = options
View
2 lib/mongo/server/address.rb
@@ -31,7 +31,7 @@ class Address
# Delegate the ip, host, and port methods to the resolver.
#
# @since 3.0.0
- def_delegators :@resolver, :ip, :host, :port
+ def_delegators :@resolver, :ip, :host, :port, :socket
# @return [ Integer ] port The port to the connect to.
attr_reader :resolver
View
19 lib/mongo/server/address/ipv4.rb
@@ -55,6 +55,25 @@ def initialize(address)
def pattern
Resolv::IPv4::Regex
end
+
+ # Get a socket for the provided address type, given the options.
+ #
+ # @example Get an IPv4 socket.
+ # ipv4.socket(5, :ssl => true)
+ #
+ # @param [ Float ] timeout The socket timeout.
+ # @param [ Hash ] ssl_opts SSL options.
+ #
+ # @return [ Pool::Socket::SSL, Pool::Socket::TCP ] The socket.
+ #
+ # @since 3.0.0
+ def socket(timeout, ssl_opts = {})
+ unless ssl_opts.empty?
+ Pool::Socket::SSL.new(ip, port, timeout, Socket::PF_INET, ssl_opts)
+ else
+ Pool::Socket::TCP.new(ip, port, timeout, Socket::PF_INET)
+ end
+ end
end
end
end
View
19 lib/mongo/server/address/ipv6.rb
@@ -55,6 +55,25 @@ def initialize(address)
def pattern
Resolv::IPv6::Regex
end
+
+ # Get a socket for the provided address type, given the options.
+ #
+ # @example Get an IPv6 socket.
+ # ipv4.socket(5, :ssl => true)
+ #
+ # @param [ Float ] timeout The socket timeout.
+ # @param [ Hash ] ssl_opts SSL options.
+ #
+ # @return [ Pool::Socket::SSL, Pool::Socket::TCP ] The socket.
+ #
+ # @since 3.0.0
+ def socket(timeout, ssl_opts = {})
+ unless ssl_opts.empty?
+ Pool::Socket::SSL.new(ip, port, timeout, Socket::PF_INET6, ssl_opts)
+ else
+ Pool::Socket::TCP.new(ip, port, timeout, Socket::PF_INET6)
+ end
+ end
end
end
end
View
15 lib/mongo/server/address/sock.rb
@@ -38,6 +38,21 @@ class Sock
def initialize(address)
@host = address
end
+
+ # Get a socket for the provided address type, given the options.
+ #
+ # @example Get a Unix socket.
+ # ipv4.socket(5)
+ #
+ # @param [ Float ] timeout The socket timeout.
+ # @param [ Hash ] ssl_opts SSL options - ignored.
+ #
+ # @return [ Pool::Socket::Unix ] The socket.
+ #
+ # @since 3.0.0
+ def socket(timeout, ssl_opts = {})
+ Pool::Socket::Unix.new(host, timeout)
+ end
end
end
end
View
28 spec/mongo/pool/connection_spec.rb
@@ -2,10 +2,14 @@
describe Mongo::Pool::Connection do
+ let(:address) do
+ Mongo::Server::Address.new('127.0.0.1:27017')
+ end
+
describe '#connect!' do
let(:connection) do
- described_class.new('127.0.0.1', 27017)
+ described_class.new(address)
end
context 'when no socket exists' do
@@ -53,7 +57,7 @@
context 'when a socket is not connected' do
let(:connection) do
- described_class.new('127.0.0.1', 27017)
+ described_class.new(address)
end
it 'does not raise an error' do
@@ -64,7 +68,7 @@
context 'when a socket is connected' do
let(:connection) do
- described_class.new('127.0.0.1', 27017)
+ described_class.new(address)
end
before do
@@ -83,15 +87,11 @@
context 'when host and port are provided' do
let(:connection) do
- described_class.new('127.0.0.1', 27017)
- end
-
- it 'sets the host' do
- expect(connection.host).to eq('127.0.0.1')
+ described_class.new(address)
end
- it 'sets the port' do
- expect(connection.port).to eq(27017)
+ it 'sets the address' do
+ expect(connection.address).to eq(address)
end
it 'sets the socket to nil' do
@@ -106,7 +106,7 @@
context 'when timeout options are provided' do
let(:connection) do
- described_class.new('127.0.0.1', 27017, 10)
+ described_class.new(address, 10)
end
it 'sets the timeout' do
@@ -117,7 +117,7 @@
context 'when ssl options are provided' do
let(:connection) do
- described_class.new('127.0.0.1', 27017, nil, :ssl => true)
+ described_class.new(address, nil, :ssl => true)
end
it 'sets the ssl options' do
@@ -129,7 +129,7 @@
describe '#read' do
let(:connection) do
- described_class.new('127.0.0.1', 27017, 5)
+ described_class.new(address, 5)
end
let(:documents) do
@@ -170,7 +170,7 @@
describe '#write' do
let(:connection) do
- described_class.new('127.0.0.1', 27017, 5)
+ described_class.new(address, 5)
end
let(:documents) do
View
10 spec/mongo/pool/socket/ssl_spec.rb
@@ -26,7 +26,7 @@
context 'when verifying the certificate' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 5, :ssl_verify => true)
+ described_class.new('127.0.0.1', 27017, 5, Socket::PF_INET, :ssl_verify => true)
end
before do
@@ -42,7 +42,7 @@
context 'when not verifying the certificate' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 5, :ssl => true)
+ described_class.new('127.0.0.1', 27017, 5, Socket::PF_INET, :ssl => true)
end
it 'connects the socket using ssl' do
@@ -54,7 +54,7 @@
describe '#initialize' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 10)
+ described_class.new('127.0.0.1', 27017, 10, Socket::PF_INET)
end
it 'sets the host' do
@@ -79,7 +79,7 @@
context 'when the verify mode is not nil' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 10, :ssl_verify => true)
+ described_class.new('127.0.0.1', 27017, 10, Socket::PF_INET, :ssl_verify => true)
end
it 'returns true' do
@@ -90,7 +90,7 @@
context 'when the verify mode is nil' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 10)
+ described_class.new('127.0.0.1', 27017, 10, Socket::PF_INET)
end
it 'returns false' do
View
4 spec/mongo/pool/socket/tcp_spec.rb
@@ -5,7 +5,7 @@
describe '#connect' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 10)
+ described_class.new('127.0.0.1', 27017, 10, Socket::PF_INET)
end
let(:connected) do
@@ -24,7 +24,7 @@
describe '#initialize' do
let(:socket) do
- described_class.new('127.0.0.1', 27017, 10)
+ described_class.new('127.0.0.1', 27017, 10, Socket::PF_INET)
end
it 'sets the host' do
View
40 spec/mongo/pool/socket_spec.rb
@@ -1,40 +0,0 @@
-require 'spec_helper'
-
-describe Mongo::Pool::Socket do
-
- describe '.create' do
-
- context 'when the port is nil' do
-
- let(:socket) do
- described_class.create('/path/to/socket.sock', nil, 5)
- end
-
- it 'creates a unix socket' do
- expect(socket).to be_a(Mongo::Pool::Socket::Unix)
- end
- end
-
- context 'when ssl options exist' do
-
- let(:socket) do
- described_class.create('127.0.0.1', 27017, 5, :ssl => true)
- end
-
- it 'creates an ssl socket' do
- expect(socket).to be_a(Mongo::Pool::Socket::SSL)
- end
- end
-
- context 'when a port and no ssl options exist' do
-
- let(:socket) do
- described_class.create('127.0.0.1', 27017, 5)
- end
-
- it 'creates a tcp socket' do
- expect(socket).to be_a(Mongo::Pool::Socket::TCP)
- end
- end
- end
-end
View
6 spec/mongo/pool_spec.rb
@@ -68,7 +68,7 @@
end
it 'returns a new connection' do
- expect(connection.host).to eq(server.address.ip)
+ expect(connection.address).to eq(server.address)
end
it 'puts the new connection on the thread local stack' do
@@ -87,7 +87,7 @@
end
it 'returns the threads connection' do
- expect(pool.checkout.host).to eq(server.address.ip)
+ expect(pool.checkout.address).to eq(server.address)
end
it 'keeps the connection on the thread local stack' do
@@ -106,7 +106,7 @@
end
it 'returns a new connection' do
- expect(pool.checkout.host).to eq(server.address.ip)
+ expect(pool.checkout.address).to eq(server.address)
end
it 'does not return the same connection instance' do
View
37 spec/mongo/server/address/ipv4_spec.rb
@@ -42,4 +42,41 @@
end
end
end
+
+ describe '#socket' do
+
+ let(:resolver) do
+ described_class.new('127.0.0.1')
+ end
+
+ context 'when ssl options are provided' do
+
+ let(:socket) do
+ resolver.socket(5, :ssl => true)
+ end
+
+ it 'returns an ssl socket' do
+ expect(socket).to be_a(Mongo::Pool::Socket::SSL)
+ end
+
+ it 'sets the family as ipv4' do
+ expect(socket.family).to eq(Socket::PF_INET)
+ end
+ end
+
+ context 'when ssl options are not provided' do
+
+ let(:socket) do
+ resolver.socket(5)
+ end
+
+ it 'returns a tcp socket' do
+ expect(socket).to be_a(Mongo::Pool::Socket::TCP)
+ end
+
+ it 'sets the family a ipv4' do
+ expect(socket.family).to eq(Socket::PF_INET)
+ end
+ end
+ end
end
View
37 spec/mongo/server/address/ipv6_spec.rb
@@ -42,4 +42,41 @@
end
end
end
+
+ describe '#socket' do
+
+ let(:resolver) do
+ described_class.new('[::1]')
+ end
+
+ context 'when ssl options are provided' do
+
+ let(:socket) do
+ resolver.socket(5, :ssl => true)
+ end
+
+ it 'returns an ssl socket' do
+ expect(socket).to be_a(Mongo::Pool::Socket::SSL)
+ end
+
+ it 'sets the family as ipv6' do
+ expect(socket.family).to eq(Socket::PF_INET6)
+ end
+ end
+
+ context 'when ssl options are not provided' do
+
+ let(:socket) do
+ resolver.socket(5)
+ end
+
+ it 'returns a tcp socket' do
+ expect(socket).to be_a(Mongo::Pool::Socket::TCP)
+ end
+
+ it 'sets the family a ipv6' do
+ expect(socket.family).to eq(Socket::PF_INET6)
+ end
+ end
+ end
end
View
15 spec/mongo/server/address/sock_spec.rb
@@ -20,4 +20,19 @@
expect(resolver.host).to eq('/path/to/socket.sock')
end
end
+
+ describe '#socket' do
+
+ let(:resolver) do
+ described_class.new('/path/to/socket.sock')
+ end
+
+ let(:socket) do
+ resolver.socket(5)
+ end
+
+ it 'returns a unix socket' do
+ expect(socket).to be_a(Mongo::Pool::Socket::Unix)
+ end
+ end
end

0 comments on commit 4507e2d

Please sign in to comment.