diff options
author | Eric Wong <normalperson@yhbt.net> | 2009-10-26 01:57:56 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2009-10-26 02:26:22 -0700 |
commit | 8e9672d13ed07cc262894b4770c0b9c016359712 (patch) | |
tree | aafa88a841ade6f076c46af19aa203b5bd8f5d26 | |
parent | 9248ead0ae16091ea0c362930391834c0f32891c (diff) | |
download | rainbows-8e9672d13ed07cc262894b4770c0b9c016359712.tar.gz |
This is should be compatible with how the Thin webserver provides async callback support. See http://github.com/raggi/async_sinatra for the details
-rw-r--r-- | lib/rainbows/event_machine.rb | 15 | ||||
-rw-r--r-- | t/async_sinatra.ru | 13 | ||||
-rwxr-xr-x | t/t0300-async_sinatra.sh | 65 |
3 files changed, 91 insertions, 2 deletions
diff --git a/lib/rainbows/event_machine.rb b/lib/rainbows/event_machine.rb index 678a771..7009fce 100644 --- a/lib/rainbows/event_machine.rb +++ b/lib/rainbows/event_machine.rb @@ -31,6 +31,9 @@ module Rainbows include Rainbows::EvCore G = Rainbows::G + # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ] + ASYNC_CALLBACK = 'async.callback'.freeze + def initialize(io) @_io = io end @@ -48,7 +51,15 @@ module Rainbows (@env[RACK_INPUT] = @input).rewind alive = @hp.keepalive? @env[REMOTE_ADDR] = @remote_addr - response = G.app.call(@env.update(RACK_DEFAULTS)) + @env[ASYNC_CALLBACK] = @response_write ||= method(:response_write) + + response = catch(:async) { G.app.call(@env.update(RACK_DEFAULTS)) } + + # too tricky to support pipelining with :async since the + # second (pipelined) request could be a stuck behind a + # long-running async response + (response.nil? || -1 == response.first) and return @state = :close + alive &&= G.alive out = [ alive ? CONN_ALIVE : CONN_CLOSE ] if @hp.headers? response_write(response, out, alive) @@ -64,7 +75,7 @@ module Rainbows end while true end - def response_write(response, out, alive) + def response_write(response, out = [], alive = false) body = response.last unless body.respond_to?(:to_path) HttpResponse.write(self, response, out) diff --git a/t/async_sinatra.ru b/t/async_sinatra.ru new file mode 100644 index 0000000..94a24b1 --- /dev/null +++ b/t/async_sinatra.ru @@ -0,0 +1,13 @@ +# See http://github.com/raggi/async_sinatra +# gem install async_sinatra -v0.1.5 +require 'sinatra/async' + +class AsyncTest < Sinatra::Base + register Sinatra::Async + + aget '/:n' do |n| + EM.add_timer(n.to_i) { body { "delayed for #{n} seconds\n" } } + end +end + +run AsyncTest.new diff --git a/t/t0300-async_sinatra.sh b/t/t0300-async_sinatra.sh new file mode 100755 index 0000000..3cf729c --- /dev/null +++ b/t/t0300-async_sinatra.sh @@ -0,0 +1,65 @@ +#!/bin/sh +. ./test-lib.sh + +# n - number of seconds to sleep +n=10 +CONFIG_RU=async_sinatra.ru +case $model in +EventMachine) ;; +*) + t_info "skipping $T since it's not compatible with $model" + exit 0 + ;; +esac + +t_plan 7 "async_sinatra test for EM" + +t_begin "setup and start" && { + rainbows_setup + rtmpfiles a b c curl_err + + # Async Sinatra does not support Rack::Lint + rainbows -E none -D $CONFIG_RU -c $unicorn_config + rainbows_wait_start +} + +t_begin "send async requests off in parallel" && { + t0=$(date +%s) + ( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | utee $a) & + ( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | utee $b) & + ( curl --no-buffer -sSf http://$listen/$n 2>> $curl_err | utee $c) & +} + +t_begin "ensure elapsed requests were processed in parallel" && { + wait + t1=$(date +%s) + elapsed=$(( $t1 - $t0 )) + echo "elapsed=$elapsed < 30" + test $elapsed -lt 30 +} + +t_begin "termination signal sent" && { + kill $rainbows_pid +} + +dbgcat a +dbgcat b +dbgcat c +dbgcat r_err +dbgcat curl_err + +t_begin "no errors from curl" && { + test ! -s $curl_err +} + +t_begin "no errors in stderr" && check_stderr + +dbgcat r_err + +t_begin "no responses are chunked" && { + test x"$(cat $a)" = x"delayed for $n seconds" + test x"$(cat $b)" = x"delayed for $n seconds" + test x"$(cat $c)" = x"delayed for $n seconds" +} + +t_done |