rainbows 0.92.0 → 0.93.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.
@@ -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