about summary refs log tree commit homepage
path: root/lib/unicorn/configurator.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/unicorn/configurator.rb')
-rw-r--r--lib/unicorn/configurator.rb332
1 files changed, 209 insertions, 123 deletions
diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb
index fb37c56..b6ad022 100644
--- a/lib/unicorn/configurator.rb
+++ b/lib/unicorn/configurator.rb
@@ -8,14 +8,26 @@ require 'logger'
 # example configuration files.  An example config file for use with
 # nginx is also available at
 # http://unicorn.bogomips.org/examples/nginx.conf
-class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
+#
+# See the link:/TUNING.html document for more information on tuning unicorn.
+class Unicorn::Configurator
+  include Unicorn
+
+  # :stopdoc:
+  attr_accessor :set, :config_file, :after_reload
+
   # used to stash stuff for deferred processing of cli options in
   # config.ru after "working_directory" is bound.  Do not rely on
   # this being around later on...
-  RACKUP = {} # :nodoc:
+  RACKUP = {
+    :daemonize => false,
+    :host => Unicorn::Const::DEFAULT_HOST,
+    :port => Unicorn::Const::DEFAULT_PORT,
+    :set_listener => false,
+    :options => { :listeners => [] }
+  }
 
   # Default settings for Unicorn
-  # :stopdoc:
   DEFAULTS = {
     :timeout => 60,
     :logger => Logger.new($stderr),
@@ -31,6 +43,9 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
       },
     :pid => nil,
     :preload_app => false,
+    :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
+    :client_body_buffer_size => Unicorn::Const::MAX_BODY,
+    :trust_x_forwarded => true,
   }
   #:startdoc:
 
@@ -58,6 +73,9 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
 
     parse_rackup_file
 
+    RACKUP[:set_listener] and
+      set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
+
     # unicorn_rails creates dirs here after working_directory is bound
     after_reload.call if after_reload
 
@@ -87,20 +105,24 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
     set[key]
   end
 
-  # sets object to the +new+ Logger-like object.  The new logger-like
+  # sets object to the +obj+ Logger-like object.  The new Logger-like
   # object must respond to the following methods:
-  #  +debug+, +info+, +warn+, +error+, +fatal+
+  # * debug
+  # * info
+  # * warn
+  # * error
+  # * fatal
   # The default Logger will log its output to the path specified
   # by +stderr_path+.  If you're running Unicorn daemonized, then
   # you must specify a path to prevent error messages from going
   # to /dev/null.
-  def logger(new)
+  def logger(obj)
     %w(debug info warn error fatal).each do |m|
-      new.respond_to?(m) and next
-      raise ArgumentError, "logger=#{new} does not respond to method=#{m}"
+      obj.respond_to?(m) and next
+      raise ArgumentError, "logger=#{obj} does not respond to method=#{m}"
     end
 
-    set[:logger] = new
+    set[:logger] = obj
   end
 
   # sets after_fork hook to a given block.  This block will be called by
@@ -116,11 +138,6 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
   #    # Existing options for Unicorn::Configurator#listen such as
   #    # :backlog, :rcvbuf, :sndbuf are available here as well.
   #    server.listen(addr, :tries => -1, :delay => 5, :backlog => 128)
-  #
-  #    # drop permissions to "www-data" in the worker
-  #    # generally there's no reason to start Unicorn as a priviledged user
-  #    # as it is not recommended to expose Unicorn to public clients.
-  #    worker.user('www-data', 'www-data') if Process.euid == 0
   #  end
   def after_fork(*args, &block)
     set_hook(:after_fork, block_given? ? block : args[0])
@@ -168,11 +185,7 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
   #      server 192.168.0.9:8080 fail_timeout=0;
   #    }
   def timeout(seconds)
-    Numeric === seconds or raise ArgumentError,
-                                "not numeric: timeout=#{seconds.inspect}"
-    seconds >= 3 or raise ArgumentError,
-                                "too low: timeout=#{seconds.inspect}"
-    set[:timeout] = seconds
+    set_int(:timeout, seconds, 3)
   end
 
   # sets the current number of worker_processes to +nr+.  Each worker
@@ -182,11 +195,7 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
   # the rest of your Unicorn configuration.  See the SIGNALS document
   # for more information.
   def worker_processes(nr)
