about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-04-10 14:58:48 -0700
committerEric Wong <normalperson@yhbt.net>2009-04-10 15:12:21 -0700
commit3e9fe197d4daac14fa98addfcf9be3208c7b96b8 (patch)
treedca6a4d119af3b2b1da1b206d6a5064731d5a1f6
parent0bd8cb742eadf45969133c13ebc5252b5234ef92 (diff)
downloadunicorn-3e9fe197d4daac14fa98addfcf9be3208c7b96b8.tar.gz
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.
-rw-r--r--lib/unicorn.rb5
-rw-r--r--lib/unicorn/configurator.rb4
-rw-r--r--lib/unicorn/socket.rb44
-rw-r--r--test/unit/test_socket_helper.rb20
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)