Skip to content

Address uses getaddrinfo to determine protocol #599

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

Merged
merged 1 commit into from
Apr 9, 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
24 changes: 20 additions & 4 deletions lib/mongo/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ def hash
# @since 2.0.0
def initialize(seed, options = {})
address = seed.downcase
case address
when Unix::MATCH then @resolver = Unix.new(address)
when IPv6::MATCH then @resolver = IPv6.new(address)
else @resolver = IPv4.new(address)
host, port = match(address)
case family(host)
when ::Socket::PF_UNIX then @resolver = Unix.new(host, address)
when ::Socket::AF_INET6 then @resolver = IPv6.new(host, port, address)
else @resolver = IPv4.new(host, port, address)
end
end

Expand All @@ -107,5 +108,20 @@ def initialize(seed, options = {})
def inspect
"#<Mongo::Address:0x#{object_id} address=#{resolver.to_s}>"
end

private

def family(host)
fam = (host == 'localhost') ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
::Socket.getaddrinfo(host, nil, fam, ::Socket::SOCK_STREAM).first[4]
end

def match(address)
case address
when Unix::MATCH then Unix.parse(address)
when IPv6::MATCH then IPv6.parse(address)
else IPv4.parse(address)
end
end
end
end
30 changes: 24 additions & 6 deletions lib/mongo/address/ipv4.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,36 @@ class IPv4
# @since 2.0.0
MATCH = Regexp.new('/\./').freeze

# Parse an IPv4 address into its host and port.
#
# @example Parse the address.
# IPv4.parse("127.0.0.1:28011")
#
# @param [ String ] address The address to parse.
#
# @return [ Array<String, Integer> ] The host and port pair.
#
# @since 2.0.0
def self.parse(address)
parts = address.split(':')
host = parts[0]
port = (parts[1] || 27017).to_i
[ host, port ]
end

# Initialize the IPv4 resolver.
#
# @example Initialize the resolver.
# IPv4.new("127.0.0.1:28011")
# IPv4.new("127.0.0.1", 27017, "127.0.0.1:28011")
#
# @param [ String ] address The address to resolve.
# @param [ String ] host The host.
# @param [ Integer ] port The port.
# @param [ String ] address The seed address.
#
# @since 2.0.0
def initialize(address)
parts = address.split(':')
@host = parts[0]
@port = (parts[1] || 27017).to_i
def initialize(host, port, address)
@host = host
@port = port
@seed = address
end

Expand Down
30 changes: 24 additions & 6 deletions lib/mongo/address/ipv6.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,36 @@ class IPv6
# @since 2.0.0
MATCH = Regexp.new('::').freeze

# Parse an IPv6 address into its host and port.
#
# @example Parse the address.
# IPv4.parse("[::1]:28011")
#
# @param [ String ] address The address to parse.
#
# @return [ Array<String, Integer> ] The host and port pair.
#
# @since 2.0.0
def self.parse(address)
parts = address.match(/\[(.+)\]:?(.+)?/)
host = parts[1]
port = (parts[2] || 27017).to_i
[ host, port ]
end

# Initialize the IPv6 resolver.
#
# @example Initialize the resolver.
# IPv6.new("[::1]:28011")
# IPv6.new("::1", 28011, "[::1]:28011")
#
# @param [ String ] address The address to resolve.
# @param [ String ] host The host.
# @param [ Integer ] port The port.
# @param [ String ] address The seed address.
#
# @since 2.0.0
def initialize(address)
parts = address.match(/\[(.+)\]:?(.+)?/)
@host = parts[1]
@port = (parts[2] || 27017).to_i
def initialize(host, port, address)
@host = host
@port = port
@seed = address
end

Expand Down
23 changes: 19 additions & 4 deletions lib/mongo/address/unix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,31 @@ class Unix
# @since 2.0.0
MATCH = Regexp.new('\.sock').freeze

# Parse a socket path.
#
# @example Parse the address.
# Unix.parse("/path/to/socket.sock")
#
# @param [ String ] address The address to parse.
#
# @return [ Array<String> ] A list with the host (socket path).
#
# @since 2.0.0
def self.parse(address)
[ address ]
end

# Initialize the socket resolver.
#
# @example Initialize the resolver.
# Sock.new("/path/to/socket.sock")
# Unix.new("/path/to/socket.sock", "/path/to/socket.sock")
#
# @param [ String ] address The socket path.
# @param [ String ] host The host.
# @param [ String ] address The seed address.
#
# @since 2.0.0
def initialize(address)
@host = address
def initialize(host, address)
@host = host
@seed = address
end

Expand Down
33 changes: 27 additions & 6 deletions spec/mongo/address/ipv4_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,33 @@

describe Mongo::Address::IPv4 do

let(:resolver) do
described_class.new(*described_class.parse(address), address)
end

describe 'self.parse' do

context 'when a port is provided' do

it 'returns the host and port' do
expect(described_class.parse('127.0.0.1:27017')).to eq(['127.0.0.1', 27017])
end
end

context 'when no port is provided' do

