rainbows 0.93.0 → 0.94.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.gitignore +0 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +16 -7
- data/Rakefile +0 -21
- data/Test_Suite +63 -0
- data/lib/rainbows.rb +1 -0
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/sendfile.rb +100 -0
- data/lib/rainbows/writer_thread_pool.rb +3 -8
- data/lib/rainbows/writer_thread_spawn.rb +2 -6
- data/local.mk.sample +20 -52
- data/rainbows.gemspec +1 -0
- data/t/.gitignore +1 -0
- data/t/GNUmakefile +12 -10
- data/t/README +21 -4
- data/t/t0005-large-file-response.sh +9 -8
- data/t/t0300-async_sinatra.sh +6 -0
- data/t/t9001-sendfile-to-path.sh +45 -0
- data/t/t9001.ru +11 -0
- data/t/test-lib.sh +6 -10
- data/t/test_isolate.rb +39 -0
- metadata +19 -4
- data/config/.gitignore +0 -1
- data/config/isolate.rb +0 -25
data/.document
CHANGED
data/.gitignore
CHANGED
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
all::
|
3
3
|
RUBY = ruby
|
4
4
|
RAKE = rake
|
5
|
+
RSYNC = rsync
|
5
6
|
GIT_URL = git://git.bogomips.org/rainbows.git
|
6
|
-
ISOLATE_CONFIG = config/isolate.rb
|
7
7
|
|
8
8
|
GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
|
9
9
|
@./GIT-VERSION-GEN
|
@@ -16,12 +16,6 @@ ifeq ($(RUBY_VERSION),)
|
|
16
16
|
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
17
17
|
endif
|
18
18
|
|
19
|
-
# rake takes forever to start
|
20
|
-
isolate: tmp/isolate/ruby-$(RUBY_VERSION)/.isolate
|
21
|
-
tmp/isolate/ruby-$(RUBY_VERSION)/.isolate: $(ISOLATE_CONFIG)
|
22
|
-
ISOLATE_CONFIG=$(ISOLATE_CONFIG) $(RAKE) isolate
|
23
|
-
> $@
|
24
|
-
|
25
19
|
base_bins := rainbows
|
26
20
|
bins := $(addprefix bin/, $(base_bins))
|
27
21
|
man1_rdoc := $(addsuffix _1, $(base_bins))
|
@@ -112,6 +106,19 @@ doc: .document NEWS ChangeLog
|
|
112
106
|
cat Documentation/comparison.css >> doc/rdoc.css
|
113
107
|
$(RM) $(man1_rdoc)
|
114
108
|
|
109
|
+
# publishes docs to http://rainbows.rubyforge.org
|
110
|
+
publish_doc: NEWS
|
111
|
+
-git set-file-times
|
112
|
+
$(RM) -r doc ChangeLog NEWS
|
113
|
+
$(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
|
114
|
+
awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' \
|
115
|
+
< NEWS > doc/LATEST
|
116
|
+
-find doc/images doc/js -type f | \
|
117
|
+
TZ=UTC xargs touch -d '1970-01-01 00:00:01' doc/rdoc.css
|
118
|
+
chmod 644 $$(find doc -type f)
|
119
|
+
$(RSYNC) -av doc/ rubyforge.org:/var/www/gforge-projects/rainbows/
|
120
|
+
git ls-files | xargs touch
|
121
|
+
|
115
122
|
ifneq ($(VERSION),)
|
116
123
|
rfproject := rainbows
|
117
124
|
rfpackage := rainbows
|
@@ -172,6 +179,8 @@ release: verify package $(release_notes) $(release_changes)
|
|
172
179
|
# in case of gem downloads from RubyForge releases page
|
173
180
|
-rubyforge add_file \
|
174
181
|
$(rfproject) $(rfpackage) $(VERSION) $(pkggem)
|
182
|
+
$(RAKE) raa_update VERSION=$(VERSION)
|
183
|
+
$(RAKE) fm_update VERSION=$(VERSION)
|
175
184
|
else
|
176
185
|
gem install-gem: GIT-VERSION-FILE
|
177
186
|
$(MAKE) $@ VERSION=$(GIT_VERSION)
|
data/Rakefile
CHANGED
@@ -183,24 +183,3 @@ task :fm_update do
|
|
183
183
|
p http.post(uri.path, req, {'Content-Type'=>'application/json'})
|
184
184
|
end
|
185
185
|
end
|
186
|
-
|
187
|
-
desc 'isolate gems for development'
|
188
|
-
task :isolate do
|
189
|
-
require 'isolate'
|
190
|
-
require 'rbconfig'
|
191
|
-
|
192
|
-
Isolate.now! :file => ENV['ISOLATE_CONFIG'], :system => false
|
193
|
-
|
194
|
-
# for Ruby 1.8 isolate uses "1.8" instead of "1.8.7" for paths,
|
195
|
-
# but we'll still try to support 1.8.6 for now even though isolate
|
196
|
-
# does not.
|
197
|
-
if Gem.ruby_engine == "ruby" &&
|
198
|
-
RbConfig::CONFIG["ruby_version"] != RUBY_VERSION
|
199
|
-
require 'fileutils'
|
200
|
-
Dir.chdir('tmp/isolate') do
|
201
|
-
FileUtils.rm_rf("ruby-#{RUBY_VERSION}")
|
202
|
-
File.symlink "ruby-#{RbConfig::CONFIG["ruby_version"]}",
|
203
|
-
"ruby-#{RUBY_VERSION}"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
data/Test_Suite
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= \Rainbows! test suite - YES OUR TEST SUITE IS CONCURRENT!
|
2
|
+
|
3
|
+
These are all integration tests that start the server on random, unused
|
4
|
+
TCP ports or Unix domain sockets. They're all designed to run
|
5
|
+
concurrently with other tests to minimize test time, but tests may be
|
6
|
+
run independently as well.
|
7
|
+
|
8
|
+
We write our tests primarily in Bourne shell because that's what we're
|
9
|
+
comfortable writing integration tests with. This test suite is also
|
10
|
+
easily portable to non-Ruby web servers.
|
11
|
+
|
12
|
+
== Requirements
|
13
|
+
|
14
|
+
* {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
|
15
|
+
* {isolate ~> 2.0.2}[http://github.com/jbarnette/isolate] - for dependencies
|
16
|
+
* {GNU make}[http://www.gnu.org/software/make/]
|
17
|
+
* {socat}[http://www.dest-unreach.org/socat/]
|
18
|
+
* {curl >= 7.18.0}[http://curl.haxx.se/]
|
19
|
+
* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)
|
20
|
+
|
21
|
+
We do not use bashisms or any non-portable, non-POSIX constructs
|
22
|
+
in our shell code. We use the "pipefail" option if available and
|
23
|
+
mainly test with {ksh}[http://kornshell.com/], but occasionally
|
24
|
+
with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
|
25
|
+
{bash}[http://www.gnu.org/software/bash/], too.
|
26
|
+
|
27
|
+
== Running Tests
|
28
|
+
|
29
|
+
*BSD users: use "gmake" instead of "make"
|
30
|
+
|
31
|
+
To run the entire test suite with 8 tests running at once:
|
32
|
+
|
33
|
+
make -j8
|
34
|
+
|
35
|
+
To run one individual test for all concurrency models:
|
36
|
+
|
37
|
+
make t0000-simple-http.sh
|
38
|
+
|
39
|
+
To run one individual test for one concurrency model:
|
40
|
+
|
41
|
+
make Revactor.t0000-simple-http.sh
|
42
|
+
|
43
|
+
To run all tests for one concurrency model:
|
44
|
+
|
45
|
+
make EventMachine
|
46
|
+
|
47
|
+
You may also increase verbosity by setting the "V" variable for
|
48
|
+
GNU make. To disable trapping of stdout/stderr:
|
49
|
+
|
50
|
+
make V=1
|
51
|
+
|
52
|
+
To enable the "set -x" option in shell scripts to trace execution
|
53
|
+
|
54
|
+
make V=2
|
55
|
+
|
56
|
+
== Performance
|
57
|
+
|
58
|
+
Some of the tests are rather I/O intensive due to the rewindability
|
59
|
+
requirement of "rack.input" in the Rack specification and the somewhat
|
60
|
+
complicated (but awesome!) nature of the TeeInput class leading us to
|
61
|
+
test it very heavily. If you have lots of RAM and a large tmpfs
|
62
|
+
partition, it is advisable to set your TMPDIR and also make the t/trash/
|
63
|
+
directory a symlink to a directory inside in your TMPDIR.
|
data/lib/rainbows.rb
CHANGED
@@ -32,6 +32,7 @@ module Rainbows
|
|
32
32
|
require 'rainbows/http_response'
|
33
33
|
require 'rainbows/base'
|
34
34
|
require 'rainbows/tee_input'
|
35
|
+
autoload :Sendfile, 'rainbows/sendfile'
|
35
36
|
autoload :AppPool, 'rainbows/app_pool'
|
36
37
|
autoload :DevFdResponse, 'rainbows/dev_fd_response'
|
37
38
|
autoload :MaxBody, 'rainbows/max_body'
|
data/lib/rainbows/const.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
module Rainbows
|
3
|
+
|
4
|
+
# This middleware handles X-\Sendfile headers generated by applications
|
5
|
+
# or middlewares down the stack. It should be placed at the top
|
6
|
+
# (outermost layer) of the middleware stack to avoid having its
|
7
|
+
# +to_path+ method clobbered by another middleware.
|
8
|
+
#
|
9
|
+
# This converts X-\Sendfile responses to bodies which respond to the
|
10
|
+
# +to_path+ method which allows certain concurrency models to serve
|
11
|
+
# efficiently using sendfile() or similar. With multithreaded models
|
12
|
+
# under Ruby 1.9, IO.copy_stream will be used.
|
13
|
+
#
|
14
|
+
# This middleware is the opposite of Rack::Contrib::Sendfile as it
|
15
|
+
# reverses the effect of Rack::Contrib::Sendfile. Unlike many Ruby
|
16
|
+
# web servers, some configurations of \Rainbows! are capable of
|
17
|
+
# serving static files efficiently.
|
18
|
+
#
|
19
|
+
# === Compatibility (via IO.copy_stream in Ruby 1.9):
|
20
|
+
# * ThreadSpawn
|
21
|
+
# * ThreadPool
|
22
|
+
# * WriterThreadPool
|
23
|
+
# * WriterThreadSpawn
|
24
|
+
#
|
25
|
+
# === Compatibility (Ruby 1.8 and 1.9)
|
26
|
+
# * EventMachine
|
27
|
+
# * NeverBlock (using EventMachine)
|
28
|
+
#
|
29
|
+
# DO NOT use this middleware if you're proxying to \Rainbows! with a
|
30
|
+
# server that understands X-\Sendfile (e.g. Apache, Lighttpd) natively.
|
31
|
+
#
|
32
|
+
# This does NOT understand X-Accel-Redirect headers intended for nginx.
|
33
|
+
# X-Accel-Redirect requires the application to be highly coupled with
|
34
|
+
# the corresponding nginx configuration, and is thus too complicated to
|
35
|
+
# be worth supporting.
|
36
|
+
#
|
37
|
+
# Example config.ru:
|
38
|
+
#
|
39
|
+
# use Rainbows::Sendfile
|
40
|
+
# run lambda { |env|
|
41
|
+
# path = "#{Dir.pwd}/random_blob"
|
42
|
+
# [ 200,
|
43
|
+
# {
|
44
|
+
# 'X-Sendfile' => path,
|
45
|
+
# 'Content-Type' => 'application/octet-stream'
|
46
|
+
# },
|
47
|
+
# []
|
48
|
+
# ]
|
49
|
+
# }
|
50
|
+
|
51
|
+
class Sendfile < Struct.new(:app)
|
52
|
+
|
53
|
+
# :stopdoc:
|
54
|
+
HH = Rack::Utils::HeaderHash
|
55
|
+
# :startdoc:
|
56
|
+
|
57
|
+
# Body wrapper, this allows us to fall back gracefully to
|
58
|
+
# +each+ in case a given concurrency model does not optimize
|
59
|
+
# +to_path+ calls.
|
60
|
+
class Body < Struct.new(:to_io)
|
61
|
+
|
62
|
+
def initialize(path, headers)
|
63
|
+
# Rainbows! will try #to_io if #to_path exists to avoid unnecessary
|
64
|
+
# open() calls.
|
65
|
+
self.to_io = File.open(path, 'rb')
|
66
|
+
|
67
|
+
unless headers['Content-Length']
|
68
|
+
stat = to_io.stat
|
69
|
+
headers['Content-Length'] = stat.size.to_s if stat.file?
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_path
|
74
|
+
to_io.path
|
75
|
+
end
|
76
|
+
|
77
|
+
# fallback in case our +to_path+ doesn't get handled for whatever reason
|
78
|
+
def each(&block)
|
79
|
+
buf = ''
|
80
|
+
while to_io.read(0x4000, buf)
|
81
|
+
yield buf
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def close
|
86
|
+
to_io.close
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def call(env)
|
91
|
+
status, headers, body = app.call(env)
|
92
|
+
headers = HH.new(headers)
|
93
|
+
if path = headers.delete('X-Sendfile')
|
94
|
+
body = Body.new(path, headers) unless body.respond_to?(:to_path)
|
95
|
+
end
|
96
|
+
[ status, headers, body ]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -46,13 +46,8 @@ module Rainbows
|
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
|
50
|
-
|
51
|
-
undef_method :write_body
|
52
|
-
|
53
|
-
def write_body(qclient, body)
|
54
|
-
qclient.q << [ qclient.to_io, :body, body ]
|
55
|
-
end
|
49
|
+
def write_body(qclient, body)
|
50
|
+
qclient.q << [ qclient.to_io, :body, body ]
|
56
51
|
end
|
57
52
|
|
58
53
|
@@nr = 0
|
@@ -71,7 +66,7 @@ module Rainbows
|
|
71
66
|
begin
|
72
67
|
io, arg1, arg2 = response
|
73
68
|
case arg1
|
74
|
-
when :body then
|
69
|
+
when :body then Base.write_body(io, arg2)
|
75
70
|
when :close then io.close unless io.closed?
|
76
71
|
else
|
77
72
|
io.write(arg1)
|
@@ -88,12 +88,8 @@ module Rainbows
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
def write_body(my_sock, body)
|
95
|
-
my_sock.write_body(body)
|
96
|
-
end
|
91
|
+
def write_body(my_sock, body)
|
92
|
+
my_sock.write_body(body)
|
97
93
|
end
|
98
94
|
|
99
95
|
def process_client(client)
|
data/local.mk.sample
CHANGED
@@ -4,37 +4,32 @@
|
|
4
4
|
#
|
5
5
|
# This is depends on a bunch of GNU-isms from bash, sed, touch.
|
6
6
|
|
7
|
-
RSYNC = rsync
|
8
7
|
DLEXT := so
|
9
8
|
|
9
|
+
# if you have a decent amount of RAM, setting TMPDIR to be on tmpfs
|
10
|
+
# can significantly improve performance because uploads take a lot
|
11
|
+
# of disk I/O due to the rewindability requirement in Rack.
|
12
|
+
# TMPDIR := /dev/shm
|
13
|
+
# export TMPDIR
|
14
|
+
|
10
15
|
# Avoid loading rubygems to speed up tests because gmake is
|
11
16
|
# fork+exec heavy with Ruby.
|
12
17
|
prefix = $(HOME)
|
13
18
|
|
14
|
-
ifeq ($(
|
15
|
-
|
19
|
+
ifeq ($(r192),)
|
20
|
+
ifeq ($(r19),)
|
21
|
+
RUBY := $(prefix)/bin/ruby
|
22
|
+
else
|
23
|
+
prefix := $(prefix)/ruby-1.9
|
24
|
+
export PATH := $(prefix)/bin:$(PATH)
|
25
|
+
RUBY := $(prefix)/bin/ruby --disable-gems
|
26
|
+
endif
|
16
27
|
else
|
17
|
-
prefix := $(prefix)/ruby-1.9
|
28
|
+
prefix := $(prefix)/ruby-1.9.2
|
18
29
|
export PATH := $(prefix)/bin:$(PATH)
|
19
30
|
RUBY := $(prefix)/bin/ruby --disable-gems
|
20
31
|
endif
|
21
32
|
|
22
|
-
ifndef NO_ISOLATE
|
23
|
-
x := $(shell test -d t/ && \
|
24
|
-
PATH=$(PATH) NO_ISOLATE=T $(MAKE) -s isolate RUBY:="$(RUBY)")
|
25
|
-
endif
|
26
|
-
|
27
|
-
RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
|
28
|
-
|
29
|
-
updir := $(shell git rev-parse --show-cdup)
|
30
|
-
gem_paths := $(wildcard $(updir)tmp/isolate/ruby-$(RUBY_VERSION)/gems/*-*)
|
31
|
-
|
32
|
-
ifdef gem_paths
|
33
|
-
sp :=
|
34
|
-
sp +=
|
35
|
-
export RUBYLIB := $(subst $(sp),:,$(addsuffix /lib,$(gem_paths)))
|
36
|
-
endif
|
37
|
-
|
38
33
|
# pipefail is THE reason to use bash (v3+) or never revisions of ksh93
|
39
34
|
# SHELL := /bin/bash -e -o pipefail
|
40
35
|
SHELL := /bin/ksh93 -e -o pipefail
|
@@ -43,37 +38,10 @@ SHELL := /bin/ksh93 -e -o pipefail
|
|
43
38
|
# TRACER = strace -f -o $(t_pfx).strace -s 100000
|
44
39
|
TRACER = /usr/bin/time -v -o $(t_pfx).time
|
45
40
|
|
46
|
-
full-test: test-18 test-
|
41
|
+
full-test: test-18 test-191 test-192
|
47
42
|
test-18:
|
48
43
|
$(MAKE) test 2>&1 | sed -e 's!^!1.8 !'
|
49
|
-
test-
|
50
|
-
$(MAKE) test r19=T 2>&1 | sed -e 's!^!1.9 !'
|
51
|
-
|
52
|
-
|
53
|
-
@awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' < $<
|
54
|
-
|
55
|
-
# publishes docs to http://rainbows.rubyforge.org
|
56
|
-
publish_doc:
|
57
|
-
-git set-file-times
|
58
|
-
$(RM) -r doc ChangeLog NEWS
|
59
|
-
$(MAKE) doc LOG_VERSION=$(shell git tag -l | tail -1)
|
60
|
-
$(MAKE) -s latest > doc/LATEST
|
61
|
-
find doc/images doc/js -type f | \
|
62
|
-
TZ=UTC xargs touch -d '1970-01-01 00:00:00' doc/rdoc.css
|
63
|
-
$(MAKE) doc_gz
|
64
|
-
chmod 644 $$(find doc -type f)
|
65
|
-
$(RSYNC) -av doc/ rubyforge.org:/var/www/gforge-projects/rainbows/
|
66
|
-
$(RSYNC) -av doc/ dcvr:/srv/rainbows/
|
67
|
-
git ls-files | xargs touch
|
68
|
-
|
69
|
-
# Create gzip variants of the same timestamp as the original so nginx
|
70
|
-
# "gzip_static on" can serve the gzipped versions directly.
|
71
|
-
doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$')
|
72
|
-
doc_gz:
|
73
|
-
touch doc/NEWS.atom.xml -d "$$(awk 'NR==1{print $$4,$$5,$$6}' NEWS)"
|
74
|
-
for i in $(docs); do \
|
75
|
-
gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done
|
76
|
-
|
77
|
-
# launches any of the following shells with RUBYLIB set
|
78
|
-
irb sh bash ksh:
|
79
|
-
$@
|
44
|
+
test-191:
|
45
|
+
$(MAKE) test r19=T 2>&1 | sed -e 's!^!1.9.1 !'
|
46
|
+
test-192:
|
47
|
+
$(MAKE) test r192=T 2>&1 | sed -e 's!^!1.9.2 !'
|
data/rainbows.gemspec
CHANGED
@@ -44,6 +44,7 @@ Gem::Specification.new do |s|
|
|
44
44
|
# The HTTP parser in Unicorn <= 0.97.0 was vulnerable to a remote DoS
|
45
45
|
# when exposed directly to untrusted clients.
|
46
46
|
s.add_dependency(%q<unicorn>, [">= 0.97.1", "< 2.0.0"])
|
47
|
+
s.add_development_dependency(%q<isolate>, "~> 2.0.2")
|
47
48
|
|
48
49
|
# Unicorn already depends on Rack
|
49
50
|
# s.add_dependency(%q<rack>)
|
data/t/.gitignore
CHANGED
data/t/GNUmakefile
CHANGED
@@ -15,12 +15,7 @@ ifeq ($(RUBY_VERSION),)
|
|
15
15
|
$(error unable to detect RUBY_VERSION)
|
16
16
|
endif
|
17
17
|
|
18
|
-
|
19
|
-
RUBYLIB := $(rainbows_lib)
|
20
|
-
else
|
21
|
-
RUBYLIB := $(rainbows_lib):$(RUBYLIB)
|
22
|
-
endif
|
23
|
-
export RUBYLIB RUBY_VERSION
|
18
|
+
export RUBY_VERSION
|
24
19
|
|
25
20
|
models += WriterThreadPool
|
26
21
|
models += WriterThreadSpawn
|
@@ -99,15 +94,22 @@ $(deps):
|
|
99
94
|
@test -s $@.$(pid) || \
|
100
95
|
{ echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
|
101
96
|
@mv $@.$(pid) $@
|
102
|
-
|
97
|
+
|
98
|
+
libs := tmp/isolate/ruby-$(RUBY_VERSION)/.libs
|
99
|
+
$(libs): test_isolate.rb
|
100
|
+
mkdir -p $(@D)
|
101
|
+
$(RUBY) $< > $@+
|
102
|
+
mv $@+ $@
|
103
|
+
t_deps := $(libs) $(deps) test-bin-$(RUBY_VERSION)/rainbows trash/.gitignore
|
104
|
+
$(T): $(t_deps)
|
103
105
|
|
104
106
|
$(MODEL_T): export model = $(firstword $(subst ., ,$@))
|
105
107
|
$(MODEL_T): script = $(subst $(model).,,$@)
|
106
|
-
$(MODEL_T): trash/.gitignore
|
107
108
|
$(MODEL_T): export RUBY := $(RUBY)
|
108
109
|
$(MODEL_T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
|
109
|
-
$(MODEL_T):
|
110
|
-
|
110
|
+
$(MODEL_T): $(t_deps)
|
111
|
+
RUBYLIB=$(rainbows_lib):$$(cat $(libs)) \
|
112
|
+
$(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
|
111
113
|
|
112
114
|
trash/.gitignore:
|
113
115
|
mkdir -p $(@D)
|
data/t/README
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
-
= Rainbows! test suite
|
1
|
+
= \Rainbows! test suite - YES OUR TEST SUITE IS CONCURRENT!
|
2
2
|
|
3
3
|
These are all integration tests that start the server on random, unused
|
4
4
|
TCP ports or Unix domain sockets. They're all designed to run
|
5
5
|
concurrently with other tests to minimize test time, but tests may be
|
6
6
|
run independently as well.
|
7
7
|
|
8
|
-
We write our tests in Bourne shell because that's what we're
|
9
|
-
comfortable writing integration tests with.
|
8
|
+
We write our tests primarily in Bourne shell because that's what we're
|
9
|
+
comfortable writing integration tests with. This test suite is also
|
10
|
+
easily portable to non-Ruby web servers.
|
10
11
|
|
11
12
|
== Requirements
|
12
13
|
|
13
14
|
* {Ruby 1.8 or 1.9}[http://www.ruby-lang.org/] (duh!)
|
15
|
+
* {isolate ~> 2.0.2}[http://github.com/jbarnette/isolate] - for dependencies
|
14
16
|
* {GNU make}[http://www.gnu.org/software/make/]
|
15
17
|
* {socat}[http://www.dest-unreach.org/socat/]
|
16
|
-
* {curl}[http://curl.haxx.se/]
|
18
|
+
* {curl >= 7.18.0}[http://curl.haxx.se/]
|
17
19
|
* standard UNIX shell utilities (Bourne sh, awk, sed, grep, ...)
|
18
20
|
|
19
21
|
We do not use bashisms or any non-portable, non-POSIX constructs
|
@@ -24,6 +26,8 @@ with {dash}[http://gondor.apana.org.au/~herbert/dash/] and
|
|
24
26
|
|
25
27
|
== Running Tests
|
26
28
|
|
29
|
+
*BSD users: use "gmake" instead of "make"
|
30
|
+
|
27
31
|
To run the entire test suite with 8 tests running at once:
|
28
32
|
|
29
33
|
make -j8
|
@@ -36,6 +40,10 @@ To run one individual test for one concurrency model:
|
|
36
40
|
|
37
41
|
make Revactor.t0000-simple-http.sh
|
38
42
|
|
43
|
+
To run all tests for one concurrency model:
|
44
|
+
|
45
|
+
make EventMachine
|
46
|
+
|
39
47
|
You may also increase verbosity by setting the "V" variable for
|
40
48
|
GNU make. To disable trapping of stdout/stderr:
|
41
49
|
|
@@ -44,3 +52,12 @@ GNU make. To disable trapping of stdout/stderr:
|
|
44
52
|
To enable the "set -x" option in shell scripts to trace execution
|
45
53
|
|
46
54
|
make V=2
|
55
|
+
|
56
|
+
== Performance
|
57
|
+
|
58
|
+
Some of the tests are rather I/O intensive due to the rewindability
|
59
|
+
requirement of "rack.input" in the Rack specification and the somewhat
|
60
|
+
complicated (but awesome!) nature of the TeeInput class leading us to
|
61
|
+
test it very heavily. If you have lots of RAM and a large tmpfs
|
62
|
+
partition, it is advisable to set your TMPDIR and also make the t/trash/
|
63
|
+
directory a symlink to a directory inside in your TMPDIR.
|
@@ -18,7 +18,8 @@ t_begin "setup and startup" && {
|
|
18
18
|
rainbows_wait_start
|
19
19
|
}
|
20
20
|
|
21
|
-
t_begin "read random blob size" && {
|
21
|
+
t_begin "read random blob sha1 and size" && {
|
22
|
+
random_blob_sha1=$(rsha1 < random_blob)
|
22
23
|
random_blob_size=$(wc -c < random_blob)
|
23
24
|
}
|
24
25
|
|
@@ -32,18 +33,18 @@ t_begin "read current RSS" && {
|
|
32
33
|
t_begin "send a series HTTP/1.1 requests sequentially" && {
|
33
34
|
for i in a b c
|
34
35
|
do
|
35
|
-
|
36
|
-
echo ok >$ok) |
|
37
|
-
test $
|
36
|
+
sha1=$( (curl -sSfv http://$listen/random_blob &&
|
37
|
+
echo ok >$ok) | rsha1)
|
38
|
+
test $sha1 = $random_blob_sha1
|
38
39
|
test xok = x$(cat $ok)
|
39
40
|
done
|
40
41
|
}
|
41
42
|
|
42
43
|
# this was a problem during development
|
43
44
|
t_begin "HTTP/1.0 test" && {
|
44
|
-
|
45
|
-
echo ok >$ok) |
|
46
|
-
test $
|
45
|
+
sha1=$( (curl -0 -sSfv http://$listen/random_blob &&
|
46
|
+
echo ok >$ok) | rsha1)
|
47
|
+
test $sha1 = $random_blob_sha1
|
47
48
|
test xok = x$(cat $ok)
|
48
49
|
}
|
49
50
|
|
@@ -54,7 +55,7 @@ t_begin "HTTP/0.9 test" && {
|
|
54
55
|
wait
|
55
56
|
echo ok > $ok
|
56
57
|
) | socat - TCP:$listen > $fifo
|
57
|
-
test $(cat $tmp) = $
|
58
|
+
test $(cat $tmp) = $random_blob_sha1
|
58
59
|
test xok = x$(cat $ok)
|
59
60
|
}
|
60
61
|
|
data/t/t0300-async_sinatra.sh
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
|
4
|
+
t_plan 7 "Sendfile middleware test for $model"
|
5
|
+
|
6
|
+
t_begin "configure and start" && {
|
7
|
+
rtmpfiles curl_out curl_err
|
8
|
+
rainbows_setup
|
9
|
+
|
10
|
+
# do not allow default middleware to be loaded since it may
|
11
|
+
# kill body#to_path
|
12
|
+
rainbows -E none -D t9001.ru -c $unicorn_config
|
13
|
+
rainbows_wait_start
|
14
|
+
}
|
15
|
+
|
16
|
+
t_begin "hit with curl" && {
|
17
|
+
curl -sSfv http://$listen/ > $curl_out 2> $curl_err
|
18
|
+
}
|
19
|
+
|
20
|
+
t_begin "kill server" && {
|
21
|
+
kill $rainbows_pid
|
22
|
+
}
|
23
|
+
|
24
|
+
t_begin "file matches source" && {
|
25
|
+
cmp $curl_out random_blob
|
26
|
+
}
|
27
|
+
|
28
|
+
t_begin "no errors in Rainbows! stderr" && {
|
29
|
+
check_stderr
|
30
|
+
}
|
31
|
+
|
32
|
+
t_begin "X-Sendfile does not show up in headers" && {
|
33
|
+
dbgcat curl_err
|
34
|
+
if grep -i x-sendfile $curl_err
|
35
|
+
then
|
36
|
+
die "X-Sendfile did show up!"
|
37
|
+
fi
|
38
|
+
}
|
39
|
+
|
40
|
+
t_begin "Content-Length is set correctly in headers" && {
|
41
|
+
expect=$(wc -c < random_blob)
|
42
|
+
grep "^< Content-Length: $expect" $curl_err
|
43
|
+
}
|
44
|
+
|
45
|
+
t_done
|
data/t/t9001.ru
ADDED
data/t/test-lib.sh
CHANGED
@@ -3,6 +3,12 @@
|
|
3
3
|
. ./my-tap-lib.sh
|
4
4
|
|
5
5
|
set +u
|
6
|
+
|
7
|
+
# sometimes we rely on http_proxy to avoid wasting bandwidth with Isolate
|
8
|
+
# and multiple Ruby versions
|
9
|
+
NO_PROXY=${UNICORN_TEST_ADDR-127.0.0.1}
|
10
|
+
export NO_PROXY
|
11
|
+
|
6
12
|
if test -z "$model"
|
7
13
|
then
|
8
14
|
# defaulting to Base would unfortunately fail some concurrency tests
|
@@ -21,16 +27,6 @@ export PATH
|
|
21
27
|
|
22
28
|
test -x $PWD/bin/unused_listen || die "must be run in 't' directory"
|
23
29
|
|
24
|
-
wait_for_pid () {
|
25
|
-
path="$1"
|
26
|
-
nr=30
|
27
|
-
while ! test -s "$path" && test $nr -gt 0
|
28
|
-
do
|
29
|
-
nr=$(($nr - 1))
|
30
|
-
sleep 1
|
31
|
-
done
|
32
|
-
}
|
33
|
-
|
34
30
|
# requires $1 and prints out the value of $2
|
35
31
|
require_check () {
|
36
32
|
lib=$1
|
data/t/test_isolate.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'isolate'
|
3
|
+
|
4
|
+
path = "tmp/isolate/ruby-#{RUBY_VERSION}"
|
5
|
+
opts = {
|
6
|
+
:system => false,
|
7
|
+
# we want "ruby-1.8.7" and not "ruby-1.8", so disable multiruby
|
8
|
+
:multiruby => false,
|
9
|
+
:path => path,
|
10
|
+
}
|
11
|
+
|
12
|
+
old_out = $stdout.dup
|
13
|
+
$stdout.reopen($stderr)
|
14
|
+
|
15
|
+
Isolate.now!(opts) do
|
16
|
+
gem 'rack', '1.1.0'
|
17
|
+
gem 'unicorn', '0.99.0'
|
18
|
+
|
19
|
+
gem 'iobuffer', '0.1.3'
|
20
|
+
gem 'rev', '0.3.2'
|
21
|
+
|
22
|
+
gem 'eventmachine', '0.12.10'
|
23
|
+
|
24
|
+
gem 'sinatra', '0.9.4'
|
25
|
+
gem 'async_sinatra', '0.1.5'
|
26
|
+
|
27
|
+
gem 'neverblock', '0.1.6.2'
|
28
|
+
|
29
|
+
if defined?(::Fiber)
|
30
|
+
gem 'case', '0.5'
|
31
|
+
gem 'revactor', '0.1.5'
|
32
|
+
gem 'rack-fiber_pool', '0.9.0'
|
33
|
+
end
|
34
|
+
|
35
|
+
gem 'cramp', '0.11'
|
36
|
+
end
|
37
|
+
|
38
|
+
$stdout.reopen(old_out)
|
39
|
+
puts Dir["#{path}/gems/*-*/lib"].map { |x| File.expand_path(x) }.join(':')
|
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.
|
4
|
+
version: 0.94.0
|
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: 2010-
|
12
|
+
date: 2010-06-04 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +25,16 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.0.0
|
27
27
|
version:
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: isolate
|
30
|
+
type: :development
|
31
|
+
version_requirement:
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
33
|
+
requirements:
|
34
|
+
- - ~>
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: 2.0.2
|
37
|
+
version:
|
28
38
|
description: |-
|
29
39
|
\Rainbows! is an HTTP server for sleepy Rack applications. It is based on
|
30
40
|
Unicorn, but designed to handle applications that expect long
|
@@ -73,6 +83,7 @@ extra_rdoc_files:
|
|
73
83
|
- lib/rainbows/rev_thread_pool.rb
|
74
84
|
- lib/rainbows/rev_thread_spawn.rb
|
75
85
|
- lib/rainbows/revactor.rb
|
86
|
+
- lib/rainbows/sendfile.rb
|
76
87
|
- lib/rainbows/tee_input.rb
|
77
88
|
- lib/rainbows/thread_pool.rb
|
78
89
|
- lib/rainbows/thread_spawn.rb
|
@@ -86,6 +97,7 @@ extra_rdoc_files:
|
|
86
97
|
- TUNING
|
87
98
|
- vs_Unicorn
|
88
99
|
- Summary
|
100
|
+
- Test_Suite
|
89
101
|
files:
|
90
102
|
- .document
|
91
103
|
- .gitignore
|
@@ -110,9 +122,8 @@ files:
|
|
110
122
|
- Summary
|
111
123
|
- TODO
|
112
124
|
- TUNING
|
125
|
+
- Test_Suite
|
113
126
|
- bin/rainbows
|
114
|
-
- config/.gitignore
|
115
|
-
- config/isolate.rb
|
116
127
|
- lib/rainbows.rb
|
117
128
|
- lib/rainbows/actor_spawn.rb
|
118
129
|
- lib/rainbows/app_pool.rb
|
@@ -146,6 +157,7 @@ files:
|
|
146
157
|
- lib/rainbows/rev_thread_pool.rb
|
147
158
|
- lib/rainbows/rev_thread_spawn.rb
|
148
159
|
- lib/rainbows/revactor.rb
|
160
|
+
- lib/rainbows/sendfile.rb
|
149
161
|
- lib/rainbows/tee_input.rb
|
150
162
|
- lib/rainbows/thread_pool.rb
|
151
163
|
- lib/rainbows/thread_spawn.rb
|
@@ -231,7 +243,10 @@ files:
|
|
231
243
|
- t/t0700-app-deferred.sh
|
232
244
|
- t/t9000-rack-app-pool.sh
|
233
245
|
- t/t9000.ru
|
246
|
+
- t/t9001-sendfile-to-path.sh
|
247
|
+
- t/t9001.ru
|
234
248
|
- t/test-lib.sh
|
249
|
+
- t/test_isolate.rb
|
235
250
|
- t/worker-follows-master-to-death.ru
|
236
251
|
- vs_Unicorn
|
237
252
|
has_rdoc: true
|
data/config/.gitignore
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
/isolate_*.rb
|
data/config/isolate.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# this the default config file used by John Barnette's isolate gem
|
2
|
-
# you can create a config/isolate_local.rb file to override this
|
3
|
-
# See the corresponding tasks in Rakefile and GNUmakefile
|
4
|
-
# `rake isolate' or (faster in the unmodified case, `make isolate')
|
5
|
-
|
6
|
-
gem 'rack', '1.1.0'
|
7
|
-
gem 'unicorn', '0.99.0'
|
8
|
-
|
9
|
-
gem 'iobuffer', '0.1.3'
|
10
|
-
gem 'rev', '0.3.2'
|
11
|
-
|
12
|
-
gem 'eventmachine', '0.12.10'
|
13
|
-
|
14
|
-
gem 'sinatra', '0.9.4'
|
15
|
-
gem 'async_sinatra', '0.1.5'
|
16
|
-
|
17
|
-
gem 'neverblock', '0.1.6.2'
|
18
|
-
|
19
|
-
if defined?(::Fiber)
|
20
|
-
gem 'case', '0.5'
|
21
|
-
gem 'revactor', '0.1.5'
|
22
|
-
gem 'rack-fiber_pool', '0.9.0'
|
23
|
-
end
|
24
|
-
|
25
|
-
gem 'cramp', '0.11'
|