rainbows 0.90.0 → 0.90.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Documentation/rainbows.1.txt +6 -8
- data/GIT-VERSION-GEN +1 -1
- data/TODO +3 -2
- data/lib/rainbows.rb +12 -0
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/ev_core.rb +2 -0
- data/lib/rainbows/event_machine.rb +14 -3
- data/lib/rainbows/thread_pool.rb +1 -2
- data/lib/rainbows/thread_spawn.rb +2 -3
- data/rainbows.gemspec +1 -2
- data/t/async_examples/README +3 -0
- data/t/async_examples/async_app.ru +126 -0
- data/t/async_examples/async_tailer.ru +105 -0
- data/t/t0012-spurious-wakeups-quiet.sh +41 -0
- data/t/t0400-em-async-app.sh +57 -0
- data/t/t0401-em-async-tailer.sh +74 -0
- metadata +11 -4
- data/rainbows.1 +0 -0
@@ -12,10 +12,9 @@ rainbows [-c CONFIG_FILE] [-E RACK_ENV] [-D] [RACKUP_FILE]
|
|
12
12
|
|
13
13
|
# DESCRIPTION
|
14
14
|
|
15
|
-
A rackup(1)-like command to launch Rack applications using
|
16
|
-
|
17
|
-
|
18
|
-
RACKUP_FILE.
|
15
|
+
A rackup(1)-like command to launch Rack applications using Rainbows!.
|
16
|
+
It is expected to be started in your application root (APP_ROOT),
|
17
|
+
but the "working_directory" directive may be used in the CONFIG_FILE.
|
19
18
|
|
20
19
|
While Rainbows! takes a myriad of command-line options for
|
21
20
|
compatibility with ruby(1) and rackup(1), it is recommended to stick
|
@@ -34,10 +33,9 @@ with rackup(1) but strongly discouraged.
|
|
34
33
|
# UNICORN OPTIONS
|
35
34
|
-c, \--config-file CONFIG_FILE
|
36
35
|
: Path to the Unicorn-specific config file. The config file is
|
37
|
-
implemented as a Ruby DSL, so Ruby code may executed
|
38
|
-
|
39
|
-
|
40
|
-
directives available from the DSL.
|
36
|
+
implemented as a Ruby DSL, so Ruby code may executed.
|
37
|
+
See the RDoc/ri for the *Unicorn::Configurator* class for the full
|
38
|
+
list of directives available from the DSL.
|
41
39
|
|
42
40
|
-D, \--daemonize
|
43
41
|
: Run daemonized in the background. The process is detached from
|
data/GIT-VERSION-GEN
CHANGED
data/TODO
CHANGED
@@ -3,14 +3,15 @@
|
|
3
3
|
We're lazy and pick the easy items to do first, then the ones people
|
4
4
|
care about.
|
5
5
|
|
6
|
+
* Split out NeverBlock into NeverBlockEventMachine and NeverBlockReactor
|
7
|
+
NeverBlock will default to one of them (depending on NB upstream).
|
8
|
+
|
6
9
|
* Improve test suite coverage. We won't waste cycles with puny
|
7
10
|
unit tests, only integration tests that exercise externally
|
8
11
|
visible parts.
|
9
12
|
|
10
13
|
* EventMachine.spawn - should be like Revactor, maybe?
|
11
14
|
|
12
|
-
* EventMachine deferrables
|
13
|
-
|
14
15
|
* conditional app.deferred?(env) support
|
15
16
|
Merb uses it, some other servers support it
|
16
17
|
|
data/lib/rainbows.rb
CHANGED
@@ -60,6 +60,13 @@ module Rainbows
|
|
60
60
|
|
61
61
|
# returns nil if accept fails
|
62
62
|
if defined?(Fcntl::FD_CLOEXEC)
|
63
|
+
def sync_accept(sock)
|
64
|
+
rv = sock.accept
|
65
|
+
rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
66
|
+
rv
|
67
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
|
68
|
+
end
|
69
|
+
|
63
70
|
def accept(sock)
|
64
71
|
rv = sock.accept_nonblock
|
65
72
|
rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
|
@@ -67,6 +74,11 @@ module Rainbows
|
|
67
74
|
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
68
75
|
end
|
69
76
|
else
|
77
|
+
def sync_accept(sock)
|
78
|
+
sock.accept
|
79
|
+
rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
|
80
|
+
end
|
81
|
+
|
70
82
|
def accept(sock)
|
71
83
|
sock.accept_nonblock
|
72
84
|
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
data/lib/rainbows/const.rb
CHANGED
data/lib/rainbows/ev_core.rb
CHANGED
@@ -11,6 +11,8 @@ module Rainbows
|
|
11
11
|
# Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
|
12
12
|
ASYNC_CALLBACK = "async.callback".freeze
|
13
13
|
|
14
|
+
ASYNC_CLOSE = "async.close".freeze
|
15
|
+
|
14
16
|
def post_init
|
15
17
|
@remote_addr = ::TCPSocket === @_io ? @_io.peeraddr.last : LOCALHOST
|
16
18
|
@env = {}
|
@@ -37,6 +37,7 @@ module Rainbows
|
|
37
37
|
|
38
38
|
def initialize(io)
|
39
39
|
@_io = io
|
40
|
+
@body = nil
|
40
41
|
end
|
41
42
|
|
42
43
|
alias write send_data
|
@@ -54,6 +55,9 @@ module Rainbows
|
|
54
55
|
@env[REMOTE_ADDR] = @remote_addr
|
55
56
|
@env[ASYNC_CALLBACK] = method(:response_write)
|
56
57
|
|
58
|
+
# we're not sure if anybody uses this, but Thin sets it, too
|
59
|
+
@env[ASYNC_CLOSE] = EM::DefaultDeferrable.new
|
60
|
+
|
57
61
|
response = catch(:async) { APP.call(@env.update(RACK_DEFAULTS)) }
|
58
62
|
|
59
63
|
# too tricky to support pipelining with :async since the
|
@@ -77,9 +81,14 @@ module Rainbows
|
|
77
81
|
end while true
|
78
82
|
end
|
79
83
|
|
80
|
-
def response_write(response, out = [], alive = false)
|
81
|
-
body = response.last
|
82
|
-
|
84
|
+
def response_write(response, out = [ CONN_CLOSE ], alive = false)
|
85
|
+
@body = body = response.last
|
86
|
+
if body.respond_to?(:errback) && body.respond_to?(:callback)
|
87
|
+
body.callback { quit }
|
88
|
+
body.errback { quit }
|
89
|
+
HttpResponse.write(self, response, out)
|
90
|
+
return
|
91
|
+
elsif ! body.respond_to?(:to_path)
|
83
92
|
HttpResponse.write(self, response, out)
|
84
93
|
quit unless alive
|
85
94
|
return
|
@@ -120,6 +129,8 @@ module Rainbows
|
|
120
129
|
end
|
121
130
|
|
122
131
|
def unbind
|
132
|
+
async_close = @env[ASYNC_CLOSE] and async_close.succeed
|
133
|
+
@body.respond_to?(:fail) and @body.fail
|
123
134
|
@_io.close
|
124
135
|
end
|
125
136
|
end
|
data/lib/rainbows/thread_pool.rb
CHANGED
@@ -34,8 +34,8 @@ module Rainbows
|
|
34
34
|
# unlikely one. Since this case is (or should be) uncommon,
|
35
35
|
# just busy wait when we have to.
|
36
36
|
sleep(0.01)
|
37
|
-
|
38
|
-
klass.new(
|
37
|
+
elsif c = Rainbows.sync_accept(l)
|
38
|
+
klass.new(c) do |c|
|
39
39
|
begin
|
40
40
|
lock.synchronize { G.cur += 1 }
|
41
41
|
process_client(c)
|
@@ -44,7 +44,6 @@ module Rainbows
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
47
|
-
rescue Errno::EINTR, Errno::ECONNABORTED
|
48
47
|
rescue => e
|
49
48
|
Error.listen_loop(e)
|
50
49
|
end while G.alive
|
data/rainbows.gemspec
CHANGED
@@ -41,8 +41,7 @@ Gem::Specification.new do |s|
|
|
41
41
|
s.test_files = test_files
|
42
42
|
|
43
43
|
# we need Unicorn for the HTTP parser and process management
|
44
|
-
|
45
|
-
s.add_dependency(%q<unicorn>, ["~> 0.95.0"])
|
44
|
+
s.add_dependency(%q<unicorn>, ["~> 0.95.0", "< 0.97.0"])
|
46
45
|
|
47
46
|
# Unicorn already depends on Rack
|
48
47
|
# s.add_dependency(%q<rack>)
|
@@ -0,0 +1,126 @@
|
|
1
|
+
#!/usr/bin/env rackup -s thin
|
2
|
+
#
|
3
|
+
# async_app.ru
|
4
|
+
# raggi/thin
|
5
|
+
#
|
6
|
+
# A second demo app for async rack + thin app processing!
|
7
|
+
# Now using http status code 100 instead.
|
8
|
+
#
|
9
|
+
# Created by James Tucker on 2008-06-17.
|
10
|
+
# Copyright 2008 James Tucker <raggi@rubyforge.org>.
|
11
|
+
#
|
12
|
+
#--
|
13
|
+
# Benchmark Results:
|
14
|
+
#
|
15
|
+
# raggi@mbk:~$ ab -c 100 -n 500 http://127.0.0.1:3000/
|
16
|
+
# This is ApacheBench, Version 2.0.40-dev <$Revision: 1.146 $> apache-2.0
|
17
|
+
# Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
|
18
|
+
# Copyright 2006 The Apache Software Foundation, http://www.apache.org/
|
19
|
+
#
|
20
|
+
# Benchmarking 127.0.0.1 (be patient)
|
21
|
+
# Completed 100 requests
|
22
|
+
# Completed 200 requests
|
23
|
+
# Completed 300 requests
|
24
|
+
# Completed 400 requests
|
25
|
+
# Finished 500 requests
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# Server Software: thin
|
29
|
+
# Server Hostname: 127.0.0.1
|
30
|
+
# Server Port: 3000
|
31
|
+
#
|
32
|
+
# Document Path: /
|
33
|
+
# Document Length: 12 bytes
|
34
|
+
#
|
35
|
+
# Concurrency Level: 100
|
36
|
+
# Time taken for tests: 5.263089 seconds
|
37
|
+
# Complete requests: 500
|
38
|
+
# Failed requests: 0
|
39
|
+
# Write errors: 0
|
40
|
+
# Total transferred: 47000 bytes
|
41
|
+
# HTML transferred: 6000 bytes
|
42
|
+
# Requests per second: 95.00 [#/sec] (mean)
|
43
|
+
# Time per request: 1052.618 [ms] (mean)
|
44
|
+
# Time per request: 10.526 [ms] (mean, across all concurrent requests)
|
45
|
+
# Transfer rate: 8.55 [Kbytes/sec] received
|
46
|
+
#
|
47
|
+
# Connection Times (ms)
|
48
|
+
# min mean[+/-sd] median max
|
49
|
+
# Connect: 0 3 2.2 3 8
|
50
|
+
# Processing: 1042 1046 3.1 1046 1053
|
51
|
+
# Waiting: 1037 1042 3.6 1041 1050
|
52
|
+
# Total: 1045 1049 3.1 1049 1057
|
53
|
+
#
|
54
|
+
# Percentage of the requests served within a certain time (ms)
|
55
|
+
# 50% 1049
|
56
|
+
# 66% 1051
|
57
|
+
# 75% 1053
|
58
|
+
# 80% 1053
|
59
|
+
# 90% 1054
|
60
|
+
# 95% 1054
|
61
|
+
# 98% 1056
|
62
|
+
# 99% 1057
|
63
|
+
# 100% 1057 (longest request)
|
64
|
+
|
65
|
+
class DeferrableBody
|
66
|
+
include EventMachine::Deferrable
|
67
|
+
|
68
|
+
def call(body)
|
69
|
+
body.each do |chunk|
|
70
|
+
@body_callback.call(chunk)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def each &blk
|
75
|
+
@body_callback = blk
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
class AsyncApp
|
81
|
+
|
82
|
+
# This is a template async response. N.B. Can't use string for body on 1.9
|
83
|
+
AsyncResponse = [-1, {}, []].freeze
|
84
|
+
|
85
|
+
def call(env)
|
86
|
+
|
87
|
+
body = DeferrableBody.new
|
88
|
+
|
89
|
+
# Get the headers out there asap, let the client know we're alive...
|
90
|
+
EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
|
91
|
+
|
92
|
+
# Semi-emulate a long db request, instead of a timer, in reality we'd be
|
93
|
+
# waiting for the response data. Whilst this happens, other connections
|
94
|
+
# can be serviced.
|
95
|
+
# This could be any callback based thing though, a deferrable waiting on
|
96
|
+
# IO data, a db request, an http request, an smtp send, whatever.
|
97
|
+
EventMachine::add_timer(1) {
|
98
|
+
body.call ["Woah, async!\n"]
|
99
|
+
|
100
|
+
EventMachine::next_tick {
|
101
|
+
# This could actually happen any time, you could spawn off to new
|
102
|
+
# threads, pause as a good looking lady walks by, whatever.
|
103
|
+
# Just shows off how we can defer chunks of data in the body, you can
|
104
|
+
# even call this many times.
|
105
|
+
body.call ["Cheers then!"]
|
106
|
+
body.succeed
|
107
|
+
}
|
108
|
+
}
|
109
|
+
|
110
|
+
# throw :async # Still works for supporting non-async frameworks...
|
111
|
+
|
112
|
+
AsyncResponse # May end up in Rack :-)
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
# The additions to env for async.connection and async.callback absolutely
|
118
|
+
# destroy the speed of the request if Lint is doing it's checks on env.
|
119
|
+
# It is also important to note that an async response will not pass through
|
120
|
+
# any further middleware, as the async response notification has been passed
|
121
|
+
# right up to the webserver, and the callback goes directly there too.
|
122
|
+
# Middleware could possibly catch :async, and also provide a different
|
123
|
+
# async.connection and async.callback.
|
124
|
+
|
125
|
+
# use Rack::Lint
|
126
|
+
run AsyncApp.new
|
@@ -0,0 +1,105 @@
|
|
1
|
+
#!/usr/bin/env rackup -s thin
|
2
|
+
#
|
3
|
+
# async_tailer.ru
|
4
|
+
# raggi/thin
|
5
|
+
#
|
6
|
+
# Tested with 150 spawned tails on OS X
|
7
|
+
#
|
8
|
+
# Created by James Tucker on 2008-06-18.
|
9
|
+
# Copyright 2008 James Tucker <raggi@rubyforge.org>.
|
10
|
+
|
11
|
+
# Uncomment if appropriate for you..
|
12
|
+
# EM.epoll
|
13
|
+
# EM.kqueue
|
14
|
+
|
15
|
+
tail_log_file = ENV["TAIL_LOG_FILE"] or abort "TAIL_LOG_FILE= env must be set"
|
16
|
+
unless ::File.file?(tail_log_file) && ::File.readable?(tail_log_file)
|
17
|
+
abort "#{tail_log_file} must be a readable regular file"
|
18
|
+
end
|
19
|
+
|
20
|
+
class DeferrableBody
|
21
|
+
include EventMachine::Deferrable
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@queue = []
|
25
|
+
# make sure to flush out the queue before closing the connection
|
26
|
+
callback{
|
27
|
+
until @queue.empty?
|
28
|
+
@queue.shift.each{|chunk| @body_callback.call(chunk) }
|
29
|
+
end
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def schedule_dequeue
|
34
|
+
return unless @body_callback
|
35
|
+
EventMachine::next_tick do
|
36
|
+
next unless body = @queue.shift
|
37
|
+
body.each do |chunk|
|
38
|
+
@body_callback.call(chunk)
|
39
|
+
end
|
40
|
+
schedule_dequeue unless @queue.empty?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def call(body)
|
45
|
+
@queue << body
|
46
|
+
schedule_dequeue
|
47
|
+
end
|
48
|
+
|
49
|
+
def each &blk
|
50
|
+
@body_callback = blk
|
51
|
+
schedule_dequeue
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
module TailRenderer
|
57
|
+
attr_accessor :callback
|
58
|
+
|
59
|
+
def receive_data(data)
|
60
|
+
@callback.call([data])
|
61
|
+
end
|
62
|
+
|
63
|
+
def unbind
|
64
|
+
@callback.succeed
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class AsyncTailer
|
69
|
+
|
70
|
+
AsyncResponse = [-1, {}, []].freeze
|
71
|
+
|
72
|
+
def call(env)
|
73
|
+
|
74
|
+
body = DeferrableBody.new
|
75
|
+
|
76
|
+
EventMachine::next_tick do
|
77
|
+
|
78
|
+
env['async.callback'].call [200, {'Content-Type' => 'text/html'}, body]
|
79
|
+
|
80
|
+
body.call ["<h1>Async Tailer</h1><pre>"]
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
EventMachine::popen("tail -f #{ENV["TAIL_LOG_FILE"]}", TailRenderer) do |t|
|
85
|
+
|
86
|
+
t.callback = body
|
87
|
+
|
88
|
+
# If for some reason we 'complete' body, close the tail.
|
89
|
+
body.callback do
|
90
|
+
t.close_connection
|
91
|
+
end
|
92
|
+
|
93
|
+
# If for some reason the client disconnects, close the tail.
|
94
|
+
body.errback do
|
95
|
+
t.close_connection
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
AsyncResponse
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
run AsyncTailer.new
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
nr=${nr-4}
|
3
|
+
. ./test-lib.sh
|
4
|
+
|
5
|
+
# ApacheBench (ab) is commonly installed in the sbin paths in Debian-based
|
6
|
+
# systems...
|
7
|
+
AB="$(which ab 2>/dev/null || :)"
|
8
|
+
if test -z "$AB"
|
9
|
+
then
|
10
|
+
AB=$(PATH=/usr/local/sbin:/usr/sbin:$PATH which ab 2>/dev/null || :)
|
11
|
+
fi
|
12
|
+
|
13
|
+
if test -z "$AB"
|
14
|
+
then
|
15
|
+
t_info "skipping $T since 'ab' could not be found"
|
16
|
+
exit 0
|
17
|
+
fi
|
18
|
+
|
19
|
+
t_plan 4 "quiet spurious wakeups for $model"
|
20
|
+
|
21
|
+
t_begin "setup and start" && {
|
22
|
+
rainbows_setup $model
|
23
|
+
echo "preload_app true" >> $unicorn_config
|
24
|
+
echo "worker_processes $nr" >> $unicorn_config
|
25
|
+
rainbows -D env.ru -c $unicorn_config -E none
|
26
|
+
rainbows_wait_start
|
27
|
+
}
|
28
|
+
|
29
|
+
t_begin "spam the server with requests" && {
|
30
|
+
$AB -c1 -n100 http://$listen/
|
31
|
+
}
|
32
|
+
|
33
|
+
t_begin "killing succeeds" && {
|
34
|
+
kill -QUIT $rainbows_pid
|
35
|
+
}
|
36
|
+
|
37
|
+
t_begin "check stderr" && {
|
38
|
+
check_stderr
|
39
|
+
}
|
40
|
+
|
41
|
+
t_done
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
nr=${nr-5}
|
3
|
+
. ./test-lib.sh
|
4
|
+
case $model in
|
5
|
+
NeverBlock|EventMachine) ;;
|
6
|
+
*)
|
7
|
+
t_info "skipping $T since it's not compatible with $model"
|
8
|
+
exit 0
|
9
|
+
;;
|
10
|
+
esac
|
11
|
+
|
12
|
+
t_plan 7 "async_app test for test for EM"
|
13
|
+
|
14
|
+
CONFIG_RU=async_examples/async_app.ru
|
15
|
+
|
16
|
+
t_begin "setup and start" && {
|
17
|
+
rainbows_setup
|
18
|
+
rtmpfiles a b c curl_err expect
|
19
|
+
|
20
|
+
# this does not does not support Rack::Lint
|
21
|
+
rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
|
22
|
+
rainbows_wait_start
|
23
|
+
}
|
24
|
+
|
25
|
+
t_begin "send async requests off in parallel" && {
|
26
|
+
t0=$(date +%s)
|
27
|
+
curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
|
28
|
+
curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
|
29
|
+
curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
|
30
|
+
}
|
31
|
+
|
32
|
+
t_begin "wait for curl terminations" && {
|
33
|
+
wait
|
34
|
+
t1=$(date +%s)
|
35
|
+
elapsed=$(( $t1 - $t0 ))
|
36
|
+
t_info "elapsed=$elapsed"
|
37
|
+
}
|
38
|
+
|
39
|
+
t_begin "termination signal sent" && {
|
40
|
+
kill $rainbows_pid
|
41
|
+
}
|
42
|
+
|
43
|
+
t_begin "no errors from curl" && {
|
44
|
+
test ! -s $curl_err
|
45
|
+
}
|
46
|
+
|
47
|
+
t_begin "no errors in stderr" && check_stderr
|
48
|
+
|
49
|
+
t_begin "responses match expected" && {
|
50
|
+
echo 'Woah, async!' > $expect
|
51
|
+
printf 'Cheers then!' >> $expect
|
52
|
+
cmp $expect $a
|
53
|
+
cmp $expect $b
|
54
|
+
cmp $expect $c
|
55
|
+
}
|
56
|
+
|
57
|
+
t_done
|
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
nr=${nr-5}
|
3
|
+
. ./test-lib.sh
|
4
|
+
case $model in
|
5
|
+
NeverBlock|EventMachine) ;;
|
6
|
+
*)
|
7
|
+
t_info "skipping $T since it's not compatible with $model"
|
8
|
+
exit 0
|
9
|
+
;;
|
10
|
+
esac
|
11
|
+
|
12
|
+
t_plan 8 "async_tailer test for test for EM"
|
13
|
+
|
14
|
+
CONFIG_RU=async_examples/async_tailer.ru
|
15
|
+
|
16
|
+
t_begin "setup and start" && {
|
17
|
+
rainbows_setup
|
18
|
+
rtmpfiles a b c curl_err TAIL_LOG_FILE expect
|
19
|
+
|
20
|
+
printf '<h1>Async Tailer</h1><pre>' >> $expect
|
21
|
+
|
22
|
+
export TAIL_LOG_FILE
|
23
|
+
|
24
|
+
# this does not does not support Rack::Lint
|
25
|
+
rainbows -E deployment -D $CONFIG_RU -c $unicorn_config
|
26
|
+
rainbows_wait_start
|
27
|
+
}
|
28
|
+
|
29
|
+
t_begin "send async requests off in parallel" && {
|
30
|
+
t0=$(date +%s)
|
31
|
+
curl --no-buffer -sSf http://$listen/ > $a 2>> $curl_err &
|
32
|
+
curl_a=$!
|
33
|
+
curl --no-buffer -sSf http://$listen/ > $b 2>> $curl_err &
|
34
|
+
curl_b=$!
|
35
|
+
curl --no-buffer -sSf http://$listen/ > $c 2>> $curl_err &
|
36
|
+
curl_c=$!
|
37
|
+
}
|
38
|
+
|
39
|
+
t_begin "generate log output" && {
|
40
|
+
for i in $(awk "BEGIN {for(i=0;i<$nr;i++) print i}" < /dev/null)
|
41
|
+
do
|
42
|
+
date >> $TAIL_LOG_FILE
|
43
|
+
sleep 1
|
44
|
+
done
|
45
|
+
# sometimes tail(1) can be slow
|
46
|
+
sleep 2
|
47
|
+
}
|
48
|
+
|
49
|
+
t_begin "kill curls and wait for termination" && {
|
50
|
+
kill $curl_a $curl_b $curl_c
|
51
|
+
wait
|
52
|
+
t1=$(date +%s)
|
53
|
+
elapsed=$(( $t1 - $t0 ))
|
54
|
+
t_info "elapsed=$elapsed"
|
55
|
+
}
|
56
|
+
|
57
|
+
t_begin "termination signal sent" && {
|
58
|
+
kill $rainbows_pid
|
59
|
+
}
|
60
|
+
|
61
|
+
t_begin "no errors from curl" && {
|
62
|
+
test ! -s $curl_err
|
63
|
+
}
|
64
|
+
|
65
|
+
t_begin "no errors in stderr" && check_stderr
|
66
|
+
|
67
|
+
t_begin "responses match expected" && {
|
68
|
+
cat $TAIL_LOG_FILE >> $expect
|
69
|
+
cmp $expect $a
|
70
|
+
cmp $expect $b
|
71
|
+
cmp $expect $c
|
72
|
+
}
|
73
|
+
|
74
|
+
t_done
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rainbows
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.90.
|
4
|
+
version: 0.90.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rainbows! hackers
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-12-
|
12
|
+
date: 2009-12-30 00:00:00 -08:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -21,6 +21,9 @@ dependencies:
|
|
21
21
|
- - ~>
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 0.95.0
|
24
|
+
- - <
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.97.0
|
24
27
|
version:
|
25
28
|
description: |-
|
26
29
|
Rainbows! is an HTTP server for sleepy Rack applications. It is based on
|
@@ -74,7 +77,6 @@ extra_rdoc_files:
|
|
74
77
|
- lib/rainbows/thread_spawn.rb
|
75
78
|
- LICENSE
|
76
79
|
- NEWS
|
77
|
-
- rainbows.1
|
78
80
|
- README
|
79
81
|
- SIGNALS
|
80
82
|
- TODO
|
@@ -150,6 +152,9 @@ files:
|
|
150
152
|
- t/README
|
151
153
|
- t/async-response-no-autochunk.ru
|
152
154
|
- t/async-response.ru
|
155
|
+
- t/async_examples/README
|
156
|
+
- t/async_examples/async_app.ru
|
157
|
+
- t/async_examples/async_tailer.ru
|
153
158
|
- t/async_sinatra.ru
|
154
159
|
- t/bin/content-md5-put
|
155
160
|
- t/bin/sha1sum.rb
|
@@ -193,18 +198,20 @@ files:
|
|
193
198
|
- t/t0009.ru
|
194
199
|
- t/t0010-keepalive-timeout-effective.sh
|
195
200
|
- t/t0011-close-on-exec-set.sh
|
201
|
+
- t/t0012-spurious-wakeups-quiet.sh
|
196
202
|
- t/t0100-rack-input-hammer.sh
|
197
203
|
- t/t0101-rack-input-trailer.sh
|
198
204
|
- t/t0102-rack-input-short.sh
|
199
205
|
- t/t0200-async-response.sh
|
200
206
|
- t/t0201-async-response-no-autochunk.sh
|
201
207
|
- t/t0300-async_sinatra.sh
|
208
|
+
- t/t0400-em-async-app.sh
|
209
|
+
- t/t0401-em-async-tailer.sh
|
202
210
|
- t/t9000-rack-app-pool.sh
|
203
211
|
- t/t9000.ru
|
204
212
|
- t/test-lib.sh
|
205
213
|
- t/worker-follows-master-to-death.ru
|
206
214
|
- vs_Unicorn
|
207
|
-
- rainbows.1
|
208
215
|
has_rdoc: true
|
209
216
|
homepage: http://rainbows.rubyforge.org/
|
210
217
|
licenses: []
|
data/rainbows.1
DELETED
File without changes
|