Skip to content

Commit

Permalink
Handle exceptions inside of body blocks that are run asynchronously
Browse files Browse the repository at this point in the history
  • Loading branch information
raggi committed Jun 20, 2010
1 parent 151cd5b commit 9509d5c
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 19 deletions.
9 changes: 9 additions & 0 deletions examples/basic.ru
Expand Up @@ -18,6 +18,15 @@ class AsyncTest < Sinatra::Base
raise 'boom'
end

aget '/araise' do
EM.add_timer(1) { body { raise "boom" } }
end

# This will blow up in thin currently
aget '/raise/die' do
EM.add_timer(1) { raise 'die' }
end

end

run AsyncTest.new
39 changes: 20 additions & 19 deletions lib/sinatra/async.rb
Expand Up @@ -71,18 +71,18 @@ def aroute(verb, path, opts = {}, &block) #:nodoc:
module Helpers
# Send the given body or block as the final response to the asynchronous
# request.
def body(*args, &blk)
b = super
def body(*args)
if @async_running
block_given? ? async_handle_exception { super yield } : super
request.env['async.callback'][
[response.status, response.headers, response.body]
]
else
b
super
end
end

# By default schedule_async calls EventMachine#next_tick, if you're using
# By default async_schedule calls EventMachine#next_tick, if you're using
# threads or some other scheduling mechanism, it must take the block
# passed here.
def async_schedule(&b)
Expand All @@ -102,29 +102,30 @@ def async_response
def async_runner(method, *bargs)
async_schedule do
@async_running = true
begin
h = catch(:halt) { __send__(method, *bargs); nil }
if h
async_handle_exception do
if h = catch(:halt) { __send__(method, *bargs); nil }
invoke { halt h }
invoke { error_block! response.status }
body(response.body)
end
rescue ::Exception => boom
if options.show_exceptions?
# HACK: handle_exception! re-raises the exception if show_exceptions?,
# so we ignore any errors and instead create a ShowExceptions page manually
handle_exception!(boom) rescue nil
s, h, b = Sinatra::ShowExceptions.new(proc{ raise boom }).call(request.env)
response.status = s
response.headers.replace(h)
body(b)
else
body(handle_exception!(boom))
end
end
end
end

def async_handle_exception
yield
rescue ::Exception => boom
if options.show_exceptions?
printer = Sinatra::ShowExceptions.new(proc{ raise boom })
s, h, b = printer.call(request.env)
response.status = s
response.headers.replace(h)
response.body = b
else
body(handle_exception!(boom))
end
end

# Asynchronous halt must be used when the halt is occuring outside of
# the original call stack.
def ahalt(*args)
Expand Down

0 comments on commit 9509d5c

Please sign in to comment.