Skip to content

Commit

Permalink
Support IPv6 addresses in address lists (see below for limitations)
Browse files Browse the repository at this point in the history
This requires IPv6 addresses to be enquoted in square brackets.
Fixes #383.

Credits: Jeremy Heiler.
  • Loading branch information
michaelklishin committed Mar 9, 2016
1 parent e429256 commit 6be10be
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 11 deletions.
78 changes: 67 additions & 11 deletions lib/bunny/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,18 +138,23 @@ def initialize(connection_string_or_opts = Hash.new, optz = Hash.new)
@default_hosts_shuffle_strategy = Proc.new { |hosts| hosts.shuffle }

@opts = opts
log_file = opts[:log_file] || opts[:logfile] || STDOUT
log_level = opts[:log_level] || ENV["BUNNY_LOG_LEVEL"] || Logger::WARN
# we might need to log a warning about ill-formatted IPv6 address but
# progname includes hostname, so init like this first
@logger = opts.fetch(:logger, init_default_logger_without_progname(log_file, log_level))

@addresses = self.addresses_from(opts)
@address_index = 0

# re-init, see above
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))

@user = self.username_from(opts)
@pass = self.password_from(opts)
@vhost = self.vhost_from(opts)
@threaded = opts.fetch(:threaded, true)

log_file = opts[:log_file] || opts[:logfile] || STDOUT
log_level = opts[:log_level] || ENV["BUNNY_LOG_LEVEL"] || Logger::WARN
@logger = opts.fetch(:logger, init_default_logger(log_file, log_level))

validate_connection_options(opts)

# should automatic recovery from network failures be used?
Expand Down Expand Up @@ -774,11 +779,6 @@ def addresses_from(options)
shuffle_strategy.call addresses
end

# @private
def host_with_port?(address)
address.include? ':'
end

# @private
def port_from(options)
fallback = if options[:tls] || options[:ssl]
Expand All @@ -790,14 +790,61 @@ def port_from(options)
options.fetch(:port, fallback)
end

# @private
def host_with_port?(address)
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
last_colon = address.rindex(":")
last_closing_square_bracket = address.rindex("]")

if last_closing_square_bracket.nil?
address.include?(":")
else
last_closing_square_bracket < last_colon
end
end

# @private
def host_from_address(address)
address.split(":")[0]
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
last_colon = address.rindex(":")
last_closing_square_bracket = address.rindex("]")

if last_closing_square_bracket.nil?
parts = address.split(":")
# this looks like an unquoted IPv6 address, so emit a warning
if parts.size > 2
@logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]"
end
return parts[0]
end

if last_closing_square_bracket < last_colon
# there is a port
address[0, last_colon]
elsif last_closing_square_bracket > last_colon
address
end
end

# @private
def port_from_address(address)
address.split(":")[1].to_i
# we need to handle cases such as [2001:db8:85a3:8d3:1319:8a2e:370:7348]:5671
last_colon = address.rindex(":")
last_closing_square_bracket = address.rindex("]")

if last_closing_square_bracket.nil?
parts = address.split(":")
# this looks like an unquoted IPv6 address, so emit a warning
if parts.size > 2
@logger.warn "Address #{address} looks like an unquoted IPv6 address. Make sure you quote IPv6 addresses like so: [2001:db8:85a3:8d3:1319:8a2e:370:7348]"
end
return parts[1].to_i
end

if last_closing_square_bracket < last_colon
# there is a port
address[(last_colon + 1)..-1].to_i
end
end

# @private
Expand Down Expand Up @@ -1220,6 +1267,15 @@ def init_default_logger(logfile, level)
end
end

# @private
def init_default_logger_without_progname(logfile, level)
@default_logger = begin
lgr = ::Logger.new(logfile)
lgr.level = normalize_log_level(level)
lgr
end
end

# @private
def normalize_log_level(level)
case level
Expand Down
77 changes: 77 additions & 0 deletions spec/higher_level_api/integration/connection_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,83 @@
end
end

context "initialized with :addresses => [...] with quoted IPv6 hostnames" do
after :each do
subject.close if subject.open?
end

let(:host) { "[2001:db8:85a3:8d3:1319:8a2e:370:7348]" }
let(:port) { 5673 }
let(:address) { "#{host}:#{port}" }
let(:addresses) { [address] }
let(:subject) { described_class.new(:addresses => addresses) }

it "uses correct hostname" do
expect(subject.host).to eq host
expect(subject.hostname).to eq host
end

it "uses port 5673" do
expect(subject.port).to eq port
end

it "uses username = guest" do
expect(subject.username).to eq username
expect(subject.user).to eq username
end
end

context "initialized with :addresses => [...] with quoted IPv6 hostnames without ports" do
after :each do
subject.close if subject.open?
end

let(:host) { "[2001:db8:85a3:8d3:1319:8a2e:370:7348]" }
let(:address) { host }
let(:addresses) { [address] }
let(:subject) { described_class.new(:addresses => addresses) }

it "uses correct hostname" do
expect(subject.host).to eq host
expect(subject.hostname).to eq host
end

it "uses port 5672" do
expect(subject.port).to eq 5672
end

it "uses username = guest" do
expect(subject.username).to eq username
expect(subject.user).to eq username
end
end

context "initialized with :addresses => [...] with an quoted IPv6 hostnames" do
after :each do
subject.close if subject.open?
end

let(:host) { "2001:db8:85a3:8d3:1319:8a2e:370:7348" }
let(:port) { 5673 }
let(:address) { "#{host}:#{port}" }
let(:addresses) { [address] }
let(:subject) { described_class.new(:addresses => addresses) }

it "fails to correctly parse the host (and emits a warning)" do
expect(subject.host).to eq "2001"
expect(subject.hostname).to eq "2001"
end

it "fails to correctly parse the port (and emits a warning)" do
expect(subject.port).to eq 0
end

it "uses username = guest" do
expect(subject.username).to eq username
expect(subject.user).to eq username
end
end

context "initialized with conflicting hosts and addresses" do
let(:host) { "192.168.1.10" }
let(:port) { 5673 }
Expand Down

0 comments on commit 6be10be

Please sign in to comment.