about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-02-05 20:50:58 -0800
committerEric Wong <normalperson@yhbt.net>2009-02-09 19:50:50 -0800
commitbc44990ddb16669030cf0ca72f48e1c6c620a4f2 (patch)
tree0118dccd255738aaf365bc15aaf2360c98f5479b
parenta105f96edd9bc842170c6272b0d37b8891152824 (diff)
downloadunicorn-bc44990ddb16669030cf0ca72f48e1c6c620a4f2.tar.gz
We'll be supporting UNIX domain sockets soon...  Get rid of
tcphack since it was overriding a default method and just
manually call Socket.new, bind, listen ourselves.  Additionaly,
use SO_REUSEADDR when binding since it is convenient for
restarts.
-rw-r--r--lib/unicorn.rb40
-rw-r--r--lib/unicorn/socket.rb53
-rw-r--r--lib/unicorn/tcphack.rb18
3 files changed, 60 insertions, 51 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index b91950f..943ad99 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -15,7 +15,7 @@ require 'http11'
 
 require 'rack'
 
-require 'unicorn/tcphack'
+require 'unicorn/socket'
 require 'unicorn/const'
 require 'unicorn/http_request'
 require 'unicorn/header_out'
@@ -74,9 +74,7 @@ module Unicorn
         instance_variable_set("@#{key.to_s.downcase}", value)
       end
 
-      @socket = TCPServer.new(@host, @port)      
-      @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined?(Fcntl::FD_CLOEXEC)
-
+      @socket = Socket.unicorn_tcp_server(@host, @port, 1024)
     end
 
     # Does the majority of the IO processing.  It has been written in Ruby using
@@ -117,7 +115,7 @@ module Unicorn
             #  ultimate source of the request.  They identify the client for the
             #  immediate request to the server; that client may be a proxy, gateway,
             #  or other intermediary acting on behalf of the actual source client."
-            params[Const::REMOTE_ADDR] = client.peeraddr.last
+            params[Const::REMOTE_ADDR] = client.unicorn_peeraddr.last
 
             # Select handlers that want more detailed request notification
             request = $http_request ||= HttpRequest.new(logger)
@@ -139,7 +137,7 @@ module Unicorn
       rescue EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,Errno::EBADF
         client.close rescue nil
       rescue HttpParserError => e
-        logger.error "HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #{e.inspect}"
+        logger.error "HTTP parse error, malformed request (#{params[Const::HTTP_X_FORWARDED_FOR] || client.unicorn_peeraddr.last}): #{e.inspect}"
         logger.error "REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
       rescue Errno::EMFILE
         logger.error "too many files"
@@ -159,32 +157,12 @@ module Unicorn
       end
     end
 
-    def configure_socket_options
-      case RUBY_PLATFORM
-      when /linux/
-        # 9 is currently TCP_DEFER_ACCEPT
-        $tcp_defer_accept_opts = [Socket::SOL_TCP, 9, 1]
-        $tcp_cork_opts = [Socket::SOL_TCP, 3, 1]
-      when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
-        # Do nothing, just closing a bug when freebsd <= 5.4
-      when /freebsd/
-        # Use the HTTP accept filter if available.
-        # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg
-        unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
-          $tcp_defer_accept_opts = [Socket::SOL_SOCKET, Socket::SO_ACCEPTFILTER, ['httpready', nil].pack('a16a240')]
-        end
-      end
-    end
-    
     # Runs the thing.  Returns a hash keyed by pid with worker number values
     # for which to wait on.  Access the HttpServer.workers attribute
     # to get this hash later.
     def start
       BasicSocket.do_not_reverse_lookup = true
-      configure_socket_options
-      if defined?($tcp_defer_accept_opts) and $tcp_defer_accept_opts
-        @socket.setsockopt(*$tcp_defer_accept_opts) rescue nil
-      end
+      @socket.unicorn_server_init if @socket.respond_to?(:unicorn_server_init)
 
       (1..@nr_workers).each do |worker_nr|
         pid = fork do
@@ -193,12 +171,8 @@ module Unicorn
           trap('QUIT') { alive = false; @socket.close rescue nil }
           while alive
             begin
-              client = @socket.accept
-              client.sync = true
-
-              if defined?($tcp_cork_opts) and $tcp_cork_opts
-                client.setsockopt(*$tcp_cork_opts) rescue nil
-              end
+              client, addr = @socket.accept
+              client.unicorn_client_init
               process_client(client)
             rescue Errno::EMFILE
               logger.error "too many open files"
diff --git a/lib/unicorn/socket.rb b/lib/unicorn/socket.rb
new file mode 100644
index 0000000..40631d5
--- /dev/null
+++ b/lib/unicorn/socket.rb
@@ -0,0 +1,53 @@
+# non-portable Socket code goes here:
+class Socket
+
+  # configure platform-specific options (only tested on Linux 2.6 so far)
+  case RUBY_PLATFORM
+  when /linux/
+    # from /usr/include/linux/tcp.h
+    TCP_DEFER_ACCEPT = 9 unless defined?(TCP_DEFER_ACCEPT)
+    TCP_CORK = 3 unless defined?(TCP_CORK)
+
+    def unicorn_server_init
+      self.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1)
+    end
+  when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
+  when /freebsd/
+    # Use the HTTP accept filter if available.
+    # The struct made by pack() is defined in /usr/include/sys/socket.h as accept_filter_arg
+    unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
+      unless defined?(SO_ACCEPTFILTER_HTTPREADY)
+        SO_ACCEPTFILTER_HTTPREADY = ['httpready',nil].pack('a16a240').freeze
+      end
+
+      def unicorn_server_init
+        self.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, SO_ACCEPTFILTER_HTTPREADY)
+      end
+    end
+  end
+
+  def unicorn_client_init
+    self.sync = true
+    self.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) if defined?(TCP_NODELAY)
+    self.setsockopt(SOL_TCP, TCP_CORK, 1) if defined?(TCP_CORK)
+  end
+
+  def unicorn_peeraddr
+    Socket.unpack_sockaddr_in(getpeername)
+  end
+
+  class << self
+
+    def unicorn_tcp_server(host, port, backlog = 5)
+      s = new(AF_INET, SOCK_STREAM, 0)
+      s.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) if defined?(Fcntl::FD_CLOEXEC)
+      s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) if defined?(SO_REUSEADDR)
+      s.bind(pack_sockaddr_in(port, host))
+      s.listen(backlog)
+      s
+    end
+
+  end
+
+end
+
diff --git a/lib/unicorn/tcphack.rb b/lib/unicorn/tcphack.rb
deleted file mode 100644
index 634f9dd..0000000
--- a/lib/unicorn/tcphack.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-# Copyright (c) 2005 Zed A. Shaw
-# You can redistribute it and/or modify it under the same terms as Ruby.
-#
-# Additional work donated by contributors.  See http://mongrel.rubyforge.org/attributions.html
-# for more information.
-
-
-# A modification proposed by Sean Treadway that increases the default accept
-# queue of TCPServer to 1024 so that it handles more concurrent requests.
-class TCPServer
-   def initialize_with_backlog(*args)
-     initialize_without_backlog(*args)
-     listen(1024)
-   end
-
-   alias_method :initialize_without_backlog, :initialize
-   alias_method :initialize, :initialize_with_backlog
-end