it 'returns the host and port' do
expect(described_class.parse('127.0.0.1')).to eq(['127.0.0.1', 27017])
end
end
end

describe '#initialize' do

context 'when a port is provided' do

let(:resolver) do
described_class.new('127.0.0.1:27017')
let(:address) do
'127.0.0.1:27017'
end

it 'sets the port' do
Expand All @@ -21,8 +42,8 @@

context 'when no port is provided' do

let(:resolver) do
described_class.new('127.0.0.1')
let(:address) do
'127.0.0.1'
end

it 'sets the port to 27017' do
Expand All @@ -37,8 +58,8 @@

describe '#socket' do

let(:resolver) do
described_class.new('127.0.0.1')
let(:address) do
'127.0.0.1'
end

context 'when ssl options are provided' do
Expand Down
38 changes: 32 additions & 6 deletions spec/mongo/address/ipv6_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,38 @@

describe Mongo::Address::IPv6 do

before do
allow(::Socket).to receive(:getaddrinfo).at_most(2).times.
and_return([[nil,nil,nil,nil,::Socket::AF_INET6]])
end

let(:resolver) do
described_class.new(*described_class.parse(address), address)
end

describe 'self.parse' do

context 'when a port is provided' do

it 'returns the host and port' do
expect(described_class.parse('[::1]:27017')).to eq(['::1', 27017])
end
end

context 'when no port is provided' do

it 'returns the host and port' do
expect(described_class.parse('[::1]')).to eq(['::1', 27017])
end
end
end

describe '#initialize' do

context 'when a port is provided' do

let(:resolver) do
described_class.new('[::1]:27017')
let(:address) do
'[::1]:27017'
end

it 'sets the port' do
Expand All @@ -21,8 +47,8 @@

context 'when no port is provided' do

let(:resolver) do
described_class.new('[::1]')
let(:address) do
'[::1]'
end

it 'sets the port to 27017' do
Expand All @@ -37,8 +63,8 @@

describe '#socket' do

let(:resolver) do
described_class.new('[::1]')
let(:address) do
'[::1]'
end

context 'when ssl options are provided' do
Expand Down
24 changes: 20 additions & 4 deletions spec/mongo/address/unix_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,26 @@

describe Mongo::Address::Unix do

before do
allow(::Socket).to receive(:getaddrinfo).at_most(2).times.
and_return([[nil,nil,nil,nil,::Socket::PF_UNIX]])
end

let(:resolver) do
described_class.new(*described_class.parse(address), address)
end

describe 'self.parse' do

it 'returns the host and no port' do
expect(described_class.parse('/path/to/socket.sock')).to eq(['/path/to/socket.sock'])
end
end

describe '#initialize' do

let(:resolver) do
described_class.new('/path/to/socket.sock')
let(:address) do
'/path/to/socket.sock'
end

it 'sets the host' do
Expand All @@ -15,8 +31,8 @@

describe '#socket' do

let(:resolver) do
described_class.new('/path/to/socket.sock')
let(:address) do
'/path/to/socket.sock'
end

let(:socket) do
Expand Down
15 changes: 15 additions & 0 deletions spec/mongo/address_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@

context 'when the addresses are identical unix sockets' do

before do
allow(::Socket).to receive(:getaddrinfo).twice.
and_return([[nil,nil,nil,nil,::Socket::PF_UNIX]])
end

let(:address) do
described_class.new('/path/to/socket.sock')
end
Expand Down Expand Up @@ -124,6 +129,11 @@

context 'when providing an ipv6 host' do

before do
allow(::Socket).to receive(:getaddrinfo).once.
and_return([[nil,nil,nil,nil,::Socket::AF_INET6]])
end

context 'when a port is provided' do

let(:address) do
Expand Down Expand Up @@ -190,6 +200,11 @@

context 'when providing a socket path' do

before do
allow(::Socket).to receive(:getaddrinfo).once.
and_return([[nil,nil,nil,nil,::Socket::PF_UNIX]])
end

let(:address) do
described_class.new('/path/to/socket.sock')
end
Expand Down
23 changes: 23 additions & 0 deletions spec/mongo/server_discovery_and_monitoring_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,20 @@

before(:all) do

# We monkey-patch the address, so that looking up the spec's hostname does
# not throw an error.
#
# @since 2.0.0
class Mongo::Address
private

def family(host)
fam = host == 'localhost' ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
::Socket.getaddrinfo(host, nil, fam, ::Socket::SOCK_STREAM).first[4]
rescue SocketError
end
end

# We monkey-patch the server here, so the monitors do not run and no
# real TCP connection is attempted. Thus we can control the server
# descriptions per-phase.
Expand Down Expand Up @@ -60,6 +74,15 @@ def disconnect!
@monitor.stop! and true
end
end

class Mongo::Address
private

def family(host)
fam = host == 'localhost' ? ::Socket::AF_INET : ::Socket::AF_UNSPEC
::Socket.getaddrinfo(host, nil, fam, ::Socket::SOCK_STREAM).first[4]
end
end
end

spec.phases.each_with_index do |phase, index|
Expand Down
Loading