about summary refs log tree commit homepage
path: root/lib/unicorn/socket_helper.rb
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-06-30 19:16:43 -0700
committerEric Wong <normalperson@yhbt.net>2009-07-01 13:35:27 -0700
commit563d03f649ef31d2aec3505cbbed1e015493b8fc (patch)
treec08a5605671250f523c6ddc6e2e86d767aeac4b0 /lib/unicorn/socket_helper.rb
parentd247b5d95a3ad2de65cc909db21fdfbc6194b4c9 (diff)
downloadunicorn-563d03f649ef31d2aec3505cbbed1e015493b8fc.tar.gz
Now that we support tunnelling arbitrary protocols over HTTP as
well as "100 Continue" responses, TCP_NODELAY actually becomes
useful to us.  TCP_NODELAY is actually reasonably portable
nowadays; even.

While we're adding non-portable options, TCP_CORK/TCP_NOPUSH can
be enabled, too.  Unlike some other servers, these can't be
disabled explicitly/intelligently to force a flush, however.
However, these may still improve performance with "normal" HTTP
applications (Mongrel has always had TCP_CORK enabled in Linux).

While we're adding OS-specific features, we might as well
support TCP_DEFER_ACCEPT in Linux and FreeBSD the "httpready"
accept filter to prevent abuse.

These options can all be enabled on a per-listener basis.
Diffstat (limited to 'lib/unicorn/socket_helper.rb')
-rw-r--r--lib/unicorn/socket_helper.rb50
1 files changed, 49 insertions, 1 deletions
diff --git a/lib/unicorn/socket_helper.rb b/lib/unicorn/socket_helper.rb
index 850ad85..f8e3725 100644
--- a/lib/unicorn/socket_helper.rb
+++ b/lib/unicorn/socket_helper.rb
@@ -4,8 +4,56 @@ module Unicorn
   module SocketHelper
     include Socket::Constants
 
+    # 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)
+    when /freebsd(([1-4]\..{1,2})|5\.[0-4])/
+      # Do nothing for httpready, just closing a bug when freebsd <= 5.4
+      TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
+    when /freebsd/
+      TCP_NOPUSH = 4 unless defined?(TCP_NOPUSH)
+      # Use the HTTP accept filter if available.
+      # The struct made by pack() is defined in /usr/include/sys/socket.h
+      # as accept_filter_arg
+      # We won't be seupportin the "dataready" filter unlike nginx
+      # since we only support HTTP and no other protocols
+      unless `/sbin/sysctl -nq net.inet.accf.http`.empty?
+        HTTPREADY = ['httpready', nil].pack('a16a240').freeze
+      end
+    end
+
+    def set_tcp_sockopt(sock, opt)
+
+      # highly portable, but off by default because we don't do keepalive
+      if defined?(TCP_NODELAY) && ! (val = opt[:tcp_nodelay]).nil?
+        sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, val ? 1 : 0) rescue nil
+      end
+
+      unless (val = opt[:tcp_nopush]).nil?
+        val = val ? 1 : 0
+        if defined?(TCP_CORK) # Linux
+          sock.setsockopt(IPPROTO_TCP, TCP_CORK, val) rescue nil
+        elsif defined?(TCP_NOPUSH) # TCP_NOPUSH is untested (FreeBSD)
+          sock.setsockopt(IPPROTO_TCP, TCP_NOPUSH, val) rescue nil
+        end
+      end
+
+      # No good reason to ever have deferred accepts off
+      if defined?(TCP_DEFER_ACCEPT)
+        sock.setsockopt(SOL_TCP, TCP_DEFER_ACCEPT, 1) rescue nil
+      elsif defined?(SO_ACCEPTFILTER) && defined?(HTTPREADY)
+        sock.setsockopt(SOL_SOCKET, SO_ACCEPTFILTER, HTTPREADY) rescue nil
+      end
+    end
+
     def set_server_sockopt(sock, opt)
       opt ||= {}
+
+      TCPSocket === sock and set_tcp_sockopt(sock, opt)
+
       if opt[:rcvbuf] || opt[:sndbuf]
         log_buffer_sizes(sock, "before: ")
         sock.setsockopt(SOL_SOCKET, SO_RCVBUF, opt[:rcvbuf]) if opt[:rcvbuf]
@@ -25,7 +73,7 @@ module Unicorn
     # creates a new server, socket. address may be a HOST:PORT or
     # an absolute path to a UNIX socket.  address can even be a Socket
     # object in which case it is immediately returned
-    def bind_listen(address = '0.0.0.0:8080', opt = { :backlog => 1024 })
+    def bind_listen(address = '0.0.0.0:8080', opt = {})
       return address unless String === address
 
       sock = if address[0..0] == "/"