rainbows 0.90.0 → 0.90.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|