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