1 # -*- encoding: binary -*-
4 class Rainbows::HttpServer < Unicorn::HttpServer
6 Rainbows.server.instance_eval(&block)
9 def initialize(app, options)
10 Rainbows.server = self
11 @logger = Unicorn::Configurator::DEFAULTS[:logger]
13 defined?(@use) or use(:Base)
14 @worker_connections ||= @use == :Base ? 1 : 50
17 def reopen_worker_logs(worker_nr)
18 logger.info "worker=#{worker_nr} reopening logs..."
19 Unicorn::Util.reopen_logs
20 logger.info "worker=#{worker_nr} done reopening logs"
22 Rainbows.quit! # let the master reopen and refork us
25 # Add one second to the timeout since our fchmod heartbeat is less
26 # precise (and must be more conservative) than Unicorn does. We
27 # handle many clients per process and can't chmod on every
28 # connection we accept without wasting cycles. That added to the
29 # fact that we let clients keep idle connections open for long
30 # periods of time means we have to chmod at a fixed interval.
38 @worker_connections = nil
40 @worker_connections ||= @use == :Base ? 1 : 50
43 def worker_loop(worker)
44 orig = method(:worker_loop)
45 extend(Rainbows.const_get(@use))
46 m = method(:worker_loop)
47 orig == m ? super(worker) : worker_loop(worker)
50 def spawn_missing_workers
51 # 5: std{in,out,err} + heartbeat FD + per-process listener
52 nofile = 5 + @worker_connections + LISTENERS.size
53 trysetrlimit(:RLIMIT_NOFILE, nofile)
56 when :ThreadSpawn, :ThreadPool, :ActorSpawn,
57 :CoolioThreadSpawn, :RevThreadSpawn,
58 :XEpollThreadSpawn, :WriterThreadPool, :WriterThreadSpawn
59 trysetrlimit(:RLIMIT_NPROC, @worker_connections + LISTENERS.size + 1)
64 def trysetrlimit(resource, want)
65 var = Process.const_get(resource)
66 cur, max = Process.getrlimit(var)
67 cur <= want and Process.setrlimit(var, cur = max > want ? max : want)
69 @logger.warn "#{resource} rlim_cur=#{cur} is barely enough"
70 @logger.warn "#{svc} may monopolize resources dictated by #{resource}" \
71 " and leave none for your app"
74 @logger.error e.message
75 @logger.error "#{resource} needs to be increased to >=#{want} before" \
84 model = args.shift or return @use
86 Rainbows.const_get(model)
88 logger.error "error loading #{model.inspect}: #{e}"
89 e.backtrace.each { |l| logger.error l }
90 raise ArgumentError, "concurrency model #{model.inspect} not supported"
94 raise ArgumentError, "concurrency model #{model.inspect} not supported"
97 when Hash; Rainbows::O.update(opt)
98 when Symbol; Rainbows::O[opt] = true
99 else; raise ArgumentError, "can't handle option: #{opt.inspect}"
102 mod.setup if mod.respond_to?(:setup)
104 'rainbows.model' => (@use = model.to_sym),
105 'rack.multithread' => !!(model.to_s =~ /Thread/),
106 'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
107 :EventMachine,:NeverBlock].include?(@use),
109 Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
112 def worker_connections(*args)
113 return @worker_connections if args.empty?
115 (Integer === nr && nr > 0) or
116 raise ArgumentError, "worker_connections must be a positive Integer"
117 @worker_connections = nr
120 def keepalive_timeout(nr)
121 (Integer === nr && nr >= 0) or
122 raise ArgumentError, "keepalive_timeout must be a non-negative Integer"
123 Rainbows.keepalive_timeout = nr
126 def keepalive_requests(nr)
128 raise ArgumentError, "keepalive_requests must be a non-negative Integer"
129 Unicorn::HttpRequest.keepalive_requests = nr
132 def client_max_body_size(nr)
133 err = "client_max_body_size must be nil or a non-negative Integer"
137 nr >= 0 or raise ArgumentError, err
139 raise ArgumentError, err
141 Rainbows.client_max_body_size = nr
144 def client_header_buffer_size(bytes)
145 Integer === bytes && bytes > 0 or raise ArgumentError,
146 "client_header_buffer_size must be a positive Integer"
147 Rainbows.client_header_buffer_size = bytes