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.
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