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 = [] @ios = []
end 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) def env(sock)
@envs.fetch(sock, @proto_env) @envs.fetch(sock, @proto_env)
@ -204,12 +205,6 @@ module Puma
end end
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+. # Tell the server to listen on host +host+, port +port+.
# If +optimize_for_latency+ is true (the default) then clients connecting # If +optimize_for_latency+ is true (the default) then clients connecting
# will be optimized for latency over throughput. # will be optimized for latency over throughput.
@ -238,7 +233,7 @@ module Puma
end end
def connected_ports def connected_ports
ios.map { |io| io.addr[1] } ios.map { |io| io.addr[1] }.uniq
end end
def inherit_tcp_listener(host, port, fd) def inherit_tcp_listener(host, port, fd)
@ -361,12 +356,12 @@ module Puma
end end
def close_listeners def close_listeners
@listeners.each do |l, io| listeners.each do |l, io|
io.close unless io.closed? # Ruby 2.2 issue io.close unless io.closed? # Ruby 2.2 issue
uri = URI.parse(l) uri = URI.parse(l)
next unless uri.scheme == 'unix' next unless uri.scheme == 'unix'
unix_path = "#{uri.host}#{uri.path}" 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
end end
@ -378,5 +373,13 @@ module Puma
end end
redirects redirects
end 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
end end

View file

@ -23,18 +23,44 @@ class TestBinderBase < Minitest::Test
end end
class TestBinder < TestBinderBase class TestBinder < TestBinderBase
parallelize_me!
def test_localhost_addresses_dont_alter_listeners_for_tcp_addresses 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 assert_empty @binder.listeners
end 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 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 assert_empty @binder.listeners
end 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 def test_correct_zero_port
@binder.parse ["tcp://localhost:0"], @events @binder.parse ["tcp://localhost:0"], @events
@ -59,7 +85,7 @@ class TestBinder < TestBinderBase
@binder.parse ["tcp://localhost:0"], @events @binder.parse ["tcp://localhost:0"], @events
assert_match %r!tcp://127.0.0.1:(\d+)!, @events.stdout.string 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 assert_match %r!tcp://\[::1\]:(\d+)!, @events.stdout.string
end end
end end
@ -69,7 +95,7 @@ class TestBinder < TestBinderBase
@binder.parse ["ssl://localhost:0?#{ssl_query}"], @events @binder.parse ["ssl://localhost:0?#{ssl_query}"], @events
assert_match %r!ssl://127.0.0.1:(\d+)!, @events.stdout.string 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 assert_match %r!ssl://\[::1\]:(\d+)!, @events.stdout.string
end end
end end
@ -156,6 +182,54 @@ class TestBinder < TestBinderBase
assert_equal @events.stderr, env_hash["rack.errors"] assert_equal @events.stderr, env_hash["rack.errors"]
end 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 private
def assert_parsing_logs_uri(order = [:unix, :tcp]) def assert_parsing_logs_uri(order = [:unix, :tcp])