From bc44990ddb16669030cf0ca72f48e1c6c620a4f2 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Thu, 5 Feb 2009 20:50:58 -0800 Subject: Move portability and override Socket stuff to unicorn/socket 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. --- lib/unicorn.rb | 40 +++++++------------------------------ lib/unicorn/socket.rb | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ lib/unicorn/tcphack.rb | 18 ----------------- 3 files changed, 60 insertions(+), 51 deletions(-) create mode 100644 lib/unicorn/socket.rb delete mode 100644 lib/unicorn/tcphack.rb (limited to 'lib') 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 -- cgit v1.2.3-24-ge0c7