rainbows 0.92.0 → 0.93.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -99,6 +99,20 @@
99
99
  %td.r19 Yes
100
100
  %td.rbx No
101
101
  %td.slow Yes
102
+ %tr.comp_row
103
+ %td.mod WriterThreadPool
104
+ %td.tee Yes
105
+ %td.r18 Yes
106
+ %td.r19 Yes
107
+ %td.rbx Yes
108
+ %td.slow no
109
+ %tr.comp_row
110
+ %td.mod WriterThreadSpawn
111
+ %td.tee Yes
112
+ %td.r18 Yes
113
+ %td.r19 Yes
114
+ %td.rbx Yes
115
+ %td.slow no
102
116
  %ul
103
117
  %li
104
118
  RevThread* + 1.8 requires Rev >= 0.3.2 for reasonable performance
@@ -203,7 +217,16 @@
203
217
  %a(href="Rainbows/Fiber/IO.html") Rainbows::Fiber::IO
204
218
  %td.thr No
205
219
  %td.reent Yes
206
-
220
+ %tr.comp_base
221
+ %td.mod WriterThreadPool
222
+ %td.slowio avoid
223
+ %td.thr Maybe
224
+ %td.reent Maybe
225
+ %tr.comp_base
226
+ %td.mod WriterThreadSpawn
227
+ %td.slowio avoid
228
+ %td.thr Maybe
229
+ %td.reent Maybe
207
230
  %ul
208
231
  %li
209
232
  Requirements for single thread reentrancy are loose in that there is
@@ -219,6 +242,9 @@
219
242
  on UNIX domain sockets and named pipes. Nearly all other operations
220
243
  on POSIX filesystems can be considered "fast", or at least
221
244
  uninterruptible.
245
+ %li
246
+ WriterThread{Pool,Spawn} will require thread safety if your response
247
+ body is dynamically generated during the body#each call.
222
248
 
223
249
  %h2 middlewares and frameworks
224
250
  %br
@@ -250,7 +276,7 @@
250
276
  %td.ws Sunshowers
251
277
  %tr.comp_row
252
278
  %td.mod ThreadPool
253
- %td.devfd no-op
279
+ %td.devfd Yes
254
280
  %td.app_pool Yes
255
281
  %td.lock Yes
256
282
  %td.async standard Ruby
@@ -264,7 +290,7 @@
264
290
  %td.ws no
265
291
  %tr.comp_row
266
292
  %td.mod ThreadSpawn
267
- %td.devfd no-op
293
+ %td.devfd Yes
268
294
  %td.app_pool Yes
269
295
  %td.lock Yes
270
296
  %td.async standard Ruby
@@ -316,15 +342,28 @@
316
342
  %td.devfd Yes
317
343
  %td.app_pool Yes
318
344
  %td.lock Dumb
319
- %td.async standard Ruby
345
+ %td.async Rev, standard Ruby
320
346
  %td.ws no
321
347
  %tr.comp_row
322
348
  %td.mod RevFiberSpawn
323
349
  %td.devfd Yes
324
350
  %td.app_pool Yes
325
351
  %td.lock No!
326
- %td.async Rainbows::Fiber::IO, Rainbows.sleep
327
-
352
+ %td.async Rev, Rainbows::Fiber::IO, Rainbows.sleep
353
+ %tr.comp_row
354
+ %td.mod WriterThreadPool
355
+ %td.devfd Yes
356
+ %td.app_pool no-op
357
+ %td.lock no-op
358
+ %td.async Standard Ruby in response body only
359
+ %td.ws response body only
360
+ %tr.comp_row
361
+ %td.mod WriterThreadSpawn
362
+ %td.devfd Yes
363
+ %td.app_pool no-op
364
+ %td.lock no-op
365
+ %td.async Standard Ruby in response body only
366
+ %td.ws response body only
328
367
  %ul
329
368
  %li
