mirror of
https://github.com/puma/puma.git
synced 2022-11-09 13:48:40 -05:00
parent
5f9e30e6e0
commit
3df357192a
3 changed files with 71 additions and 41 deletions
|
@ -58,34 +58,32 @@ module Puma
|
|||
ios.map { |io| io.addr[1] }.uniq
|
||||
end
|
||||
|
||||
def import_from_env(env_hash)
|
||||
remove = []
|
||||
remove += create_inherited_fds(env_hash)
|
||||
env_hash.each do |k,v|
|
||||
if k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
|
||||
# systemd socket activation.
|
||||
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
||||
# LISTEN_PID = PID of the service process, aka us
|
||||
# see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
||||
def create_inherited_fds(env_hash)
|
||||
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
||||
fd, url = v.split(":", 2)
|
||||
@inherited_fds[url] = fd.to_i
|
||||
end.keys # pass keys back for removal
|
||||
end
|
||||
|
||||
number_of_sockets_to_listen_on = v.to_i
|
||||
number_of_sockets_to_listen_on.times do |index|
|
||||
fd = index + 3 # 3 is the magic number you add to follow the SA protocol
|
||||
sock = TCPServer.for_fd(fd) # TODO: change to BasicSocket?
|
||||
key = begin # Try to parse as a path
|
||||
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
||||
rescue ArgumentError # Try to parse as a port/ip
|
||||
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
||||
addr = "[#{addr}]" if addr =~ /\:/
|
||||
[:tcp, addr, port]
|
||||
end
|
||||
@activated_sockets[key] = sock
|
||||
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
||||
end
|
||||
remove << k << 'LISTEN_PID'
|
||||
# systemd socket activation.
|
||||
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
||||
# LISTEN_PID = PID of the service process, aka us
|
||||
# see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
||||
def create_activated_fds(env_hash)
|
||||
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
||||
env_hash['LISTEN_FDS'].to_i.times do |index|
|
||||
sock = TCPServer.for_fd(socket_activation_fd(index))
|
||||
key = begin # Try to parse as a path
|
||||
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
||||
rescue ArgumentError # Try to parse as a port/ip
|
||||
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
||||
addr = "[#{addr}]" if addr =~ /\:/
|
||||
[:tcp, addr, port]
|
||||
end
|
||||
@activated_sockets[key] = sock
|
||||
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
||||
end
|
||||
remove
|
||||
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
||||
end
|
||||
|
||||
def parse(binds, logger, log_msg = 'Listening')
|
||||
|
@ -382,14 +380,8 @@ module Puma
|
|||
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
||||
end
|
||||
|
||||
# def create_activated_sockets(env_hash)
|
||||
# end
|
||||
|
||||
def create_inherited_fds(env_hash)
|
||||
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
||||
fd, url = v.split(":", 2)
|
||||
@inherited_fds[url] = fd.to_i
|
||||
end.keys # pass keys back for removal
|
||||
def socket_activation_fd(int)
|
||||
int + 3 # 3 is the magic number you add to follow the SA protocol
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,8 +48,8 @@ module Puma
|
|||
@config = conf
|
||||
|
||||
@binder = Binder.new(@events)
|
||||
env_to_remove = @binder.import_from_env(ENV)
|
||||
env_to_remove.each { |k| ENV.delete k }
|
||||
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
||||
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
||||
|
||||
@environment = conf.environment
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ class TestBinder < TestBinderBase
|
|||
|
||||
def test_import_from_env_listen_inherit
|
||||
@binder.parse ["tcp://127.0.0.1:0"], @events
|
||||
removals = @binder.import_from_env(@binder.redirects_for_restart_env)
|
||||
removals = @binder.create_inherited_fds(@binder.redirects_for_restart_env)
|
||||
|
||||
@binder.listeners.each do |url, io|
|
||||
assert_equal io.to_i, @binder.inherited_fds[url]
|
||||
|
@ -258,14 +258,52 @@ class TestBinder < TestBinderBase
|
|||
assert_includes removals, "PUMA_INHERIT_0"
|
||||
end
|
||||
|
||||
# test socket activation with tcp
|
||||
# test socket activation with IPv6
|
||||
# test socket activation with Unix
|
||||
# test socket activation logs to events
|
||||
# test socket activation returns the right keys to remove
|
||||
# Socket activation tests. We have to skip all of these on non-UNIX platforms
|
||||
# because the check that we do in the code only works if you support UNIX sockets.
|
||||
# This is OK, because systemd obviously only works on Linux.
|
||||
def test_socket_activation_tcp
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
url = "127.0.0.1"
|
||||
port = UniquePort.call
|
||||
sock = Addrinfo.tcp(url, port).listen
|
||||
assert_activates_sockets(url: url, port: port, sock: sock)
|
||||
end
|
||||
|
||||
def test_socket_activation_tcp_ipv6
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
url = "::"
|
||||
port = UniquePort.call
|
||||
sock = Addrinfo.tcp(url, port).listen
|
||||
assert_activates_sockets(url: url, port: port, sock: sock)
|
||||
end
|
||||
|
||||
def test_socket_activation_unix
|
||||
skip UNIX_SKT_MSG unless UNIX_SKT_EXIST
|
||||
path = "test/unixserver.state"
|
||||
sock = Addrinfo.unix(path).listen
|
||||
assert_activates_sockets(path: path, sock: sock)
|
||||
ensure
|
||||
File.unlink path if path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def assert_activates_sockets(path: nil, port: nil, url: nil, sock: nil)
|
||||
hash = { "LISTEN_FDS" => 1, "LISTEN_PID" => $$ }
|
||||
@events.instance_variable_set(:@debug, true)
|
||||
|
||||
@binder.instance_variable_set(:@sock_fd, sock.fileno)
|
||||
def @binder.socket_activation_fd(int); @sock_fd; end
|
||||
@result = @binder.create_activated_fds(hash)
|
||||
|
||||
url = "[::]" if url == "::"
|
||||
ary = path ? [:unix, path] : [:tcp, url, port]
|
||||
|
||||
assert_kind_of TCPServer, @binder.activated_sockets[ary]
|
||||
assert_match "Registered #{ary.join(":")} for activation from LISTEN_FDS", @events.stdout.string
|
||||
assert_equal ["LISTEN_FDS", "LISTEN_PID"], @result
|
||||
end
|
||||
|
||||
def assert_parsing_logs_uri(order = [:unix, :tcp])
|
||||
skip UNIX_SKT_MSG if order.include?(:unix) && !UNIX_SKT_EXIST
|
||||
|
||||
|
|
Loading…
Reference in a new issue