-    Integer === nr or raise ArgumentError,
-                           "not an integer: worker_processes=#{nr.inspect}"
-    nr >= 0 or raise ArgumentError,
-                           "not non-negative: worker_processes=#{nr.inspect}"
-    set[:worker_processes] = nr
+    set_int(:worker_processes, nr, 1)
   end
 
   # sets listeners to the given +addresses+, replacing or augmenting the
@@ -200,131 +209,165 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
     set[:listeners] = addresses
   end
 
-  # adds an +address+ to the existing listener set.
+  # Adds an +address+ to the existing listener set.  May be specified more
+  # than once.  +address+ may be an Integer port number for a TCP port, an
+  # "IP_ADDRESS:PORT" for TCP listeners or a pathname for UNIX domain sockets.
+  #
+  #   listen 3000 # listen to port 3000 on all TCP interfaces
+  #   listen "127.0.0.1:3000"  # listen to port 3000 on the loopback interface
+  #   listen "/tmp/.unicorn.sock" # listen on the given Unix domain socket
+  #   listen "[::1]:3000" # listen to port 3000 on the IPv6 loopback interface
   #
   # The following options may be specified (but are generally not needed):
   #
-  # +:backlog+: this is the backlog of the listen() syscall.
+  # [:backlog => number of clients]
+  #
+  #   This is the backlog of the listen() syscall.
+  #
+  #   Some operating systems allow negative values here to specify the
+  #   maximum allowable value.  In most cases, this number is only
+  #   recommendation and there are other OS-specific tunables and
+  #   variables that can affect this number.  See the listen(2)
+  #   syscall documentation of your OS for the exact semantics of
+  #   this.
+  #
+  #   If you are running unicorn on multiple machines, lowering this number
+  #   can help your load balancer detect when a machine is overloaded
+  #   and give requests to a different machine.
+  #
+  #   Default: 1024
+  #
+  # [:rcvbuf => bytes, :sndbuf => bytes]
+  #
+  #   Maximum receive and send buffer sizes (in bytes) of sockets.
+  #
+  #   These correspond to the SO_RCVBUF and SO_SNDBUF settings which
+  #   can be set via the setsockopt(2) syscall.  Some kernels
+  #   (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
+  #   there is no need (and it is sometimes detrimental) to specify them.
+  #
+  #   See the socket API documentation of your operating system
+  #   to determine the exact semantics of these settings and
+  #   other operating system-specific knobs where they can be
+  #   specified.
+  #
+  #   Defaults: operating system defaults
+  #
+  # [:tcp_nodelay => true or false]
+  #
+  #   Disables Nagle's algorithm on TCP sockets if +true+.
   #
-  # Some operating systems allow negative values here to specify the
-  # maximum allowable value.  In most cases, this number is only
-  # recommendation and there are other OS-specific tunables and
-  # variables that can affect this number.  See the listen(2)
-  # syscall documentation of your OS for the exact semantics of
-  # this.
+  #   Setting this to +true+ can make streaming responses in Rails 3.1
+  #   appear more quickly at the cost of slightly higher bandwidth usage.
+  #   The effect of this option is most visible if nginx is not used,
+  #   but nginx remains highly recommended with \Unicorn.
   #
-  # If you are running unicorn on multiple machines, lowering this number
-  # can help your load balancer detect when a machine is overloaded
-  # and give requests to a different machine.
+  #   This has no effect on UNIX sockets.
   #
-  # Default: 1024
+  #   Default: +false+ (Nagle's algorithm enabled) in \Unicorn,
+  #   +true+ in Rainbows!
   #
-  # +:rcvbuf+, +:sndbuf+: maximum receive and send buffer sizes of sockets
+  # [:tcp_nopush => true or false]
   #
-  # These correspond to the SO_RCVBUF and SO_SNDBUF settings which
-  # can be set via the setsockopt(2) syscall.  Some kernels
-  # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and
-  # there is no need (and it is sometimes detrimental) to specify them.
+  #   Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
   #