330
369
  "No!" means it's fundamentally incompatible, use an
data/FAQ CHANGED
@@ -62,13 +62,19 @@ to ensure redirects go to "https://" URLs.
62
62
 
63
63
  === Is there a "rainbows_rails" command like there is "unicorn_rails"?
64
64
 
65
- Only if you write one and plan to support it.
65
+ No.
66
66
 
67
67
  "unicorn_rails" was written primarily to support older versions of
68
- Rails. Since \Rainbows! is designed for newer Rails, it can just use
69
- a "config.ru" file like other Rack frameworks and applications.
68
+ Rails. Since \Rainbows! is designed for newer applications based on
69
+ Rack, it can just use a "config.ru" file like other Rack frameworks and
70
+ applications.
70
71
 
71
- For Rails 2.3.x and later, the following config.ru will work for you:
72
+ For Rails 3.x, you should already have a config.ru file and
73
+ "rainbows(1)" will work out-of-the-box like "rackup(1)". Rails 3
74
+ will support RACK_ENV as set by "rainbows(1)", so you won't need
75
+ to set RAILS_ENV.
76
+
77
+ For Rails 2.3.x, the following config.ru will work for you:
72
78
 
73
79
  ENV["RAILS_ENV"] ||= ENV["RACK_ENV"]
74
80
  require "config/environment"
data/GIT-VERSION-GEN CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
 
3
3
  GVF=GIT-VERSION-FILE
4
- DEF_VER=v0.92.0.GIT
4
+ DEF_VER=v0.93.0.GIT
5
5
 
6
6
  LF='
7
7
  '
data/GNUmakefile CHANGED
@@ -17,8 +17,8 @@ ifeq ($(RUBY_VERSION),)
17
17
  endif
18
18
 
19
19
  # rake takes forever to start
20
- isolate: tmp/gems/$(RUBY_VERSION)/.isolate
21
- tmp/gems/$(RUBY_VERSION)/.isolate: $(ISOLATE_CONFIG)
20
+ isolate: tmp/isolate/ruby-$(RUBY_VERSION)/.isolate
21
+ tmp/isolate/ruby-$(RUBY_VERSION)/.isolate: $(ISOLATE_CONFIG)
22
22
  ISOLATE_CONFIG=$(ISOLATE_CONFIG) $(RAKE) isolate
23
23
  > $@
24
24
 
@@ -66,7 +66,7 @@ NEWS: GIT-VERSION-FILE
66
66
  $(RAKE) -s news_rdoc > $@+
67
67
  mv $@+ $@
68
68
 
69
- SINCE = 0.91.1
69
+ SINCE = 0.92.0
70
70
  ChangeLog: LOG_VERSION = \
71
71
  $(shell git rev-parse -q "$(GIT_VERSION)" >/dev/null 2>&1 && \
72
72
  echo $(GIT_VERSION) || git describe)
data/README CHANGED
@@ -24,6 +24,8 @@ For network concurrency, models we currently support are:
24
24
  * {FiberPool}[link:Rainbows/FiberPool.html]
25
25
  * {NeverBlock}[link:Rainbows/NeverBlock.html]
26
26
  * {RevThreadPool}[link:Rainbows/RevThreadPool.html]
27
+ * {WriterThreadPool}[link:Rainbows/WriterThreadPool.html]
28
+ * {WriterThreadSpawn}[link:Rainbows/WriterThreadSpawn.html]
27
29
 
28
30
  We have {many more on the way}[link:TODO.html] for handling network
