1
0
Fork 0
mirror of https://github.com/puma/puma.git synced 2022-11-09 13:48:40 -05:00

Add tests to binder (#2156)

+ Make loopback_addresses private

+ Semi-bugfix: connected ports are unique

You can bind to the same port twice in ipv4 and ipv6, which probably wasnt what we intended to have those show up twice in the list
This commit is contained in:
Nate Berkopec 2020-03-07 12:44:52 -06:00 committed by GitHub
parent 80b7148b06
commit e6eb51f222
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 91 additions and 14 deletions

View file

@ -43,7 +43,8 @@ module Puma
@ios = []
end
attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs
attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs, :activated_sockets, :inherited_fds
attr_writer :ios, :listeners
def env(sock)
@envs.fetch(sock, @proto_env)
@ -204,12 +205,6 @@ module Puma
end
end
def loopback_addresses
Socket.ip_address_list.select do |addrinfo|
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
end.map { |addrinfo| addrinfo.ip_address }.uniq
end
# Tell the server to listen on host +host+, port +port+.
# If +optimize_for_latency+ is true (the default) then clients connecting
# will be optimized for latency over throughput.
@ -238,7 +233,7 @@ module Puma
end
def connected_ports
ios.map { |io| io.addr[1] }
ios.map { |io| io.addr[1] }.uniq
end
def inherit_tcp_listener(host, port, fd)
@ -361,12 +356,12 @@ module Puma
end
def close_listeners
@listeners.each do |l, io|
listeners.each do |l, io|
io.close unless io.closed? # Ruby 2.2 issue
uri = URI.parse(l)
next unless uri.scheme == 'unix'
unix_path = "#{uri.host}#{uri.path}"
File.unlink unix_path if @unix_paths.include? unix_path
File.unlink unix_path if unix_paths.include? unix_path
end
end
@ -378,5 +373,13 @@ module Puma
end
redirects
end
private
def loopback_addresses
Socket.ip_address_list.select do |addrinfo|
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
end.map { |addrinfo| addrinfo.ip_address }.uniq
end
end
end

View file

@ -23,18 +23,44 @@ class TestBinderBase < Minitest::Test
end
class TestBinder < TestBinderBase
parallelize_me!
def test_localhost_addresses_dont_alter_listeners_for_tcp_addresses
@binder.parse ["tcp://localhost:10001"], @events
@binder.parse ["tcp://localhost:0"], @events
assert_empty @binder.listeners
end
def test_home_alters_listeners_for_tcp_addresses
port = UniquePort.call
@binder.parse ["tcp://127.0.0.1:#{port}"], @events
assert_equal "tcp://127.0.0.1:#{port}", @binder.listeners[0][0]
assert_kind_of TCPServer, @binder.listeners[0][1]
end
def test_connected_ports
ports = (1..3).map { |_| UniquePort.call }
@binder.parse(ports.map { |p| "tcp://localhost:#{p}" }, @events)
assert_equal ports, @binder.connected_ports
end
def test_localhost_addresses_dont_alter_listeners_for_ssl_addresses
@binder.parse ["ssl://localhost:10002?#{ssl_query}"], @events
@binder.parse ["ssl://localhost:0?#{ssl_query}"], @events
assert_empty @binder.listeners
end
def test_home_alters_listeners_for_ssl_addresses
port = UniquePort.call
@binder.parse ["ssl://127.0.0.1:#{port}?#{ssl_query}"], @events
assert_equal "ssl://127.0.0.1:#{port}?#{ssl_query}", @binder.listeners[0][0]
assert_kind_of TCPServer, @binder.listeners[0][1]
end
def test_correct_zero_port
@binder.parse ["tcp://localhost:0"], @events
@ -59,7 +85,7 @@ class TestBinder < TestBinderBase
@binder.parse ["tcp://localhost:0"], @events
assert_match %r!tcp://127.0.0.1:(\d+)!, @events.stdout.string
if @binder.loopback_addresses.include?("::1")
if Socket.ip_address_list.any? {|i| i.ipv6_loopback? }
assert_match %r!tcp://\[::1\]:(\d+)!, @events.stdout.string
end
end
@ -69,7 +95,7 @@ class TestBinder < TestBinderBase
@binder.parse ["ssl://localhost:0?#{ssl_query}"], @events
assert_match %r!ssl://127.0.0.1:(\d+)!, @events.stdout.string
if @binder.loopback_addresses.include?("::1")
if Socket.ip_address_list.any? {|i| i.ipv6_loopback? }
assert_match %r!ssl://\[::1\]:(\d+)!, @events.stdout.string
end
end
@ -156,6 +182,54 @@ class TestBinder < TestBinderBase
assert_equal @events.stderr, env_hash["rack.errors"]
end
def test_close_calls_close_on_ios
@mocked_ios = [Minitest::Mock.new, Minitest::Mock.new]
@mocked_ios.each { |m| m.expect(:close, true) }
@binder.ios = @mocked_ios
@binder.close
assert @mocked_ios.each(&:verify)
end
# test redirect for restart
def test_close_listeners_closes_ios
@binder.parse ["tcp://127.0.0.1:#{UniquePort.call}"], @events
refute @binder.listeners.any? { |u, l| l.closed? }
@binder.close_listeners
assert @binder.listeners.all? { |u, l| l.closed? }
end
def test_close_listeners_closes_ios_unless_closed?
@binder.parse ["tcp://127.0.0.1:0"], @events
bomb = @binder.listeners.first[1]
bomb.close
def bomb.close; raise "Boom!"; end # the bomb has been planted
assert @binder.listeners.any? { |u, l| l.closed? }
@binder.close_listeners
assert @binder.listeners.all? { |u, l| l.closed? }
end
def test_listeners_file_unlink_if_unix_listener
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
@binder.parse ["unix://test/#{name}_server.sock"], @events
assert File.socket?("test/#{name}_server.sock")
@binder.close_listeners
refute File.socket?("test/#{name}_server.sock")
end
private
def assert_parsing_logs_uri(order = [:unix, :tcp])