-  # See the socket API documentation of your operating system
-  # to determine the exact semantics of these settings and
-  # other operating system-specific knobs where they can be
-  # specified.
+  #   This prevents partial TCP frames from being sent out and reduces
+  #   wakeups in nginx if it is on a different machine.  Since \Unicorn
+  #   is only designed for applications that send the response body
+  #   quickly without keepalive, sockets will always be flushed on close
+  #   to prevent delays.
   #
-  # Defaults: operating system defaults
+  #   This has no effect on UNIX sockets.
   #
-  # +:tcp_nodelay+: disables Nagle's algorithm on TCP sockets
+  #   Default: +true+ in \Unicorn 3.4+, +false+ in Rainbows!
   #
-  # This has no effect on UNIX sockets.
+  # [:tries => Integer]
   #
-  # Default: operating system defaults (usually Nagle's algorithm enabled)
+  #   Times to retry binding a socket if it is already in use
   #
-  # +:tcp_nopush+: enables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
+  #   A negative number indicates we will retry indefinitely, this is
+  #   useful for migrations and upgrades when individual workers
+  #   are binding to different ports.
   #
-  # This will prevent partial TCP frames from being sent out.
-  # Enabling +tcp_nopush+ is generally not needed or recommended as
-  # controlling +tcp_nodelay+ already provides sufficient latency
-  # reduction whereas Unicorn does not know when the best times are
-  # for flushing corked sockets.
+  #   Default: 5
   #
-  # This has no effect on UNIX sockets.
+  # [:delay => seconds]
   #
-  # +:tries+: times to retry binding a socket if it is already in use
+  #   Seconds to wait between successive +tries+
   #
-  # A negative number indicates we will retry indefinitely, this is
-  # useful for migrations and upgrades when individual workers
-  # are binding to different ports.
+  #   Default: 0.5 seconds
   #
-  # Default: 5
+  # [:umask => mode]
   #
-  # +:delay+: seconds to wait between successive +tries+
+  #   Sets the file mode creation mask for UNIX sockets.  If specified,
+  #   this is usually in octal notation.
   #
-  # Default: 0.5 seconds
+  #   Typically UNIX domain sockets are created with more liberal
+  #   file permissions than the rest of the application.  By default,
+  #   we create UNIX domain sockets to be readable and writable by
+  #   all local users to give them the same accessibility as
+  #   locally-bound TCP listeners.
   #
-  # +:umask+: sets the file mode creation mask for UNIX sockets
+  #   This has no effect on TCP listeners.
   #
-  # Typically UNIX domain sockets are created with more liberal
-  # file permissions than the rest of the application.  By default,
-  # we create UNIX domain sockets to be readable and writable by
-  # all local users to give them the same accessibility as
-  # locally-bound TCP listeners.
+  #   Default: 0000 (world-read/writable)
   #
-  # This has no effect on TCP listeners.
+  # [:tcp_defer_accept => Integer]
   #
-  # Default: 0 (world read/writable)
+  #   Defer accept() until data is ready (Linux-only)
   #
-  # +:tcp_defer_accept:+ defer accept() until data is ready (Linux-only)
+  #   For Linux 2.6.32 and later, this is the number of retransmits to
+  #   defer an accept() for if no data arrives, but the client will
+  #   eventually be accepted after the specified number of retransmits
+  #   regardless of whether data is ready.
   #
-  # For Linux 2.6.32 and later, this is the number of retransmits to
-  # defer an accept() for if no data arrives, but the client will
-  # eventually be accepted after the specified number of retransmits
-  # regardless of whether data is ready.
+  #   For Linux before 2.6.32, this is a boolean option, and
+  #   accepts are _always_ deferred indefinitely if no data arrives.
+  #   This is similar to <code>:accept_filter => "dataready"</code>
+  #   under FreeBSD.
   #
-  # For Linux before 2.6.32, this is a boolean option, and
-  # accepts are _always_ deferred indefinitely if no data arrives.
-  # This is similar to <code>:accept_filter => "dataready"</code>
-  # under FreeBSD.
+  #   Specifying +true+ is synonymous for the default value(s) below,
+  #   and +false+ or +nil+ is synonymous for a value of zero.
   #
