Zbatery v0.3.1 - quiet EOF errors from clients
[zbatery.git] / lib / zbatery.rb
blobdc3654453b52f1f26f87893f5eca8fbbd17a5598
1 # -*- encoding: binary -*-
2 # :enddoc:
3 require 'rainbows'
5 module Zbatery
7   # current version of Zbatery
8   VERSION = "0.3.1"
10   class << self
12     # runs the Zbatery HttpServer with +app+ and +options+ and does
13     # not return until the server has exited.
14     def run(app, options = {})
15       Rainbows::HttpServer.new(app, options).start.join
16     end
17   end
19   Rainbows::Const::RACK_DEFAULTS["SERVER_SOFTWARE"] = "Zbatery #{VERSION}"
21   # true if our Ruby implementation supports unlinked files
22   UnlinkedIO = begin
23     tmp = Unicorn::Util.tmpio
24     tmp.chmod(0)
25     tmp.close
26     true
27   rescue
28     false
29   end
31   # we don't actually fork workers, but allow using the
32   # {before,after}_fork hooks found in Unicorn/Rainbows!
33   # config files...
34   FORK_HOOK = lambda { |_,_| }
36 end
38 # :stopdoc:
39 # override stuff we don't need or can't use portably
40 module Rainbows
42   module Base
43     # master == worker in our case
44     def init_worker_process(worker)
45       after_fork.call(self, worker)
46       worker.user(*user) if user.kind_of?(Array) && ! worker.switched
47       build_app! unless preload_app
48       Rainbows::Response.setup(self.class)
49       Rainbows::MaxBody.setup
51       # avoid spurious wakeups and blocking-accept() with 1.8 green threads
52       if RUBY_VERSION.to_f < 1.9
53         require "io/nonblock"
54         HttpServer::LISTENERS.each { |l| l.nonblock = true }
55       end
57       logger.info "Zbatery #@use worker_connections=#@worker_connections"
58     end
59   end
61   # we can't/don't need to do the fchmod heartbeat Unicorn/Rainbows! does
62   def G.tick
63     alive
64   end
66   class HttpServer
68     # this class is only used to avoid breaking Unicorn user switching
69     class DeadIO
70       def chown(*args); end
71     end
73     # only used if no concurrency model is specified
74     def worker_loop(worker)
75       init_worker_process(worker)
76       begin
77         ret = IO.select(LISTENERS, nil, nil, nil) and
78         ret.first.each do |sock|
79           begin
80             process_client(sock.accept_nonblock)
81           rescue Errno::EAGAIN, Errno::ECONNABORTED
82           end
83         end
84       rescue Errno::EINTR
85       rescue Errno::EBADF, TypeError
86         break
87       rescue => e
88         Rainbows::Error.listen_loop(e)
89       end while G.alive
90     end
92     # no-op
93     def maintain_worker_count; end
94     def init_self_pipe!; end
96     # can't just do a graceful exit if reopening logs fails, so we just
97     # continue on...
98     def reopen_logs
99       logger.info "reopening logs"
100       Unicorn::Util.reopen_logs
101       logger.info "done reopening logs"
102       rescue => e
103         logger.error "failed reopening logs #{e.message}"
104     end
106     def trap_deferred(sig)
107       # nothing
108     end
110     def join
111       begin
112         trap(:INT) { stop(false) } # Mongrel trapped INT for Win32...
114         # try these anyways regardless of platform...
115         trap(:TERM) { stop(false) }
116         trap(:QUIT) { stop }
117         trap(:USR1) { reopen_logs }
118         trap(:USR2) { reexec }
120         # no other way to reliably switch concurrency models...
121         trap(:HUP) { reexec; stop }
123         # technically feasible in some cases, just not sanely supportable:
124         %w(TTIN TTOU WINCH).each do |sig|
125           trap(sig) { logger.info "SIG#{sig} is not handled by Zbatery" }
126         end
127       rescue => e # hopefully ignores errors on Win32...
128         logger.error "failed to setup signal handler: #{e.message}"
129       end
131       if ready_pipe
132         ready_pipe.syswrite($$.to_s)
133         ready_pipe.close rescue nil
134         self.ready_pipe = nil
135       end
137       worker = Worker.new(0, DeadIO.new)
138       before_fork.call(self, worker)
139       worker_loop(worker) # runs forever
140     end
142     def stop(graceful = true)
143       Rainbows::G.quit!
144       exit!(0) unless graceful
145     end
147     def before_fork
148       hook = super
149       hook == Zbatery::FORK_HOOK or
150         logger.warn "calling before_fork without forking"
151       hook
152     end
154     def after_fork
155       hook = super
156       hook == Zbatery::FORK_HOOK or
157         logger.warn "calling after_fork without having forked"
158       hook
159     end
160   end
163 module Unicorn
165   class Configurator
166     DEFAULTS[:before_fork] = DEFAULTS[:after_fork] = Zbatery::FORK_HOOK
167   end
169   unless Zbatery::UnlinkedIO
170     require 'tempfile'
171     class Util
173       # Tempfiles should get automatically unlinked by GC
174       def self.tmpio
175         fp = Tempfile.new("zbatery")
176         fp.binmode
177         fp.sync = true
178         fp
179       end
180     end
181   end