rainbows 0.91.1 → 0.92.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.document +1 -1
  2. data/.gitignore +1 -0
  3. data/.manifest +13 -4
  4. data/ChangeLog +237 -219
  5. data/DEPLOY +13 -13
  6. data/Documentation/comparison.haml +1 -1
  7. data/GIT-VERSION-FILE +1 -1
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +18 -7
  10. data/NEWS +29 -0
  11. data/README +5 -4
  12. data/Rakefile +7 -0
  13. data/SIGNALS +5 -1
  14. data/TODO +2 -5
  15. data/config/.gitignore +1 -0
  16. data/config/isolate.rb +25 -0
  17. data/lib/rainbows/base.rb +32 -8
  18. data/lib/rainbows/const.rb +3 -2
  19. data/lib/rainbows/ev_core.rb +42 -2
  20. data/lib/rainbows/event_machine.rb +48 -5
  21. data/lib/rainbows/fiber/base.rb +4 -2
  22. data/lib/rainbows/fiber/rev.rb +1 -1
  23. data/lib/rainbows/http_response.rb +21 -20
  24. data/lib/rainbows/http_server.rb +13 -6
  25. data/lib/rainbows/max_body.rb +82 -0
  26. data/lib/rainbows/rev/deferred_response.rb +10 -11
  27. data/lib/rainbows/revactor.rb +38 -4
  28. data/lib/rainbows/tee_input.rb +17 -0
  29. data/lib/rainbows.rb +26 -2
  30. data/local.mk.sample +13 -23
  31. data/rainbows.gemspec +2 -2
  32. data/t/app_deferred.ru +23 -0
  33. data/t/async_examples/async_app.ru +8 -8
  34. data/t/rack-fiber_pool/app.ru +5 -0
  35. data/t/{t0100-rack-input-hammer.sh → t0100-rack-input-hammer-chunked.sh} +1 -1
  36. data/t/t0100-rack-input-hammer-content-length.sh +50 -0
  37. data/t/t0103-rack-input-limit.sh +60 -0
  38. data/t/t0104-rack-input-limit-tiny.sh +62 -0
  39. data/t/t0105-rack-input-limit-bigger.sh +105 -0
  40. data/t/t0401-em-async-tailer.sh +1 -1
  41. data/t/t0600-rack-fiber_pool.sh +49 -0
  42. data/t/t0700-app-deferred.sh +45 -0
  43. data/t/test-lib.sh +1 -0
  44. metadata +22 -10
  45. data/lib/rainbows/event_machine_defer.rb +0 -59
  46. data/lib/rainbows/revactor/tee_input.rb +0 -52
  47. 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.defer!(client, response, out)
14
- body = response.last
15
- headers = HH.new(response[1])
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
- [ response.first, headers.to_hash, [] ]
46
- end
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)
@@ -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
- Rainbows::Revactor::TeeInput.new(client, env, hp, buf)
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 2 seconds, which should be
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 -u -e 's!^!1.8 !'
48
+ $(MAKE) test 2>&1 | sed -e 's!^!1.8 !'
59
49
  test-19:
60
- $(MAKE) test r19=t 2>&1 | sed -u -e 's!^!1.9 !'
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>, ["~> 0.97.1"])
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 local.mk.sample for the exact versions we've tested with
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
- # 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
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
- # IO data, a db request, an http request, an smtp send, whatever.
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
- # This could actually happen any time, you could spawn off to new
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
- # 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.
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
- # async.connection and async.callback.
123
+ # async.connection and async.callback.
124
124
 
125
125
  # use Rack::Lint
126
126
  run AsyncApp.new
@@ -0,0 +1,5 @@
1
+ require 'rack/fiber_pool'
2
+ use Rack::FiberPool
3
+ use Rack::ContentLength
4
+ use Rack::ContentType, 'text/plain'
5
+ run lambda { |env| [ 200, {}, [ "#{Fiber.current}\n" ] ] }
@@ -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