Rainbows! 5.2.1
[rainbows.git] / lib / rainbows / http_server.rb
blobe925d80a2503d1a858e73517f264817b3aad35dc
1 # -*- encoding: binary -*-
2 # :enddoc:
4 class Rainbows::HttpServer < Unicorn::HttpServer
5   attr_accessor :copy_stream
6   attr_accessor :worker_connections
7   attr_accessor :keepalive_timeout
8   attr_accessor :client_header_buffer_size
9   attr_accessor :client_max_body_size
10   attr_reader :use
11   attr_reader :master_pid
13   def self.setup(block)
14     Rainbows.server.instance_eval(&block)
15   end
17   def initialize(app, options)
18     Rainbows.server = self
19     @logger = Unicorn::Configurator::DEFAULTS[:logger]
20     super(app, options)
21     defined?(@use) or self.use = Rainbows::Base
22     @worker_connections ||= 50
23     @worker_connections = 1 if @use == :Base
24   end
26   # Add one second to the timeout since our fchmod heartbeat is less
27   # precise (and must be more conservative) than Unicorn does.  We
28   # handle many clients per process and can't chmod on every
29   # connection we accept without wasting cycles.  That added to the
30   # fact that we let clients keep idle connections open for long
31   # periods of time means we have to chmod at a fixed interval.
32   def timeout=(nr)
33     @timeout = nr + 1
34   end
36   def load_config!
37     super
38     @worker_connections = 1 if @use == :Base
39   end
41   def worker_loop(worker)
42     Rainbows.forked = true
43     orig = method(:worker_loop)
44     extend(Rainbows.const_get(@use))
45     m = method(:worker_loop)
46     orig == m ? super(worker) : worker_loop(worker)
47   end
49   def spawn_missing_workers
50     # 5: std{in,out,err} + heartbeat FD + per-process listener
51     nofile = 5 + @worker_connections + LISTENERS.size
52     trysetrlimit(:RLIMIT_NOFILE, nofile)
54     case @use
55     when :ThreadSpawn, :ThreadPool, :ActorSpawn,
56          :CoolioThreadSpawn, :RevThreadSpawn,
57          :XEpollThreadSpawn, :WriterThreadPool, :WriterThreadSpawn
58       trysetrlimit(:RLIMIT_NPROC, @worker_connections + LISTENERS.size + 1)
59     when :XEpollThreadPool, :CoolioThreadPool
60       trysetrlimit(:RLIMIT_NPROC, Rainbows::O[:pool_size] + LISTENERS.size + 1)
61     end
62     super
63   end
65   def trysetrlimit(resource, want)
66     var = Process.const_get(resource)
67     cur, max = Process.getrlimit(var)
68     cur <= want and Process.setrlimit(var, cur = max > want ? max : want)
69     if cur == want
70       @logger.warn "#{resource} rlim_cur=#{cur} is barely enough"
71       @logger.warn "#{svc} may monopolize resources dictated by #{resource}" \
72                    " and leave none for your app"
73     end
74   rescue => e
75     @logger.error e.message
76     @logger.error "#{resource} needs to be increased to >=#{want} before" \
77                   " starting #{svc}"
78   end
80   def svc
81     File.basename($0)
82   end
84   def use=(mod)
85     @use = mod.to_s.split('::')[-1].to_sym
86     new_defaults = {
87       'rainbows.model' => @use,
88       'rack.multithread' => mod.to_s.include?('Thread'),
89       'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
90                                :EventMachine,:NeverBlock].include?(@use),
91     }
92     Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
93   end
95   def keepalive_requests=(nr)
96     Rainbows::HttpParser.keepalive_requests = nr
97   end
99   def keepalive_requests
100     Rainbows::HttpParser.keepalive_requests
101   end
103   def client_max_header_size=(bytes)
104     Unicorn::HttpParser.max_header_len = bytes
105   end