-  # Specifying +true+ is synonymous for the default value(s) below,
-  # and +false+ or +nil+ is synonymous for a value of zero.
+  #   A value of +1+ is a good optimization for local networks
+  #   and trusted clients.  For Rainbows! and Zbatery users, a higher
+  #   value (e.g. +60+) provides more protection against some
+  #   denial-of-service attacks.  There is no good reason to ever
+  #   disable this with a +zero+ value when serving HTTP.
   #
-  # A value of +1+ is a good optimization for local networks
-  # and trusted clients.  For Rainbows! and Zbatery users, a higher
-  # value (e.g. +60+) provides more protection against some
-  # denial-of-service attacks.  There is no good reason to ever
-  # disable this with a +zero+ value when serving HTTP.
+  #   Default: 1 retransmit for \Unicorn, 60 for Rainbows! 0.95.0\+
   #
-  # Default: 1 retransmit for \Unicorn, 60 for Rainbows! 0.95.0\+
+  # [:accept_filter => String]
   #
-  # +:accept_filter: defer accept() until data is ready (FreeBSD-only)
+  #   defer accept() until data is ready (FreeBSD-only)
   #
-  # This enables either the "dataready" or (default) "httpready"
-  # accept() filter under FreeBSD.  This is intended as an
-  # optimization to reduce context switches with common GET/HEAD
-  # requests.  For Rainbows! and Zbatery users, this provides
-  # some protection against certain denial-of-service attacks, too.
+  #   This enables either the "dataready" or (default) "httpready"
+  #   accept() filter under FreeBSD.  This is intended as an
+  #   optimization to reduce context switches with common GET/HEAD
+  #   requests.  For Rainbows! and Zbatery users, this provides
+  #   some protection against certain denial-of-service attacks, too.
   #
-  # There is no good reason to change from the default.
+  #   There is no good reason to change from the default.
   #
-  # Default: "httpready"
-  def listen(address, opt = {})
+  #   Default: "httpready"
+  def listen(address, options = {})
     address = expand_addr(address)
     if String === address
       [ :umask, :backlog, :sndbuf, :rcvbuf, :tries ].each do |key|
-        value = opt[key] or next
+        value = options[key] or next
         Integer === value or
           raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
       end
       [ :tcp_nodelay, :tcp_nopush ].each do |key|
-        (value = opt[key]).nil? and next
+        (value = options[key]).nil? and next
         TrueClass === value || FalseClass === value or
           raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
       end
-      unless (value = opt[:delay]).nil?
+      unless (value = options[:delay]).nil?
         Numeric === value or
           raise ArgumentError, "not numeric: delay=#{value.inspect}"
       end
-      set[:listener_opts][address].merge!(opt)
+      set[:listener_opts][address].merge!(options)
     end
 
     set[:listeners] << address
@@ -362,12 +405,30 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
   # cause the master process to exit with an error.
 
   def preload_app(bool)
-    case bool
-    when TrueClass, FalseClass
-      set[:preload_app] = bool
-    else
-      raise ArgumentError, "preload_app=#{bool.inspect} not a boolean"
-    end
+    set_bool(:preload_app, bool)
+  end
+
+  # Toggles making \env[\"rack.input\"] rewindable.
+  # Disabling rewindability can improve performance by lowering
+  # I/O and memory usage for applications that accept uploads.
+  # Keep in mind that the Rack 1.x spec requires
+  # \env[\"rack.input\"] to be rewindable, so this allows
+  # intentionally violating the current Rack 1.x spec.
+  #
+  # +rewindable_input+ defaults to +true+ when used with Rack 1.x for
+  # Rack conformance.  When Rack 2.x is finalized, this will most
+  # likely default to +false+ while still conforming to the newer
+  # (less demanding) spec.
+  def rewindable_input(bool)
+    set_bool(:rewindable_input, bool)
+  end
+
+  # The maximum size (in +bytes+) to buffer in memory before
+  # resorting to a temporary file.  Default is 112 kilobytes.
+  # This option has no effect if "rewindable_input" is set to
+  # +false+.
+  def client_body_buffer_size(bytes)
+    set_int(:client_body_buffer_size, bytes, 0)
   end
 
   # Allow redirecting $stderr to a given path.  Unlike doing this from
@@ -417,6 +478,7 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
   # The master process always stays running as the user who started it.
   # This switch will occur after calling the after_fork hook, and only
   # if the Worker#user method is not called in the after_fork hook