29
31
  concurrency. Additionally, we also use multiple processes (managed by
data/Rakefile CHANGED
@@ -187,6 +187,20 @@ end
187
187
  desc 'isolate gems for development'
188
188
  task :isolate do
189
189
  require 'isolate'
190
- Isolate.gems "tmp/gems/#{RUBY_VERSION}",
191
- :file => ENV['ISOLATE_CONFIG']
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
192
206
  end
data/config/isolate.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # `rake isolate' or (faster in the unmodified case, `make isolate')
5
5
 
6
6
  gem 'rack', '1.1.0'
7
- gem 'unicorn', '0.97.1'
7
+ gem 'unicorn', '0.99.0'
8
8
 
9
9
  gem 'iobuffer', '0.1.3'
10
10
  gem 'rev', '0.3.2'
@@ -22,4 +22,4 @@ if defined?(::Fiber)
22
22
  gem 'rack-fiber_pool', '0.9.0'
23
23
  end
24
24
 
25
- gem 'cramp', '0.10'
25
+ gem 'cramp', '0.11'
data/lib/rainbows.rb CHANGED
@@ -35,6 +35,7 @@ module Rainbows
35
35
  autoload :AppPool, 'rainbows/app_pool'
36
36
  autoload :DevFdResponse, 'rainbows/dev_fd_response'
37
37
  autoload :MaxBody, 'rainbows/max_body'
38
+ autoload :QueuePool, 'rainbows/queue_pool'
38
39
 
39
40
  class << self
40
41
 
@@ -133,6 +134,8 @@ module Rainbows
133
134
  # highly recommended
134
135
  MODEL_WORKER_CONNECTIONS = {
135
136
  :Base => 1, # this one can't change
137
+ :WriterThreadPool => 20,
138
+ :WriterThreadSpawn => 20,
136
139
  :Revactor => 50,
137
140
  :ThreadSpawn => 30,
138
141
  :ThreadPool => 20,
data/lib/rainbows/base.rb CHANGED
@@ -49,6 +49,8 @@ module Rainbows
49
49
  end
50
50
  end
51
51
 
52
+ module_function :write_body
53
+
52
54
  # once a client is accepted, it is processed in its entirety here
53
55
  # in 3 easy steps: read request, call app, write app response
54
56
  # this is used by synchronous concurrency models
@@ -3,7 +3,7 @@
3
3
  module Rainbows
4
4
 
5
5
  module Const
6
- RAINBOWS_VERSION = '0.92.0'
6
+ RAINBOWS_VERSION = '0.93.0'
7
7
 
8
8
  include Unicorn::Const
9
9
 
@@ -0,0 +1,32 @@
1
+ # -*- encoding: binary -*-
2
+ require 'thread'
3
+
4
+ module Rainbows
5
+
6
+ # Thread pool class based on pulling off a single Ruby Queue.
7
+ # This is NOT used for the ThreadPool class, since that class does not
8
+ # need a userspace Queue.
9
+ class QueuePool < Struct.new(:queue, :threads)
10
+ G = Rainbows::G
11
+
12
+ def initialize(size = 20, &block)
13
+ q = Queue.new
14
+ self.threads = (1..size).map do
15
+ Thread.new do
16
+ while job = q.shift
17
+ block.call(job)
18
+ end
19
+ end
20
+ end
21
+ self.queue = q
22
+ end
23
+
24
+ def quit!
25
+ threads.each { |_| queue << nil }
26
+ threads.delete_if do |t|
27
+ G.tick
28
+ t.alive? ? t.join(0.01) : true
29
+ end until threads.empty?
30
+ end
31
+ end
32
+ end
@@ -33,6 +33,9 @@ module Rainbows
33
33
  # synchronization primitives for _every_ case, not just this
34
34
  # unlikely one. Since this case is (or should be) uncommon,
35
35
  # just busy wait when we have to.
36
+ # We don't use Thread.pass because it needlessly spins the
37
+ # CPU during I/O wait, CPU cycles that can be better used
38
+ # by other worker _processes_.
36
39
  sleep(0.01)
37
40
  elsif c = Rainbows.sync_accept(l)
38
41
  klass.new(c) do |c|
@@ -0,0 +1,90 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ module Rainbows
4
+
5
+ # This concurrency model implements a single-threaded app dispatch
6
+ # with a separate thread pool for writing responses.
7
+ #
8
+ # Unlike most \Rainbows! concurrency models, WriterThreadPool is
9
+ # designed to run behind nginx just like Unicorn is. This concurrency
10
+ # model may be useful for existing Unicorn users looking for more
11
+ # output concurrency than socket buffers can provide while still
12
+ # maintaining a single-threaded application dispatch (though if the
13
+ # response body is dynamically generated, it must be thread safe).
14
+ #
15
+ # For serving large or streaming responses, using more threads (via
16
+ # the +worker_connections+ setting) and setting "proxy_buffering off"
17
+ # in nginx is recommended. If your application does not handle
18
+ # uploads, then using any HTTP-aware proxy like haproxy is fine.
19
+ # Using a non-HTTP-aware proxy will leave you vulnerable to
20
+ # slow client denial-of-service attacks.
21
+
22
+ module WriterThreadPool
23
+ include Base
24
+
25
+ # used to wrap a BasicSocket to use with +q+ for all writes
26
+ # this is compatible with IO.select
27
+ class QueueSocket < Struct.new(:to_io, :q)
28
+ def readpartial(size, buf = "")
29
+ to_io.readpartial(size, buf)
30
+ end
31
+
32
+ def write_nonblock(buf)
33
+ to_io.write_nonblock(buf)
34
+ end
35
+
36
+ def write(buf)
37
+ q << [ to_io, buf ]
38
+ end
39
+
40
+ def close
41
+ q << [ to_io, :close ]
42
+ end
43
+
44
+ def closed?
45
+ false
46
+ end
47
+ end
48
+
49
+ alias base_write_body write_body
50
+ if IO.respond_to?(:copy_stream)
51
+ undef_method :write_body
52
+
53
+ def write_body(qclient, body)
54
+ qclient.q << [ qclient.to_io, :body, body ]
55
+ end
56
+ end
57
+
58
+ @@nr = 0
59
+ @@q = nil
60
+
61
+ def process_client(client)
62
+ @@nr += 1
63
+ super(QueueSocket[client, @@q[@@nr %= @@q.size]])
64
+ end
65
+
66
+ def worker_loop(worker)
67
+ # we have multiple, single-thread queues since we don't want to
68
+ # interleave writes from the same client
69
+ qp = (1..worker_connections).map do |n|
70
+ QueuePool.new(1) do |response|
71
+ begin
72
+ io, arg1, arg2 = response
73
+ case arg1
74
+ when :body then base_write_body(io, arg2)
75
+ when :close then io.close unless io.closed?
76
+ else
77
+ io.write(arg1)
78
+ end
79
+ rescue => err
80
+ Error.app(err)
81
+ end
82
+ end
83
+ end
84
+
85
+ @@q = qp.map { |q| q.queue }
86
+ super(worker) # accept loop from Unicorn
87
+ qp.map { |q| q.quit! }
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,113 @@
1
+ # -*- encoding: binary -*-
2
+ require 'thread'
3
+ module Rainbows
4
+
5
+ # This concurrency model implements a single-threaded app dispatch and
6
+ # spawns a new thread for writing responses. This concurrency model
7
+ # should be ideal for apps that serve large responses or stream
8
+ # responses slowly.
9
+ #
10
+ # Unlike most \Rainbows! concurrency models, WriterThreadSpawn is
11
+ # designed to run behind nginx just like Unicorn is. This concurrency
12
+ # model may be useful for existing Unicorn users looking for more
13
+ # output concurrency than socket buffers can provide while still
14
+ # maintaining a single-threaded application dispatch (though if the
15
+ # response body is generated on-the-fly, it must be thread safe).
16
+ #
17
+ # For serving large or streaming responses, setting
18
+ # "proxy_buffering off" in nginx is recommended. If your application
19
+ # does not handle uploads, then using any HTTP-aware proxy like
20
+ # haproxy is fine. Using a non-HTTP-aware proxy will leave you
21
+ # vulnerable to slow client denial-of-service attacks.
22
+
23
+ module WriterThreadSpawn
24
+ include Base
25
+
26
+ CUR = {}
27
+
28
+ # used to wrap a BasicSocket to use with +q+ for all writes
29
+ # this is compatible with IO.select
30
+ class MySocket < Struct.new(:to_io, :q, :thr)
31
+ def readpartial(size, buf = "")
32
+ to_io.readpartial(size, buf)
33
+ end
34
+
35
+ def write_nonblock(buf)
36
+ to_io.write_nonblock(buf)
37
+ end
38
+
39
+ def queue_writer
40
+ # not using Thread.pass here because that spins the CPU during
41
+ # I/O wait and will eat cycles from other worker processes.
42
+ until CUR.size < MAX
43
+ CUR.delete_if { |t,_|
44
+ t.alive? ? t.join(0) : true
45
+ }.size >= MAX and sleep(0.01)
46
+ end
47
+
48
+ q = Queue.new
49
+ self.thr = Thread.new(to_io, q) do |io, q|
50
+ while response = q.shift
51
+ begin
52
+ arg1, arg2 = response
53
+ case arg1
54
+ when :body then Base.write_body(io, arg2)
55
+ when :close
56
+ io.close unless io.closed?
57
+ break
58
+ else
59
+ io.write(arg1)
60
+ end
61
+ rescue => e
62
+ Error.app(e)
63
+ end
64
+ end
65
+ CUR.delete(Thread.current)
66
+ end
67
+ CUR[thr] = q
68
+ end
69
+
70
+ def write(buf)
71
+ (self.q ||= queue_writer) << buf
72
+ end
73
+
74
+ def write_body(body)
75
+ (self.q ||= queue_writer) << [ :body, body ]
76
+ end
77
+
78
+ def close
79
+ if q
80
+ q << :close
81
+ else
82
+ to_io.close
83
+ end
84
+ end
85
+
86
+ def closed?
87
+ false
88
+ end
89
+ end
90
+
91
+ if IO.respond_to?(:copy_stream)
92
+ undef_method :write_body
93
+
94
+ def write_body(my_sock, body)
95
+ my_sock.write_body(body)
96
+ end
97
+ end
98
+
99
+ def process_client(client)
100
+ super(MySocket[client])
101
+ end
102
+
103
+ def worker_loop(worker)
104
+ MySocket.const_set(:MAX, worker_connections)
105
+ super(worker) # accept loop from Unicorn
106
+ CUR.delete_if do |t,q|
107
+ q << nil
108
+ G.tick
109
+ t.alive? ? t.join(0.01) : true
110
+ end until CUR.empty?
111
+ end
112
+ end
113
+ end
data/local.mk.sample CHANGED
@@ -27,7 +27,7 @@ endif
27
27
  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
28
28
 
29
29
  updir := $(shell git rev-parse --show-cdup)
30
- gem_paths := $(wildcard $(updir)tmp/gems/$(RUBY_VERSION)/gems/*-*)
30
+ gem_paths := $(wildcard $(updir)tmp/isolate/ruby-$(RUBY_VERSION)/gems/*-*)
31
31
 
32
32
  ifdef gem_paths
33
33
  sp :=
data/rainbows.gemspec CHANGED
@@ -43,7 +43,7 @@ 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", "< 1.0.0"])
46
+ s.add_dependency(%q<unicorn>, [">= 0.97.1", "< 2.0.0"])
47
47
 
48
48
  # Unicorn already depends on Rack
49
49
  # s.add_dependency(%q<rack>)
data/t/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  /test-bin-*
3
3
  /random_blob
4
4
  /.dep+*
5
+ /trash
data/t/GNUmakefile CHANGED
@@ -22,6 +22,8 @@ else
22
22
  endif
23
23
  export RUBYLIB RUBY_VERSION
24
24
 
25
+ models += WriterThreadPool
26
+ models += WriterThreadSpawn
25
27
  models += ThreadPool
26
28
  models += ThreadSpawn
27
29
  models += Rev
@@ -0,0 +1,9 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] && env['rainbows.model'] == :WriterThreadPool
5
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
6
+ else
7
+ raise "rack.multithread is false"
8
+ end
9
+ }
@@ -0,0 +1,9 @@
1
+ use Rack::ContentLength
2
+ use Rack::ContentType
3
+ run lambda { |env|
4
+ if env['rack.multithread'] && env['rainbows.model'] == :WriterThreadSpawn
5
+ [ 200, {}, [ Thread.current.inspect << "\n" ] ]
6
+ else
7
+ raise "rack.multithread is false"
8
+ end
9
+ }
@@ -1,7 +1,7 @@
1
1
  #!/bin/sh
2
2
  . ./test-lib.sh
3
3
 
4
- t_plan 9 "heartbeat/timeout test for $model"
4
+ t_plan 12 "heartbeat/timeout test for $model"
5
5
 
6
6
  t_begin "setup and startup" && {
7
7
  rainbows_setup $model
@@ -22,8 +22,9 @@ t_begin "sleep for a bit, ensure worker PID does not change" && {
22
22
  }
23
23
 
24
24
  t_begin "block the worker process to force it to die" && {
25
+ rm $ok
25
26
  t0=$(date +%s)
26
- err="$(curl -sSf http://$listen/block-forever 2>&1 || :)"
27
+ err="$(curl -sSf http://$listen/block-forever 2>&1 || > $ok)"
27
28
  t1=$(date +%s)
28
29
  elapsed=$(($t1 - $t0))
29
30
  t_info "elapsed=$elapsed err=$err"
@@ -31,9 +32,13 @@ t_begin "block the worker process to force it to die" && {
31
32
  test x"$err" != x"$worker_pid"
32
33
  }
33
34
 
34
- t_begin "ensure timeout took 3-6 seconds" && {
35
+ t_begin "ensure worker was killed" && {
36
+ test -e $ok
37
+ test 1 -eq $(grep timeout $r_err | grep killing | wc -l)
38
+ }
39
+
40
+ t_begin "ensure timeout took at least 3 seconds" && {
35
41
  test $elapsed -ge 3
36
- test $elapsed -le 6 # give it some slack in case box is bogged down
37
42
  }
38
43
 
39
44
  t_begin "wait for new worker to start up" && {
@@ -45,6 +50,10 @@ t_begin "we get a fresh new worker process" && {
45
50
  test $new_worker_pid -ne $worker_pid
46
51
  }
47
52
 
53
+ t_begin "truncate the server error log" && {
54
+ > $r_err
55
+ }
56
+
48
57
  t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && {
49
58
  kill -STOP $rainbows_pid
50
59
  sleep 4
@@ -54,9 +63,11 @@ t_begin "SIGSTOP and SIGCONT on rainbows master does not kill worker" && {
54
63
  }
55
64
 
56
65
  t_begin "stop server" && {
57
- kill $rainbows_pid
66
+ kill -QUIT $rainbows_pid
58
67
  }
59
68
 
69
+ t_begin "check stderr" && check_stderr
70
+
60
71
  dbgcat r_err
61
72
 
62
73
  t_done
@@ -12,7 +12,7 @@ t_plan 10 "large file response slurp avoidance for $model"
12
12
 
13
13
  t_begin "setup and startup" && {
14
14
  rtmpfiles curl_out
15
- rainbows_setup $model
15
+ rainbows_setup $model 1
16
16
  # can't load Rack::Lint here since it'll cause Rev to slurp
17
17
  rainbows -E none -D large-file-response.ru -c $unicorn_config
18
18
  rainbows_wait_start
@@ -50,11 +50,11 @@ t_begin "HTTP/1.0 test" && {
50
50
  t_begin "HTTP/0.9 test" && {
51
51
  (
52
52
  printf 'GET /random_blob\r\n'
53
- cat $fifo > $tmp &
53
+ rsha1 < $fifo > $tmp &
54
54
  wait
55
55
  echo ok > $ok
56
56
  ) | socat - TCP:$listen > $fifo
57
- cmp $tmp random_blob
57
+ test $(cat $tmp) = $(rsha1 < random_blob)
58
58
  test xok = x$(cat $ok)
59
59
  }
60
60
 
@@ -72,8 +72,12 @@ t_begin "shutdown server" && {
72
72
 
73
73
  t_begin "compare RSS before and after" && {
74
74
  diff=$(( $rss_after - $rss_before ))
75
+
76
+ # default GC malloc limit in MRI:
77
+ fudge=$(( 8 * 1024 * 1024 ))
78
+
75
79
  t_info "test diff=$diff < orig=$random_blob_size"
76
- test $diff -le $random_blob_size
80
+ test $diff -le $(( $random_blob_size + $fudge ))
77
81
  }
78
82
 
79
83
  dbgcat r_err
@@ -32,7 +32,12 @@ t_begin "nuke the master once we're connected" && {
32
32
  }
33
33
 
34
34
  t_begin "worker is no longer running" && {
35
- sleep 6
35
+ nr=30
36
+ while kill -0 $worker_pid 2>/dev/null && test $nr -gt 0
37
+ do
38
+ nr=$(( $nr - 1))
39
+ sleep 1
40
+ done
36
41
  kill -0 $worker_pid 2> $tmp && false
37
42
  test -s $tmp
38
43
  }
@@ -8,7 +8,7 @@ t_begin "setup and startup" && {
8
8
  rtmpfiles curl_out curl_err
9
9
  rainbows_setup $model
10
10
  rainbows -D sha1-random-size.ru -c $unicorn_config
11
- blob_sha1=$(rsha1 random_blob)
11
+ blob_sha1=$(rsha1 < random_blob)
12
12
  t_info "blob_sha1=$blob_sha1"
13
13
  rainbows_wait_start
14
14
  }
@@ -1,6 +1,7 @@
1
1
  #!/bin/sh
2
2
  . ./test-lib.sh
3
3
  test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ req_curl_chunked_upload_err_check
4
5
 
5
6
  t_plan 6 "rack.input client_max_body_size default"
6
7
 
@@ -1,6 +1,7 @@
1
1
  #!/bin/sh
2
2
  . ./test-lib.sh
3
3
  test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ req_curl_chunked_upload_err_check
4
5
 
5
6
  t_plan 6 "rack.input client_max_body_size tiny"
6
7
 
@@ -1,5 +1,7 @@
1
1
  #!/bin/sh
2
2
  . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ req_curl_chunked_upload_err_check
3
5
 
4
6
  t_plan 10 "rack.input client_max_body_size bigger"
5
7
 
@@ -2,6 +2,8 @@
2
2
  CONFIG_RU=${CONFIG_RU-'async-response.ru'}
3
3
  . ./test-lib.sh
4
4
 
5
+ skip_models Base WriterThreadPool WriterThreadSpawn
6
+
5
7
  case $CONFIG_RU in
6
8
  *no-autochunk.ru)
7
9
  t_plan 7 "async response w/o autochunk for $model"
data/t/test-lib.sh CHANGED
@@ -42,6 +42,19 @@ require_check () {
42
42
  fi
43
43
  }
44
44
 
45
+ skip_models () {
46
+ for i in "$@"
47
+ do
48
+ if test x"$model" != x"$i"
49
+ then
50
+ continue
51
+ fi
52
+ t_info "skipping $T since it is not compatible with $model"
53
+ exit 0
54
+ done
55
+ }
56
+
57
+
45
58
  # given a list of variable names, create temporary files and assign
46
59
  # the pathnames to those variables
47
60
  rtmpfiles () {
@@ -146,7 +159,22 @@ rsha1 () {
146
159
 
147
160
  # last resort, see comments in sha1sum.rb for reasoning
148
161
  test -n "$_cmd" || _cmd=sha1sum.rb
149
- expr "$($_cmd < random_blob)" : '\([a-f0-9]\{40\}\)'
162
+ expr "$($_cmd)" : '\([a-f0-9]\{40\}\)'
163
+ }
164
+
165
+ req_curl_chunked_upload_err_check () {
166
+ set +e
167
+ curl --version 2>/dev/null | awk '$1 == "curl" {
168
+ split($2, v, /\./)
169
+ if ((v[1] < 7) || (v[1] == 7 && v[2] < 18))
170
+ code = 1
171
+ }
172
+ END { exit(code) }'
173
+ if test $? -ne 0
174
+ then
175
+ t_info "curl >= 7.18.0 required for $T"
176
+ exit 0
177
+ fi
150
178
  }
151
179
 
152
180
  case $model in
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.92.0
4
+ version: 0.93.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-05-04 00:00:00 +00:00
12
+ date: 2010-05-29 00:00:00 +00:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -23,7 +23,7 @@ dependencies:
23
23
  version: 0.97.1
24
24
  - - <
25
25
  - !ruby/object:Gem::Version
26
- version: 1.0.0
26
+ version: 2.0.0
27
27
  version:
28
28
  description: |-
29
29
  \Rainbows! is an HTTP server for sleepy Rack applications. It is based on
@@ -61,6 +61,7 @@ extra_rdoc_files:
61
61
  - lib/rainbows/max_body.rb
62
62
  - lib/rainbows/never_block.rb
63
63
  - lib/rainbows/never_block/event_machine.rb
64
+ - lib/rainbows/queue_pool.rb
64
65
  - lib/rainbows/rev.rb
65
66
  - lib/rainbows/rev/client.rb
66
67
  - lib/rainbows/rev/core.rb
@@ -75,6 +76,8 @@ extra_rdoc_files:
75
76
  - lib/rainbows/tee_input.rb
76
77
  - lib/rainbows/thread_pool.rb
77
78
  - lib/rainbows/thread_spawn.rb
79
+ - lib/rainbows/writer_thread_pool.rb
80
+ - lib/rainbows/writer_thread_spawn.rb
78
81
  - LICENSE
79
82
  - NEWS
80
83
  - README
@@ -131,6 +134,7 @@ files:
131
134
  - lib/rainbows/max_body.rb
132
135
  - lib/rainbows/never_block.rb
133
136
  - lib/rainbows/never_block/event_machine.rb
137
+ - lib/rainbows/queue_pool.rb
134
138
  - lib/rainbows/rev.rb
135
139
  - lib/rainbows/rev/client.rb
136
140
  - lib/rainbows/rev/core.rb
@@ -145,6 +149,8 @@ files:
145
149
  - lib/rainbows/tee_input.rb
146
150
  - lib/rainbows/thread_pool.rb
147
151
  - lib/rainbows/thread_spawn.rb
152
+ - lib/rainbows/writer_thread_pool.rb
153
+ - lib/rainbows/writer_thread_spawn.rb
148
154
  - local.mk.sample
149
155
  - man/man1/rainbows.1
150
156
  - rainbows.gemspec
@@ -187,6 +193,8 @@ files:
187
193
  - t/simple-http_Revactor.ru
188
194
  - t/simple-http_ThreadPool.ru
189
195
  - t/simple-http_ThreadSpawn.ru
196
+ - t/simple-http_WriterThreadPool.ru
197
+ - t/simple-http_WriterThreadSpawn.ru
190
198
  - t/sleep.ru
191
199
  - t/t0000-simple-http.sh
192
200
  - t/t0000.ru