# -*- encoding: binary -*- # This module adds \Rainbows! to the # {Unicorn::Configurator}[https://bogomips.org/unicorn/Unicorn/Configurator.html] # \Rainbows!-specific configuration options must be inside a the Rainbows! # block, otherwise Unicorn::Configurator directives may be used anywhere # in the file. # # Warning: The "timeout" directive in unicorn is far more dangerous # in Rainbows!, since ALL requests running on a process will be lost # on worker death, not just one. Instead, handle application-level # timeouts yourself: https://bogomips.org/unicorn/Application_Timeouts.html # # Rainbows! do # use :ThreadSpawn # concurrency model to use # worker_connections 400 # keepalive_timeout 0 # zero disables keepalives entirely # client_max_body_size 5*1024*1024 # 5 megabytes # keepalive_requests 666 # default:100 # client_header_buffer_size 2 * 1024 # 2 kilobytes # end # # # the rest of the Unicorn configuration... # worker_processes 8 # stderr_path "/path/to/error.log" # stdout_path "/path/to/output.log" module Rainbows::Configurator Unicorn::Configurator::DEFAULTS.merge!({ use: Rainbows::Base, worker_connections: 50, keepalive_timeout: 5, keepalive_requests: 100, client_max_body_size: 1024 * 1024, client_header_buffer_size: 1024, client_max_header_size: 112 * 1024, copy_stream: IO, }) # Configures \Rainbows! with a given concurrency model to +use+ and # a +worker_connections+ upper-bound. This method should be called # inside a Unicorn/\Rainbows! configuration file. # # All other methods in Rainbows::Configurator must be called # inside this block. def Rainbows!(&block) block_given? or raise ArgumentError, "Rainbows! requires a block" @block = true instance_eval(&block) ensure @block = false end def check! # :nodoc: @block or abort "must be inside a Rainbows! block" end # This limits the number of connected clients per-process. The total # number of clients on a server is +worker_processes+ * +worker_connections+. # # This option has no effect with the Base concurrency model, which is # limited to +1+. # # Default: 50 def worker_connections(clients) check! set_int(:worker_connections, clients, 1) end # Select a concurrency model for use with \Rainbows!. You must select # this with a Symbol (prefixed with ":"). Thus if you wish to select # the Rainbows::ThreadSpawn concurrency model, you would use: # # Rainbows! do # use :ThreadSpawn # end # # See the {Summary}[link:Summary.html] document for a summary of # supported concurrency models. +options+ may be specified for some # concurrency models, but the majority do not support them. # # Default: :Base (no concurrency) def use(model, *options) check! mod = begin Rainbows.const_get(model) rescue NameError => e warn "error loading #{model.inspect}: #{e}" e.backtrace.each { |l| warn l } abort "concurrency model #{model.inspect} not supported" end Module === mod or abort "concurrency model #{model.inspect} not supported" options.each do |opt| case opt when Hash Rainbows::O.merge!(opt) when Symbol Rainbows::O[opt] = true else abort "cannot handle option: #{opt.inspect} in #{options.inspect}" end end mod.setup if mod.respond_to?(:setup) set[:use] = mod end # Sets the value (in seconds) the server will wait for a client in # between requests. The default value should be enough under most # conditions for browsers to render the page and start retrieving # extra elements. # # Setting this value to +0+ disables keepalive entirely # # Default: 5 seconds def keepalive_timeout(seconds) check! set_int(:keepalive_timeout, seconds, 0) end # This limits the number of requests which can be made over a keep-alive # connection. This is used to prevent single client from monopolizing # the server and to improve fairness when load-balancing across multiple # machines by forcing a client to reconnect. This may be helpful # in mitigating some denial-of-service attacks. # # Default: 100 requests def keepalive_requests(count) check! case count when nil, Integer set[:keepalive_requests] = count else abort "not an integer or nil: keepalive_requests=#{count.inspect}" end end # Limits the maximum size of a request body for all requests. # Setting this to +nil+ disables the maximum size check. # # Default: 1 megabyte (1048576 bytes) # # If you want endpoint-specific upload limits and use a # "rack.input"-streaming concurrency model, see the Rainbows::MaxBody def client_max_body_size(bytes) check! err = "client_max_body_size must be nil or a non-negative Integer" case bytes when nil when Integer bytes >= 0 or abort err else abort err end set[:client_max_body_size] = bytes end # Limits the maximum size of a request header for all requests. # # Default: 112 kilobytes (114688 bytes) # # Lowering this will lower worst-case memory usage and mitigate some # denial-of-service attacks. This should be larger than # client_header_buffer_size. def client_max_header_size(bytes) check! set_int(:client_max_header_size, bytes, 8) end # This governs the amount of memory allocated for an individual read(2) or # recv(2) system call when reading headers. Applications that make minimal # use of cookies should not increase this from the default. # # Rails applications using session cookies may want to increase this to # 2048 bytes or more depending on expected request sizes. # # Increasing this will increase overall memory usage to your application, # as you will need at least this amount of memory for every connected client. # # Default: 1024 bytes def client_header_buffer_size(bytes) check! set_int(:client_header_buffer_size, bytes, 1) end # Allows overriding the +klass+ where the +copy_stream+ method is # used to do efficient copying of regular files, pipes, and sockets. # # This is only used with multi-threaded concurrency models: # # * ThreadSpawn # * ThreadPool # * WriterThreadSpawn # * WriterThreadPool # * XEpollThreadSpawn # * XEpollThreadPool # # Due to existing {bugs}[http://redmine.ruby-lang.org/search?q=copy_stream] # in the Ruby IO.copy_stream implementation, \Rainbows! uses the # "sendfile" RubyGem that instead of copy_stream to transfer regular files # to clients. The "sendfile" RubyGem also supports more operating systems, # and works with more concurrency models. # # Recent Linux 2.6 users may override this with "IO::Splice" from the # "io_splice" RubyGem: # # require "io/splice" # Rainbows! do # copy_stream IO::Splice # end # # Keep in mind that splice(2) itself is a relatively new system call # and has been buggy in many older Linux kernels. If you're proxying # the output of sockets to the client, be sure to use "io_splice" # 4.1.1 or later to avoid stalling responses. # # Default: IO on Ruby 1.9+, false otherwise def copy_stream(klass) check! if klass && ! klass.respond_to?(:copy_stream) abort "#{klass} must respond to `copy_stream' or be `false'" end set[:copy_stream] = klass end end # :enddoc: # inject the Rainbows! method into Unicorn::Configurator Unicorn::Configurator.__send__(:include, Rainbows::Configurator)