+  # +group+ is optional and will not change if unspecified.
   def user(user, group = nil)
     # raises ArgumentError on invalid user/group
     Etc.getpwnam(user)
@@ -424,10 +486,22 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
     set[:user] = [ user, group ]
   end
 
+  # Sets whether or not the parser will trust X-Forwarded-Proto and
+  # X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
+  # Rainbows!/Zbatery installations facing untrusted clients directly
+  # should set this to +false+.  This is +true+ by default as Unicorn
+  # is designed to only sit behind trusted nginx proxies.
+  #
+  # This has never been publically documented and is subject to removal
+  # in future releases.
+  def trust_x_forwarded(bool) # :nodoc:
+    set_bool(:trust_x_forwarded, bool)
+  end
+
   # expands "unix:path/to/foo" to a socket relative to the current path
   # expands pathnames of sockets if relative to "~" or "~username"
   # expands "*:port and ":port" to "0.0.0.0:port"
-  def expand_addr(address) #:nodoc
+  def expand_addr(address) #:nodoc:
     return "0.0.0.0:#{address}" if Integer === address
     return address unless String === address
 
@@ -438,16 +512,27 @@ class Unicorn::Configurator < Struct.new(:set, :config_file, :after_reload)
       File.expand_path(address)
     when %r{\A(?:\*:)?(\d+)\z}
       "0.0.0.0:#$1"
-    when %r{\A(.*):(\d+)\z}
-      # canonicalize the name
-      packed = Socket.pack_sockaddr_in($2.to_i, $1)
-      Socket.unpack_sockaddr_in(packed).reverse!.join(':')
+    when %r{\A\[([a-fA-F0-9:]+)\]\z}, %r/\A((?:\d+\.){3}\d+)\z/
+      canonicalize_tcp($1, 80)
+    when %r{\A\[([a-fA-F0-9:]+)\]:(\d+)\z}, %r{\A(.*):(\d+)\z}
+      canonicalize_tcp($1, $2.to_i)
     else
       address
     end
   end
 
 private
+  def set_int(var, n, min) #:nodoc:
+    Integer === n or raise ArgumentError, "not an integer: #{var}=#{n.inspect}"
+    n >= min or raise ArgumentError, "too low (< #{min}): #{var}=#{n.inspect}"
+    set[var] = n
+  end
+
+  def canonicalize_tcp(addr, port)
+    packed = Socket.pack_sockaddr_in(port, addr)
+    port, addr = Socket.unpack_sockaddr_in(packed)
+    /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
+  end
 
   def set_path(var, path) #:nodoc:
     case path
@@ -458,6 +543,15 @@ private
     end
   end
 
+  def set_bool(var, bool) #:nodoc:
+    case bool
+    when true, false
+      set[var] = bool
+    else
+      raise ArgumentError, "#{var}=#{bool.inspect} not a boolean"
+    end
+  end
+
   def set_hook(var, my_proc, req_arity = 2) #:nodoc:
     case my_proc
     when Proc
@@ -495,23 +589,15 @@ private
     /^#\\(.*)/ =~ File.read(ru) or return
     RACKUP[:optparse].parse!($1.split(/\s+/))
 
-    # XXX ugly as hell, WILL FIX in 2.x (along with Rainbows!/Zbatery)
-    host, port, set_listener, options, daemonize =
-                    eval("[ host, port, set_listener, options, daemonize ]",
-                         TOPLEVEL_BINDING)
-
-    # XXX duplicate code from bin/unicorn{,_rails}
-    set[:listeners] << "#{host}:#{port}" if set_listener
-
-    if daemonize
+    if RACKUP[:daemonize]
       # unicorn_rails wants a default pid path, (not plain 'unicorn')
       if after_reload
         spid = set[:pid]
         pid('tmp/pids/unicorn.pid') if spid.nil? || spid == :unset
       end
       unless RACKUP[:daemonized]
-        Unicorn::Launcher.daemonize!(options)
-        RACKUP[:ready_pipe] = options.delete(:ready_pipe)
+        Unicorn::Launcher.daemonize!(RACKUP[:options])
+        RACKUP[:ready_pipe] = RACKUP[:options].delete(:ready_pipe)
       end
     end
   end