module Rack # Installs signal handlers that are safely processed after a request # # NOTE: This middleware should not be used in a threaded environment # # use Rack::Signals.new do # trap 'INT', lambda { # puts "Exiting now" # exit # } # # trap_when_ready 'USR1', lambda { # puts "Exiting when ready" # exit # } # end class Signals class BodyWithCallback def initialize(body, callback) @body, @callback = body, callback end def each(&block) @body.each(&block) @callback.call end end def initialize(app, &block) @app = app @processing = false @when_ready = nil instance_eval(&block) end def call(env) begin @processing, @when_ready = true, nil status, headers, body = @app.call(env) if handler = @when_ready body = BodyWithCallback.new(body, handler) @when_ready = nil end ensure @processing = false end [status, headers, body] end def trap_when_ready(signal, handler) when_ready_handler = lambda { |signal| if @processing @when_ready = lambda { handler.call(signal) } else handler.call(signal) end } trap(signal, when_ready_handler) end end end