From 3e9fe197d4daac14fa98addfcf9be3208c7b96b8 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 10 Apr 2009 14:58:48 -0700 Subject: listen backlog, sndbuf, rcvbuf are always changeable Apparently I was smoking crack and thought they weren't changeable. Additionally, SO_REUSEADDR is set by TCPServer.new, so there's no need to set it ourselves; so avoid putting extra items in the purgatory. This allows SIGHUP to change listen options. --- lib/unicorn.rb | 5 +++-- lib/unicorn/configurator.rb | 4 ---- lib/unicorn/socket.rb | 44 +++++++++++++++++++---------------------- test/unit/test_socket_helper.rb | 20 ++++++++++++------- 4 files changed, 36 insertions(+), 37 deletions(-) diff --git a/lib/unicorn.rb b/lib/unicorn.rb index 2883bc2..fd66529 100644 --- a/lib/unicorn.rb +++ b/lib/unicorn.rb @@ -74,7 +74,7 @@ module Unicorn # before they become UNIXServer or TCPServer inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd| io = Socket.for_fd(fd.to_i) - set_server_sockopt(io) + set_server_sockopt(io, @listener_opts[sock_name(io)]) @io_purgatory << io logger.info "inherited addr=#{sock_name(io)} fd=#{fd}" server_cast(io) @@ -125,6 +125,7 @@ module Unicorn end (io.close rescue nil).nil? # true else + set_server_sockopt(io, @listener_opts[sock_name(io)]) false end end @@ -153,7 +154,7 @@ module Unicorn return if String === address && listener_names.include?(address) if io = bind_listen(address, opt) - if Socket == io.class + unless TCPServer === io || UNIXServer === io @io_purgatory << io io = server_cast(io) end diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index 150377b..3fcb892 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -192,10 +192,6 @@ module Unicorn # specified. # # Defaults: operating system defaults - # - # Due to limitations of the operating system, options specified here - # cannot affect existing listener sockets in any way, sockets must be - # completely closed and rebound. def listen(address, opt = { :backlog => 1024 }) address = expand_addr(address) if String === address diff --git a/lib/unicorn/socket.rb b/lib/unicorn/socket.rb index 1b666b4..4cb8d48 100644 --- a/lib/unicorn/socket.rb +++ b/lib/unicorn/socket.rb @@ -47,7 +47,17 @@ module Unicorn sock.setsockopt(SOL_TCP, TCP_CORK, 1) if defined?(TCP_CORK) end - def set_server_sockopt(sock) + def set_server_sockopt(sock, opt) + opt ||= {} + if opt[:rcvbuf] || opt[:sndbuf] + log_buffer_sizes(sock, "before: ") + sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf] + sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf] + log_buffer_sizes(sock, " after: ") + end + sock.listen(opt[:backlog] || 1024) + return if sock_name(sock)[0..0] == "/" + if defined?(TCP_DEFER_ACCEPT) sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1) rescue nil end @@ -70,7 +80,7 @@ module Unicorn def bind_listen(address = '0.0.0.0:8080', opt = { :backlog => 1024 }) return address unless String === address - domain, bind_addr = if address[0..0] == "/" + sock = if address[0..0] == "/" if File.exist?(address) if File.socket?(address) if self.respond_to?(:logger) @@ -82,32 +92,18 @@ module Unicorn "socket=#{address} specified but it is not a socket!" end end - [ AF_UNIX, Socket.pack_sockaddr_un(address) ] + old_umask = File.umask(0) + begin + UNIXServer.new(address) + ensure + File.umask(old_umask) + end elsif address =~ /^(\d+\.\d+\.\d+\.\d+):(\d+)$/ - [ AF_INET, Socket.pack_sockaddr_in($2.to_i, $1) ] + TCPServer.new($1, $2.to_i) else raise ArgumentError, "Don't know how to bind: #{address}" end - - sock = Socket.new(domain, SOCK_STREAM, 0) - sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) if defined?(SO_REUSEADDR) - old_umask = File.umask(0) - begin - sock.bind(bind_addr) - rescue Errno::EADDRINUSE - sock.close rescue nil - return nil - ensure - File.umask(old_umask) - end - if opt[:rcvbuf] || opt[:sndbuf] - log_buffer_sizes(sock, "before: ") - sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf] - sock.setsockopt(SOL_SOCKET, SO_SNDBUF, opt[:sndbuf]) if opt[:sndbuf] - log_buffer_sizes(sock, " after: ") - end - sock.listen(opt[:backlog] || 1024) - set_server_sockopt(sock) if domain == AF_INET + set_server_sockopt(sock, opt) sock end diff --git a/test/unit/test_socket_helper.rb b/test/unit/test_socket_helper.rb index 79e1cdc..0608e24 100644 --- a/test/unit/test_socket_helper.rb +++ b/test/unit/test_socket_helper.rb @@ -16,7 +16,7 @@ class TestSocketHelper < Test::Unit::TestCase port = unused_port @test_addr @tcp_listener_name = "#@test_addr:#{port}" @tcp_listener = bind_listen(@tcp_listener_name) - assert Socket === @tcp_listener + assert TCPServer === @tcp_listener assert_equal @tcp_listener_name, sock_name(@tcp_listener) end @@ -31,10 +31,10 @@ class TestSocketHelper < Test::Unit::TestCase ].each do |opts| assert_nothing_raised do tcp_listener = bind_listen(tcp_listener_name, opts) - assert Socket === tcp_listener + assert TCPServer === tcp_listener tcp_listener.close unix_listener = bind_listen(unix_listener_name, opts) - assert Socket === unix_listener + assert UNIXServer === unix_listener unix_listener.close end end @@ -47,7 +47,7 @@ class TestSocketHelper < Test::Unit::TestCase @unix_listener_path = tmp.path File.unlink(@unix_listener_path) @unix_listener = bind_listen(@unix_listener_path) - assert Socket === @unix_listener + assert UNIXServer === @unix_listener assert_equal @unix_listener_path, sock_name(@unix_listener) assert File.readable?(@unix_listener_path), "not readable" assert File.writable?(@unix_listener_path), "not writable" @@ -61,6 +61,7 @@ class TestSocketHelper < Test::Unit::TestCase a = bind_listen(@unix_listener) assert_equal a.fileno, @unix_listener.fileno unix_server = server_cast(@unix_listener) + assert UNIXServer === unix_server a = bind_listen(unix_server) assert_equal a.fileno, unix_server.fileno assert_equal a.fileno, @unix_listener.fileno @@ -71,6 +72,7 @@ class TestSocketHelper < Test::Unit::TestCase a = bind_listen(@tcp_listener) assert_equal a.fileno, @tcp_listener.fileno tcp_server = server_cast(@tcp_listener) + assert TCPServer === tcp_server a = bind_listen(tcp_server) assert_equal a.fileno, tcp_server.fileno assert_equal a.fileno, @tcp_listener.fileno @@ -79,7 +81,7 @@ class TestSocketHelper < Test::Unit::TestCase def test_bind_listen_unix_rebind test_bind_listen_unix new_listener = bind_listen(@unix_listener_path) - assert Socket === new_listener + assert UNIXServer === new_listener assert new_listener.fileno != @unix_listener.fileno assert_equal sock_name(new_listener), sock_name(@unix_listener) assert_equal @unix_listener_path, sock_name(new_listener) @@ -100,13 +102,17 @@ class TestSocketHelper < Test::Unit::TestCase test_bind_listen_unix test_bind_listen_tcp end - @unix_server = server_cast(@unix_listener) + unix_listener_socket = Socket.for_fd(@unix_listener.fileno) + assert Socket === unix_listener_socket + @unix_server = server_cast(unix_listener_socket) assert_equal @unix_listener.fileno, @unix_server.fileno assert UNIXServer === @unix_server assert File.socket?(@unix_server.path) assert_equal @unix_listener_path, sock_name(@unix_server) - @tcp_server = server_cast(@tcp_listener) + tcp_listener_socket = Socket.for_fd(@tcp_listener.fileno) + assert Socket === tcp_listener_socket + @tcp_server = server_cast(tcp_listener_socket) assert_equal @tcp_listener.fileno, @tcp_server.fileno assert TCPServer === @tcp_server assert_equal @tcp_listener_name, sock_name(@tcp_server) -- cgit v1.2.3-24-ge0c7