rainbows 0.91.1 → 0.92.0
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/.document +1 -1
- data/.gitignore +1 -0
- data/.manifest +13 -4
- data/ChangeLog +237 -219
- data/DEPLOY +13 -13
- data/Documentation/comparison.haml +1 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +18 -7
- data/NEWS +29 -0
- data/README +5 -4
- data/Rakefile +7 -0
- data/SIGNALS +5 -1
- data/TODO +2 -5
- data/config/.gitignore +1 -0
- data/config/isolate.rb +25 -0
- data/lib/rainbows/base.rb +32 -8
- data/lib/rainbows/const.rb +3 -2
- data/lib/rainbows/ev_core.rb +42 -2
- data/lib/rainbows/event_machine.rb +48 -5
- data/lib/rainbows/fiber/base.rb +4 -2
- data/lib/rainbows/fiber/rev.rb +1 -1
- data/lib/rainbows/http_response.rb +21 -20
- data/lib/rainbows/http_server.rb +13 -6
- data/lib/rainbows/max_body.rb +82 -0
- data/lib/rainbows/rev/deferred_response.rb +10 -11
- data/lib/rainbows/revactor.rb +38 -4
- data/lib/rainbows/tee_input.rb +17 -0
- data/lib/rainbows.rb +26 -2
- data/local.mk.sample +13 -23
- data/rainbows.gemspec +2 -2
- data/t/app_deferred.ru +23 -0
- data/t/async_examples/async_app.ru +8 -8
- data/t/rack-fiber_pool/app.ru +5 -0
- data/t/{t0100-rack-input-hammer.sh → t0100-rack-input-hammer-chunked.sh} +1 -1
- data/t/t0100-rack-input-hammer-content-length.sh +50 -0
- data/t/t0103-rack-input-limit.sh +60 -0
- data/t/t0104-rack-input-limit-tiny.sh +62 -0
- data/t/t0105-rack-input-limit-bigger.sh +105 -0
- data/t/t0401-em-async-tailer.sh +1 -1
- data/t/t0600-rack-fiber_pool.sh +49 -0
- data/t/t0700-app-deferred.sh +45 -0
- data/t/test-lib.sh +1 -0
- metadata +22 -10
- data/lib/rainbows/event_machine_defer.rb +0 -59
- data/lib/rainbows/revactor/tee_input.rb +0 -52
- data/t/simple-http_EventMachineDefer.ru +0 -11
@@ -0,0 +1,82 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
module Rainbows
|
3
|
+
|
4
|
+
# middleware used to enforce client_max_body_size for TeeInput users,
|
5
|
+
# there is no need to configure this middleware manually, it will
|
6
|
+
# automatically be configured for you based on the client_max_body_size
|
7
|
+
# setting
|
8
|
+
class MaxBody < Struct.new(:app)
|
9
|
+
|
10
|
+
# this is meant to be included in Rainbows::TeeInput (and derived
|
11
|
+
# classes) to limit body sizes
|
12
|
+
module Limit
|
13
|
+
Util = Unicorn::Util
|
14
|
+
|
15
|
+
def initialize(socket, req, parser, buf)
|
16
|
+
self.len = parser.content_length
|
17
|
+
|
18
|
+
max = Rainbows.max_bytes # never nil, see MaxBody.setup
|
19
|
+
if len && len > max
|
20
|
+
socket.write(Const::ERROR_413_RESPONSE)
|
21
|
+
socket.close
|
22
|
+
raise IOError, "Content-Length too big: #{len} > #{max}", []
|
23
|
+
end
|
24
|
+
|
25
|
+
self.req = req
|
26
|
+
self.parser = parser
|
27
|
+
self.buf = buf
|
28
|
+
self.socket = socket
|
29
|
+
self.buf2 = ""
|
30
|
+
if buf.size > 0
|
31
|
+
parser.filter_body(buf2, buf) and finalize_input
|
32
|
+
buf2.size > max and raise IOError, "chunked request body too big", []
|
33
|
+
end
|
34
|
+
self.tmp = len && len < Const::MAX_BODY ? StringIO.new("") : Util.tmpio
|
35
|
+
if buf2.size > 0
|
36
|
+
tmp.write(buf2)
|
37
|
+
tmp.seek(0)
|
38
|
+
max -= buf2.size
|
39
|
+
end
|
40
|
+
@max_body = max
|
41
|
+
end
|
42
|
+
|
43
|
+
def tee(length, dst)
|
44
|
+
rv = super
|
45
|
+
if rv && ((@max_body -= rv.size) < 0)
|
46
|
+
# make HttpParser#keepalive? => false to force an immediate disconnect
|
47
|
+
# after we write
|
48
|
+
parser.reset
|
49
|
+
throw :rainbows_EFBIG
|
50
|
+
end
|
51
|
+
rv
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
# this is called after forking, so it won't ever affect the master
|
57
|
+
# if it's reconfigured
|
58
|
+
def self.setup
|
59
|
+
Rainbows.max_bytes or return
|
60
|
+
case G.server.use
|
61
|
+
when :Rev, :EventMachine, :NeverBlock
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
TeeInput.class_eval { include Limit }
|
66
|
+
|
67
|
+
# force ourselves to the outermost middleware layer
|
68
|
+
G.server.app = MaxBody.new(G.server.app)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Rack response returned when there's an error
|
72
|
+
def err(env)
|
73
|
+
[ 413, [ %w(Content-Length 0), %w(Content-Type text/plain) ], [] ]
|
74
|
+
end
|
75
|
+
|
76
|
+
# our main Rack middleware endpoint
|
77
|
+
def call(env)
|
78
|
+
catch(:rainbows_EFBIG) { app.call(env) } || err(env)
|
79
|
+
end
|
80
|
+
|
81
|
+
end # class
|
82
|
+
end # module
|
@@ -10,9 +10,13 @@ module Rainbows
|
|
10
10
|
G = Rainbows::G
|
11
11
|
HH = Rack::Utils::HeaderHash
|
12
12
|
|
13
|
-
def self.
|
14
|
-
body = response
|
15
|
-
|
13
|
+
def self.write(client, response, out)
|
14
|
+
status, headers, body = response
|
15
|
+
|
16
|
+
body.respond_to?(:to_path) or
|
17
|
+
return HttpResponse.write(client, response, out)
|
18
|
+
|
19
|
+
headers = HH.new(headers)
|
16
20
|
|
17
21
|
# to_io is not part of the Rack spec, but make an exception
|
18
22
|
# here since we can't get here without checking to_path first
|
@@ -39,16 +43,11 @@ module Rainbows
|
|
39
43
|
headers.delete('Transfer-Encoding')
|
40
44
|
headers['Content-Length'] ||= st.size.to_s
|
41
45
|
else # char/block device, directory, whatever... nobody cares
|
42
|
-
return response
|
46
|
+
return HttpResponse.write(client, response, out)
|
43
47
|
end
|
44
48
|
client.defer_body(io, out)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def self.write(client, response, out)
|
49
|
-
response.last.respond_to?(:to_path) and
|
50
|
-
response = defer!(client, response, out)
|
51
|
-
HttpResponse.write(client, response, out)
|
49
|
+
out.nil? or
|
50
|
+
client.write(HttpResponse.header_string(status, headers.to_hash, out))
|
52
51
|
end
|
53
52
|
|
54
53
|
def initialize(io, client, do_chunk, body)
|
data/lib/rainbows/revactor.rb
CHANGED
@@ -21,8 +21,6 @@ module Rainbows
|
|
21
21
|
# concurrency features this model provides.
|
22
22
|
|
23
23
|
module Revactor
|
24
|
-
require 'rainbows/revactor/tee_input'
|
25
|
-
|
26
24
|
RD_ARGS = {}
|
27
25
|
|
28
26
|
include Base
|
@@ -37,7 +35,7 @@ module Rainbows
|
|
37
35
|
rd_args << RD_ARGS
|
38
36
|
client.remote_addr
|
39
37
|
else
|
40
|
-
LOCALHOST
|
38
|
+
Unicorn::HttpRequest::LOCALHOST
|
41
39
|
end
|
42
40
|
buf = client.read(*rd_args)
|
43
41
|
hp = HttpParser.new
|
@@ -52,7 +50,7 @@ module Rainbows
|
|
52
50
|
env[Const::CLIENT_IO] = client
|
53
51
|
env[Const::RACK_INPUT] = 0 == hp.content_length ?
|
54
52
|
HttpRequest::NULL_IO :
|
55
|
-
|
53
|
+
TeeInput.new(PartialSocket.new(client), env, hp, buf)
|
56
54
|
env[Const::REMOTE_ADDR] = remote_addr
|
57
55
|
response = app.call(env.update(RACK_DEFAULTS))
|
58
56
|
|
@@ -137,5 +135,41 @@ module Rainbows
|
|
137
135
|
end
|
138
136
|
end
|
139
137
|
|
138
|
+
# Revactor Sockets do not implement readpartial, so we emulate just
|
139
|
+
# enough to avoid mucking with TeeInput internals. Fortunately
|
140
|
+
# this code is not heavily used so we can usually avoid the overhead
|
141
|
+
# of adding a userspace buffer.
|
142
|
+
class PartialSocket < Struct.new(:socket, :rbuf)
|
143
|
+
def initialize(socket)
|
144
|
+
# IO::Buffer is used internally by Rev which Revactor is based on
|
145
|
+
# so we'll always have it available
|
146
|
+
super(socket, IO::Buffer.new)
|
147
|
+
end
|
148
|
+
|
149
|
+
# Revactor socket reads always return an unspecified amount,
|
150
|
+
# sometimes too much
|
151
|
+
def readpartial(length, dst = "")
|
152
|
+
return dst if length == 0
|
153
|
+
# always check and return from the userspace buffer first
|
154
|
+
rbuf.size > 0 and return dst.replace(rbuf.read(length))
|
155
|
+
|
156
|
+
# read off the socket since there was nothing in rbuf
|
157
|
+
tmp = socket.read
|
158
|
+
|
159
|
+
# we didn't read too much, good, just return it straight back
|
160
|
+
# to avoid needlessly wasting memory bandwidth
|
161
|
+
tmp.size <= length and return dst.replace(tmp)
|
162
|
+
|
163
|
+
# ugh, read returned too much, copy + reread to avoid slicing
|
164
|
+
rbuf << tmp[length, tmp.size]
|
165
|
+
dst.replace(tmp[0, length])
|
166
|
+
end
|
167
|
+
|
168
|
+
# just proxy any remaining methods TeeInput may use
|
169
|
+
def close
|
170
|
+
socket.close
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
140
174
|
end
|
141
175
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
module Rainbows
|
3
|
+
|
4
|
+
# acts like tee(1) on an input input to provide a input-like stream
|
5
|
+
# while providing rewindable semantics through a File/StringIO
|
6
|
+
# backing store. On the first pass, the input is only read on demand
|
7
|
+
# so your Rack application can use input notification (upload progress
|
8
|
+
# and like). This should fully conform to the Rack::InputWrapper
|
9
|
+
# specification on the public API. This class is intended to be a
|
10
|
+
# strict interpretation of Rack::InputWrapper functionality and will
|
11
|
+
# not support any deviations from it.
|
12
|
+
class TeeInput < Unicorn::TeeInput
|
13
|
+
|
14
|
+
# empty class, this is to avoid unecessarily modifying Unicorn::TeeInput
|
15
|
+
# when MaxBody::Limit is included
|
16
|
+
end
|
17
|
+
end
|
data/lib/rainbows.rb
CHANGED
@@ -22,15 +22,19 @@ module Rainbows
|
|
22
22
|
false
|
23
23
|
end
|
24
24
|
end
|
25
|
+
# :stopdoc:
|
25
26
|
G = State.new(true, 0, 0, 5)
|
26
27
|
O = {}
|
28
|
+
# :startdoc:
|
27
29
|
|
28
30
|
require 'rainbows/const'
|
29
31
|
require 'rainbows/http_server'
|
30
32
|
require 'rainbows/http_response'
|
31
33
|
require 'rainbows/base'
|
34
|
+
require 'rainbows/tee_input'
|
32
35
|
autoload :AppPool, 'rainbows/app_pool'
|
33
36
|
autoload :DevFdResponse, 'rainbows/dev_fd_response'
|
37
|
+
autoload :MaxBody, 'rainbows/max_body'
|
34
38
|
|
35
39
|
class << self
|
36
40
|
|
@@ -73,6 +77,20 @@ module Rainbows
|
|
73
77
|
rv
|
74
78
|
rescue Errno::EAGAIN, Errno::ECONNABORTED
|
75
79
|
end
|
80
|
+
|
81
|
+
# returns a string representing the address of the given client +io+
|
82
|
+
# For local UNIX domain sockets, this will return a string referred
|
83
|
+
# to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant.
|
84
|
+
def addr(io)
|
85
|
+
io.respond_to?(:peeraddr) ?
|
86
|
+
io.peeraddr.last : Unicorn::HttpRequest::LOCALHOST
|
87
|
+
end
|
88
|
+
|
89
|
+
# the default max body size is 1 megabyte (1024 * 1024 bytes)
|
90
|
+
@@max_bytes = 1024 * 1024
|
91
|
+
|
92
|
+
def max_bytes; @@max_bytes; end
|
93
|
+
def max_bytes=(nr); @@max_bytes = nr; end
|
76
94
|
end
|
77
95
|
|
78
96
|
# configures \Rainbows! with a given concurrency model to +use+ and
|
@@ -83,6 +101,7 @@ module Rainbows
|
|
83
101
|
# use :Revactor # this may also be :ThreadSpawn or :ThreadPool
|
84
102
|
# worker_connections 400
|
85
103
|
# keepalive_timeout 0 # zero disables keepalives entirely
|
104
|
+
# client_max_body_size 5*1024*1024 # 5 megabytes
|
86
105
|
# end
|
87
106
|
#
|
88
107
|
# # the rest of the Unicorn configuration
|
@@ -94,16 +113,21 @@ module Rainbows
|
|
94
113
|
# +worker_processes+ * +worker_connections+, so in the above example
|
95
114
|
# we can serve 8 * 400 = 3200 clients concurrently.
|
96
115
|
#
|
97
|
-
# The default is +keepalive_timeout+ is
|
116
|
+
# The default is +keepalive_timeout+ is 5 seconds, which should be
|
98
117
|
# enough under most conditions for browsers to render the page and
|
99
118
|
# start retrieving extra elements for. Increasing this beyond 5
|
100
119
|
# seconds is not recommended. Zero disables keepalive entirely
|
101
120
|
# (but pipelining fully-formed requests is still works).
|
121
|
+
#
|
122
|
+
# The default +client_max_body_size+ is 1 megabyte (1024 * 1024 bytes),
|
123
|
+
# setting this to +nil+ will disable body size checks and allow any
|
124
|
+
# size to be specified.
|
102
125
|
def Rainbows!(&block)
|
103
126
|
block_given? or raise ArgumentError, "Rainbows! requires a block"
|
104
127
|
HttpServer.setup(block)
|
105
128
|
end
|
106
129
|
|
130
|
+
# :stopdoc:
|
107
131
|
# maps models to default worker counts, default worker count numbers are
|
108
132
|
# pretty arbitrary and tuning them to your application and hardware is
|
109
133
|
# highly recommended
|
@@ -116,7 +140,6 @@ module Rainbows
|
|
116
140
|
:RevThreadSpawn => 50,
|
117
141
|
:RevThreadPool => 50,
|
118
142
|
:EventMachine => 50,
|
119
|
-
:EventMachineDefer => 50,
|
120
143
|
:FiberSpawn => 50,
|
121
144
|
:FiberPool => 50,
|
122
145
|
:ActorSpawn => 50,
|
@@ -126,6 +149,7 @@ module Rainbows
|
|
126
149
|
u = model.to_s.gsub(/([a-z0-9])([A-Z0-9])/) { "#{$1}_#{$2.downcase!}" }
|
127
150
|
autoload model, "rainbows/#{u.downcase!}"
|
128
151
|
end
|
152
|
+
# :startdoc:
|
129
153
|
autoload :Fiber, 'rainbows/fiber' # core class
|
130
154
|
|
131
155
|
end
|
data/local.mk.sample
CHANGED
@@ -6,39 +6,29 @@
|
|
6
6
|
|
7
7
|
RSYNC = rsync
|
8
8
|
DLEXT := so
|
9
|
-
gems := rack-1.1.0
|
10
|
-
# gems += unicorn-0.96.0 # installed via setup.rb
|
11
|
-
gems += rev-0.3.2
|
12
|
-
gems += iobuffer-0.1.3
|
13
|
-
gems += eventmachine-0.12.10
|
14
|
-
gems += async_sinatra-0.1.5 sinatra-0.9.4
|
15
|
-
gems += espace-neverblock-0.1.6.1
|
16
|
-
|
17
|
-
# Cramp isn't enabled by default since it depends on several prerelease gems
|
18
|
-
ifdef CRAMP
|
19
|
-
gems += cramp-0.7
|
20
|
-
gems += activesupport-3.0.pre
|
21
|
-
gems += activemodel-3.0.pre
|
22
|
-
gems += arel-0.2.pre
|
23
|
-
gems += usher-0.6.2
|
24
|
-
gems += fuzzyhash-0.0.11
|
25
|
-
gems += mysqlplus-0.1.1
|
26
|
-
endif
|
27
9
|
|
28
10
|
# Avoid loading rubygems to speed up tests because gmake is
|
29
11
|
# fork+exec heavy with Ruby.
|
30
12
|
prefix = $(HOME)
|
13
|
+
|
31
14
|
ifeq ($(r19),)
|
32
15
|
RUBY := $(prefix)/bin/ruby
|
33
|
-
gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.8/gems/,$(gems))
|
34
16
|
else
|
35
17
|
prefix := $(prefix)/ruby-1.9
|
36
18
|
export PATH := $(prefix)/bin:$(PATH)
|
37
19
|
RUBY := $(prefix)/bin/ruby --disable-gems
|
38
|
-
gems += case-0.5 revactor-0.1.5
|
39
|
-
gem_paths := $(addprefix $(prefix)/lib/ruby/gems/1.9.1/gems/,$(gems))
|
40
20
|
endif
|
41
21
|
|
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/gems/$(RUBY_VERSION)/gems/*-*)
|
31
|
+
|
42
32
|
ifdef gem_paths
|
43
33
|
sp :=
|
44
34
|
sp +=
|
@@ -55,9 +45,9 @@ TRACER = /usr/bin/time -v -o $(t_pfx).time
|
|
55
45
|
|
56
46
|
full-test: test-18 test-19
|
57
47
|
test-18:
|
58
|
-
$(MAKE) test 2>&1 | sed -
|
48
|
+
$(MAKE) test 2>&1 | sed -e 's!^!1.8 !'
|
59
49
|
test-19:
|
60
|
-
$(MAKE) test r19=
|
50
|
+
$(MAKE) test r19=T 2>&1 | sed -e 's!^!1.9 !'
|
61
51
|
|
62
52
|
latest: NEWS
|
63
53
|
@awk 'BEGIN{RS="=== ";ORS=""}NR==2{sub(/\n$$/,"");print RS""$$0 }' < $<
|
data/rainbows.gemspec
CHANGED
@@ -43,13 +43,13 @@ Gem::Specification.new do |s|
|
|
43
43
|
# we need Unicorn for the HTTP parser and process management
|
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
|
-
s.add_dependency(%q<unicorn>, ["
|
46
|
+
s.add_dependency(%q<unicorn>, [">= 0.97.1", "< 1.0.0"])
|
47
47
|
|
48
48
|
# Unicorn already depends on Rack
|
49
49
|
# s.add_dependency(%q<rack>)
|
50
50
|
|
51
51
|
# optional runtime dependencies depending on configuration
|
52
|
-
# see
|
52
|
+
# see config/isolate.rb for the exact versions we've tested with
|
53
53
|
#
|
54
54
|
# Revactor >= 0.1.5 includes UNIX domain socket support
|
55
55
|
# s.add_dependency(%q<revactor>, [">= 0.1.5"])
|
data/t/app_deferred.ru
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#\-E none
|
2
|
+
# can't use non-compatible middleware that doesn't pass "deferered?" calls
|
3
|
+
#
|
4
|
+
# used for testing deferred actions for Merb and possibly other frameworks
|
5
|
+
# ref: http://brainspl.at/articles/2008/04/18/deferred-requests-with-merb-ebb-and-thin
|
6
|
+
|
7
|
+
class DeferredApp < Struct.new(:app)
|
8
|
+
def deferred?(env)
|
9
|
+
env["PATH_INFO"] == "/deferred"
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(env)
|
13
|
+
env["rack.multithread"] or raise RuntimeError, "rack.multithread not true"
|
14
|
+
body = "#{Thread.current.inspect}\n"
|
15
|
+
headers = {
|
16
|
+
"Content-Type" => "text/plain",
|
17
|
+
"Content-Length" => body.size.to_s,
|
18
|
+
}
|
19
|
+
[ 200, headers, [ body ] ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
run DeferredApp.new
|
@@ -89,16 +89,16 @@ class AsyncApp
|
|
89
89
|
# Get the headers out there asap, let the client know we're alive...
|
90
90
|
EventMachine::next_tick { env['async.callback'].call [200, {'Content-Type' => 'text/plain'}, body] }
|
91
91
|
|
92
|
-
#
|
93
|
-
#
|
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
94
|
# can be serviced.
|
95
95
|
# This could be any callback based thing though, a deferrable waiting on
|
96
|
-
#
|
96
|
+
# IO data, a db request, an http request, an smtp send, whatever.
|
97
97
|
EventMachine::add_timer(1) {
|
98
98
|
body.call ["Woah, async!\n"]
|
99
99
|
|
100
100
|
EventMachine::next_tick {
|
101
|
-
#
|
101
|
+
# This could actually happen any time, you could spawn off to new
|
102
102
|
# threads, pause as a good looking lady walks by, whatever.
|
103
103
|
# Just shows off how we can defer chunks of data in the body, you can
|
104
104
|
# even call this many times.
|
@@ -116,11 +116,11 @@ end
|
|
116
116
|
|
117
117
|
# The additions to env for async.connection and async.callback absolutely
|
118
118
|
# destroy the speed of the request if Lint is doing it's checks on env.
|
119
|
-
#
|
120
|
-
#
|
121
|
-
#
|
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
122
|
# Middleware could possibly catch :async, and also provide a different
|
123
|
-
#
|
123
|
+
# async.connection and async.callback.
|
124
124
|
|
125
125
|
# use Rack::Lint
|
126
126
|
run AsyncApp.new
|
@@ -7,7 +7,7 @@ test -r random_blob || die "random_blob required, run with 'make $0'"
|
|
7
7
|
# So we try to use things like curl and sha1sum that are implemented
|
8
8
|
# without the Ruby interpreter to validate our own Ruby internals.
|
9
9
|
|
10
|
-
t_plan 7 "concurrent rack.input hammer stress test"
|
10
|
+
t_plan 7 "concurrent rack.input hammer stress test (chunked)"
|
11
11
|
|
12
12
|
t_begin "setup and startup" && {
|
13
13
|
rtmpfiles curl_out curl_err
|
@@ -0,0 +1,50 @@
|
|
1
|
+
nr_client=${nr_client-4}
|
2
|
+
. ./test-lib.sh
|
3
|
+
test -r random_blob || die "random_blob required, run with 'make $0'"
|
4
|
+
|
5
|
+
# basically we don't trust our own implementation of content-md5-put
|
6
|
+
# nor our Ruby 1.9 knowledge nor proper use of encodings in Ruby.
|
7
|
+
# So we try to use things like curl and sha1sum that are implemented
|
8
|
+
# without the Ruby interpreter to validate our own Ruby internals.
|
9
|
+
|
10
|
+
t_plan 7 "concurrent rack.input hammer stress test (content-length)"
|
11
|
+
|
12
|
+
t_begin "setup and startup" && {
|
13
|
+
rtmpfiles curl_out curl_err
|
14
|
+
rainbows_setup $model
|
15
|
+
rainbows -D sha1.ru -c $unicorn_config
|
16
|
+
rainbows_wait_start
|
17
|
+
}
|
18
|
+
|
19
|
+
t_begin "send $nr_client concurrent requests" && {
|
20
|
+
start=$(date +%s)
|
21
|
+
for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
|
22
|
+
do
|
23
|
+
(
|
24
|
+
curl -sSf -T random_blob http://$listen/$i \
|
25
|
+
>> $curl_out 2>> $curl_err
|
26
|
+
) &
|
27
|
+
done
|
28
|
+
wait
|
29
|
+
t_info elapsed=$(( $(date +%s) - $start ))
|
30
|
+
}
|
31
|
+
|
32
|
+
t_begin "kill server" && kill $rainbows_pid
|
33
|
+
|
34
|
+
t_begin "got $nr_client responses" && {
|
35
|
+
test $nr_client -eq $(wc -l < $curl_out)
|
36
|
+
}
|
37
|
+
|
38
|
+
t_begin "all responses identical" && {
|
39
|
+
test 1 -eq $(sort < $curl_out | uniq | wc -l)
|
40
|
+
}
|
41
|
+
|
42
|
+
t_begin "sha1 matches on-disk sha1" && {
|
43
|
+
blob_sha1=$(rsha1 < random_blob)
|
44
|
+
t_info blob_sha1=$blob_sha1
|
45
|
+
test x"$blob_sha1" = x"$(sort < $curl_out | uniq)"
|
46
|
+
}
|
47
|
+
|
48
|
+
t_begin "no errors in stderr log" && check_stderr
|
49
|
+
|
50
|
+
t_done
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
test -r random_blob || die "random_blob required, run with 'make $0'"
|
4
|
+
|
5
|
+
t_plan 6 "rack.input client_max_body_size default"
|
6
|
+
|
7
|
+
t_begin "setup and startup" && {
|
8
|
+
rtmpfiles curl_out curl_err cmbs_config
|
9
|
+
rainbows_setup $model
|
10
|
+
grep -v client_max_body_size < $unicorn_config > $cmbs_config
|
11
|
+
rainbows -D sha1-random-size.ru -c $cmbs_config
|
12
|
+
rainbows_wait_start
|
13
|
+
}
|
14
|
+
|
15
|
+
t_begin "regular request" && {
|
16
|
+
rm -f $ok
|
17
|
+
curl -vsSf -T random_blob -H Expect: \
|
18
|
+
http://$listen/ > $curl_out 2> $curl_err || > $ok
|
19
|
+
dbgcat curl_err
|
20
|
+
dbgcat curl_out
|
21
|
+
test -e $ok
|
22
|
+
}
|
23
|
+
|
24
|
+
t_begin "chunked request" && {
|
25
|
+
rm -f $ok
|
26
|
+
curl -vsSf -T- < random_blob -H Expect: \
|
27
|
+
http://$listen/ > $curl_out 2> $curl_err || > $ok
|
28
|
+
dbgcat curl_err
|
29
|
+
dbgcat curl_out
|
30
|
+
test -e $ok
|
31
|
+
}
|
32
|
+
|
33
|
+
t_begin "default size sha1 chunked" && {
|
34
|
+
blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
|
35
|
+
rm -f $ok
|
36
|
+
> $r_err
|
37
|
+
dd if=/dev/zero bs=1048576 count=1 | \
|
38
|
+
curl -vsSf -T- -H Expect: \
|
39
|
+
http://$listen/ > $curl_out 2> $curl_err
|
40
|
+
test "$(cat $curl_out)" = $blob_sha1
|
41
|
+
dbgcat curl_err
|
42
|
+
dbgcat curl_out
|
43
|
+
}
|
44
|
+
|
45
|
+
t_begin "default size sha1 content-length" && {
|
46
|
+
blob_sha1=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
|
47
|
+
rm -f $ok
|
48
|
+
dd if=/dev/zero bs=1048576 count=1 of=$tmp
|
49
|
+
curl -vsSf -T $tmp -H Expect: \
|
50
|
+
http://$listen/ > $curl_out 2> $curl_err
|
51
|
+
test "$(cat $curl_out)" = $blob_sha1
|
52
|
+
dbgcat curl_err
|
53
|
+
dbgcat curl_out
|
54
|
+
}
|
55
|
+
|
56
|
+
t_begin "shutdown" && {
|
57
|
+
kill $rainbows_pid
|
58
|
+
}
|
59
|
+
|
60
|
+
t_done
|
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
. ./test-lib.sh
|
3
|
+
test -r random_blob || die "random_blob required, run with 'make $0'"
|
4
|
+
|
5
|
+
t_plan 6 "rack.input client_max_body_size tiny"
|
6
|
+
|
7
|
+
t_begin "setup and startup" && {
|
8
|
+
rtmpfiles curl_out curl_err cmbs_config
|
9
|
+
rainbows_setup $model
|
10
|
+
sed -e 's/client_max_body_size.*/client_max_body_size 256/' \
|
11
|
+
< $unicorn_config > $cmbs_config
|
12
|
+
rainbows -D sha1-random-size.ru -c $cmbs_config
|
13
|
+
rainbows_wait_start
|
14
|
+
}
|
15
|
+
|
16
|
+
t_begin "stops a regular request" && {
|
17
|
+
rm -f $ok
|
18
|
+
dd if=/dev/zero bs=257 count=1 of=$tmp
|
19
|
+
curl -vsSf -T $tmp -H Expect: \
|
20
|
+
http://$listen/ > $curl_out 2> $curl_err || > $ok
|
21
|
+
dbgcat curl_err
|
22
|
+
dbgcat curl_out
|
23
|
+
test -e $ok
|
24
|
+
}
|
25
|
+
|
26
|
+
t_begin "stops a large chunked request" && {
|
27
|
+
rm -f $ok
|
28
|
+
dd if=/dev/zero bs=257 count=1 | \
|
29
|
+
curl -vsSf -T- -H Expect: \
|
30
|
+
http://$listen/ > $curl_out 2> $curl_err || > $ok
|
31
|
+
dbgcat curl_err
|
32
|
+
dbgcat curl_out
|
33
|
+
test -e $ok
|
34
|
+
}
|
35
|
+
|
36
|
+
t_begin "small size sha1 chunked ok" && {
|
37
|
+
blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
|
38
|
+
rm -f $ok
|
39
|
+
dd if=/dev/zero bs=256 count=1 | \
|
40
|
+
curl -vsSf -T- -H Expect: \
|
41
|
+
http://$listen/ > $curl_out 2> $curl_err
|
42
|
+
dbgcat curl_err
|
43
|
+
dbgcat curl_out
|
44
|
+
test "$(cat $curl_out)" = $blob_sha1
|
45
|
+
}
|
46
|
+
|
47
|
+
t_begin "small size sha1 content-length ok" && {
|
48
|
+
blob_sha1=b376885ac8452b6cbf9ced81b1080bfd570d9b91
|
49
|
+
rm -f $ok
|
50
|
+
dd if=/dev/zero bs=256 count=1 of=$tmp
|
51
|
+
curl -vsSf -T $tmp -H Expect: \
|
52
|
+
http://$listen/ > $curl_out 2> $curl_err
|
53
|
+
dbgcat curl_err
|
54
|
+
dbgcat curl_out
|
55
|
+
test "$(cat $curl_out)" = $blob_sha1
|
56
|
+
}
|
57
|
+
|
58
|
+
t_begin "shutdown" && {
|
59
|
+
kill $rainbows_pid
|
60
|
+
}
|
61
|
+
|
62
|
+
t_done
|