rainbows 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/.document +1 -0
  2. data/COPYING +617 -282
  3. data/Documentation/comparison.haml +81 -24
  4. data/FAQ +3 -0
  5. data/GIT-VERSION-GEN +1 -1
  6. data/LICENSE +14 -5
  7. data/README +10 -9
  8. data/Sandbox +25 -0
  9. data/TODO +2 -22
  10. data/lib/rainbows.rb +50 -49
  11. data/lib/rainbows/client.rb +6 -5
  12. data/lib/rainbows/configurator.rb +191 -37
  13. data/lib/rainbows/const.rb +1 -1
  14. data/lib/rainbows/coolio.rb +4 -1
  15. data/lib/rainbows/coolio/client.rb +2 -2
  16. data/lib/rainbows/coolio/heartbeat.rb +2 -1
  17. data/lib/rainbows/coolio_fiber_spawn.rb +12 -7
  18. data/lib/rainbows/coolio_thread_pool.rb +19 -10
  19. data/lib/rainbows/coolio_thread_spawn.rb +3 -0
  20. data/lib/rainbows/epoll.rb +27 -5
  21. data/lib/rainbows/epoll/client.rb +3 -3
  22. data/lib/rainbows/ev_core.rb +2 -1
  23. data/lib/rainbows/event_machine.rb +4 -0
  24. data/lib/rainbows/event_machine/client.rb +2 -1
  25. data/lib/rainbows/fiber.rb +5 -0
  26. data/lib/rainbows/fiber/base.rb +1 -0
  27. data/lib/rainbows/fiber/coolio/methods.rb +0 -1
  28. data/lib/rainbows/fiber/io.rb +10 -6
  29. data/lib/rainbows/fiber/io/pipe.rb +6 -1
  30. data/lib/rainbows/fiber/io/socket.rb +6 -1
  31. data/lib/rainbows/fiber_pool.rb +12 -7
  32. data/lib/rainbows/fiber_spawn.rb +11 -6
  33. data/lib/rainbows/http_server.rb +55 -59
  34. data/lib/rainbows/join_threads.rb +4 -0
  35. data/lib/rainbows/max_body.rb +29 -10
  36. data/lib/rainbows/never_block.rb +7 -10
  37. data/lib/rainbows/pool_size.rb +14 -0
  38. data/lib/rainbows/process_client.rb +23 -1
  39. data/lib/rainbows/queue_pool.rb +8 -6
  40. data/lib/rainbows/response.rb +12 -11
  41. data/lib/rainbows/revactor.rb +14 -7
  42. data/lib/rainbows/revactor/client.rb +2 -2
  43. data/lib/rainbows/stream_file.rb +11 -4
  44. data/lib/rainbows/thread_pool.rb +12 -28
  45. data/lib/rainbows/thread_spawn.rb +14 -13
  46. data/lib/rainbows/thread_timeout.rb +118 -30
  47. data/lib/rainbows/writer_thread_pool/client.rb +1 -1
  48. data/lib/rainbows/writer_thread_spawn/client.rb +2 -2
  49. data/lib/rainbows/xepoll.rb +13 -5
  50. data/lib/rainbows/xepoll/client.rb +19 -17
  51. data/lib/rainbows/xepoll_thread_pool.rb +82 -0
  52. data/lib/rainbows/xepoll_thread_pool/client.rb +129 -0
  53. data/lib/rainbows/xepoll_thread_spawn.rb +58 -0
  54. data/lib/rainbows/xepoll_thread_spawn/client.rb +121 -0
  55. data/pkg.mk +4 -0
  56. data/rainbows.gemspec +4 -1
  57. data/t/GNUmakefile +5 -1
  58. data/t/client_header_buffer_size.ru +5 -0
  59. data/t/simple-http_XEpollThreadPool.ru +10 -0
  60. data/t/simple-http_XEpollThreadSpawn.ru +10 -0
  61. data/t/t0022-copy_stream-byte-range.sh +1 -15
  62. data/t/t0026-splice-copy_stream-byte-range.sh +25 -0
  63. data/t/t0027-nil-copy_stream.sh +60 -0
  64. data/t/t0041-optional-pool-size.sh +2 -2
  65. data/t/t0042-client_header_buffer_size.sh +65 -0
  66. data/t/t9100-thread-timeout.sh +1 -6
  67. data/t/t9101-thread-timeout-threshold.sh +1 -6
  68. data/t/test-lib.sh +58 -0
  69. data/t/test_isolate.rb +9 -3
  70. metadata +47 -16
@@ -10,7 +10,7 @@ module Rainbows::Epoll::Client
10
10
  OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ET
11
11
  KATO = {}
12
12
  KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
13
- KEEPALIVE_TIMEOUT = Rainbows.keepalive_timeout
13
+ Rainbows.config!(self, :keepalive_timeout)
14
14
  EP = Rainbows::Epoll::EP
15
15
  @@last_expire = Time.now
16
16
 
@@ -33,7 +33,7 @@ module Rainbows::Epoll::Client
33
33
  end
34
34
 
35
35
  def on_readable
36
- case rv = kgio_tryread(16384, RBUF)
36
+ case rv = kgio_tryread(CLIENT_HEADER_BUFFER_SIZE, RBUF)
37
37
  when String
38
38
  on_read(rv)
39
39
  return if @wr_queue[0] || closed?
@@ -226,7 +226,7 @@ module Rainbows::Epoll::Client
226
226
  else # nil => EOF
227
227
  return pipe.close # nil
228
228
  end while true
229
- rescue => e
229
+ rescue
230
230
  pipe.close
231
231
  raise
232
232
  end
@@ -9,6 +9,7 @@ module Rainbows::EvCore
9
9
  autoload :CapInput, 'rainbows/ev_core/cap_input'
10
10
  RBUF = ""
11
11
  Z = "".freeze
12
+ Rainbows.config!(self, :client_header_buffer_size)
12
13
 
13
14
  # Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
14
15
  ASYNC_CALLBACK = "async.callback".freeze
@@ -132,7 +133,7 @@ module Rainbows::EvCore
132
133
  end
133
134
 
134
135
  def mkinput
135
- max = Rainbows.max_bytes
136
+ max = Rainbows.server.client_max_body_size
136
137
  len = @hp.content_length
137
138
  if len
138
139
  if max && (len > max)
@@ -40,6 +40,10 @@ EM::VERSION >= '0.12.10' or abort 'eventmachine 0.12.10 is required'
40
40
  # the Rack application to process data as it arrives. This means
41
41
  # "rack.input" will be fully buffered in memory or to a temporary file
42
42
  # before the application is entered.
43
+ #
44
+ # === RubyGem Requirements
45
+ #
46
+ # * event_machine 0.12.10
43
47
  module Rainbows::EventMachine
44
48
  autoload :ResponsePipe, 'rainbows/event_machine/response_pipe'
45
49
  autoload :ResponseChunkPipe, 'rainbows/event_machine/response_chunk_pipe'
@@ -2,6 +2,7 @@
2
2
  # :enddoc:
3
3
  class Rainbows::EventMachine::Client < EM::Connection
4
4
  include Rainbows::EvCore
5
+ Rainbows.config!(self, :keepalive_timeout)
5
6
 
6
7
  def initialize(io)
7
8
  @_io = io
@@ -87,7 +88,7 @@ class Rainbows::EventMachine::Client < EM::Connection
87
88
  if alive
88
89
  if @deferred.nil?
89
90
  if @buf.empty?
90
- set_comm_inactivity_timeout(Rainbows.keepalive_timeout)
91
+ set_comm_inactivity_timeout(KEEPALIVE_TIMEOUT)
91
92
  else
92
93
  EM.next_tick { receive_data(nil) }
93
94
  end
@@ -8,6 +8,11 @@ end
8
8
  # :startdoc:
9
9
 
10
10
  # core namespace for all things that use Fibers in \Rainbows!
11
+ #
12
+ # It's generally not recommended to use any of this in your applications
13
+ # unless you're willing to accept breakage. Most of this is very
14
+ # difficult-to-use, fragile and we don't have much time to devote to
15
+ # supporting these in the future.
11
16
  module Rainbows::Fiber
12
17
 
13
18
  # :stopdoc:
@@ -64,6 +64,7 @@ module Rainbows::Fiber::Base
64
64
  end
65
65
 
66
66
  def self.setup(klass, app)
67
+ Rainbows::Client.__send__(:include, Rainbows::Fiber::IO::Methods)
67
68
  require 'rainbows/fiber/body'
68
69
  Rainbows::Client.__send__(:include, Rainbows::Fiber::Body)
69
70
  self.const_set(:APP, app)
@@ -38,7 +38,6 @@ end
38
38
 
39
39
  [
40
40
  Rainbows::Fiber::IO,
41
- Rainbows::Client,
42
41
  # the next two trigger autoload, ugh, oh well...
43
42
  Rainbows::Fiber::IO::Socket,
44
43
  Rainbows::Fiber::IO::Pipe
@@ -1,13 +1,18 @@
1
1
  # -*- encoding: binary -*-
2
2
 
3
- # A Fiber-aware IO class, gives users the illusion of a synchronous
4
- # interface that yields away from the current Fiber whenever
3
+ # A \Fiber-aware IO class, gives users the illusion of a synchronous
4
+ # interface that yields away from the current \Fiber whenever
5
5
  # the underlying descriptor is blocked on reads or write
6
6
  #
7
+ # It's not recommended to use any of this in your applications
8
+ # unless you're willing to accept breakage. Most of this is very
9
+ # difficult-to-use, fragile and we don't have much time to devote to
10
+ # supporting these in the future.
11
+ #
7
12
  # This is a stable, legacy interface and should be preserved for all
8
13
  # future versions of Rainbows! However, new apps should use
9
- # Rainbows::Fiber::IO::Socket or Rainbows::Fiber::IO::Pipe instead.
10
-
14
+ # Rainbows::Fiber::IO::Socket or Rainbows::Fiber::IO::Pipe instead
15
+ # (or better yet, avoid any of the Rainbows::Fiber* stuff).
11
16
  class Rainbows::Fiber::IO
12
17
  attr_accessor :to_io
13
18
 
@@ -45,7 +50,7 @@ class Rainbows::Fiber::IO
45
50
  end
46
51
 
47
52
  def write(buf)
48
- case rv = Kgio.trywrite(buf)
53
+ case rv = Kgio.trywrite(@to_io, buf)
49
54
  when String
50
55
  buf = rv
51
56
  when :wait_writable
@@ -96,7 +101,6 @@ end
96
101
  # :stopdoc:
97
102
  require 'rainbows/fiber/io/methods'
98
103
  require 'rainbows/fiber/io/compat'
99
- Rainbows::Client.__send__(:include, Rainbows::Fiber::IO::Methods)
100
104
  class Rainbows::Fiber::IO
101
105
  include Rainbows::Fiber::IO::Compat
102
106
  include Rainbows::Fiber::IO::Methods
@@ -1,7 +1,12 @@
1
1
  # -*- encoding: binary -*-
2
2
  # A Fiber-aware Pipe class, gives users the illusion of a synchronous
3
3
  # interface that yields away from the current Fiber whenever
4
- # the underlying descriptor is blocked on reads or write
4
+ # the underlying descriptor is blocked on reads or write.
5
+ #
6
+ # It's not recommended to use any of this in your applications
7
+ # unless you're willing to accept breakage. Most of this is very
8
+ # difficult-to-use, fragile and we don't have much time to devote to
9
+ # supporting these in the future.
5
10
  class Rainbows::Fiber::IO::Pipe < Kgio::Pipe
6
11
  include Rainbows::Fiber::IO::Methods
7
12
  end
@@ -1,7 +1,12 @@
1
1
  # -*- encoding: binary -*-
2
2
  # A Fiber-aware Socket class, gives users the illusion of a synchronous
3
3
  # interface that yields away from the current Fiber whenever
4
- # the underlying descriptor is blocked on reads or write
4
+ # the underlying descriptor is blocked on reads or write.
5
+ #
6
+ # It's not recommended to use any of this in your applications
7
+ # unless you're willing to accept breakage. Most of this is very
8
+ # difficult-to-use, fragile and we don't have much time to devote to
9
+ # supporting these in the future.
5
10
  class Rainbows::Fiber::IO::Socket < Kgio::Socket
6
11
  include Rainbows::Fiber::IO::Methods
7
12
  end
@@ -3,13 +3,18 @@ require 'rainbows/fiber'
3
3
 
4
4
  # A Fiber-based concurrency model for Ruby 1.9. This uses a pool of
5
5
  # Fibers to handle client IO to run the application and the root Fiber
6
- # for scheduling and connection acceptance. The pool size is equal to
7
- # the number of +worker_connections+. Compared to the ThreadPool
8
- # model, Fibers are very cheap in terms of memory usage so you can
9
- # have more active connections. This model supports a streaming
10
- # "rack.input" with lightweight concurrency. Applications are
11
- # strongly advised to wrap all slow IO objects (sockets, pipes) using
12
- # the Rainbows::Fiber::IO class whenever possible.
6
+ # for scheduling and connection acceptance.
7
+ #
8
+ # This concurrency model is difficult to use with existing applications,
9
+ # lacks third-party support, and is thus NOT recommended.
10
+ #
11
+ # The pool size is equal to the number of +worker_connections+.
12
+ # Compared to the ThreadPool model, Fibers are very cheap in terms of
13
+ # memory usage so you can have more active connections. This model
14
+ # supports a streaming "rack.input" with lightweight concurrency.
15
+ # Applications are strongly advised to wrap all slow IO objects
16
+ # (sockets, pipes) using the Rainbows::Fiber::IO class whenever
17
+ # possible.
13
18
  module Rainbows::FiberPool
14
19
  include Rainbows::Fiber::Base
15
20
 
@@ -1,12 +1,17 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rainbows/fiber'
3
3
 
4
- # Simple Fiber-based concurrency model for 1.9. This spawns a new
5
- # Fiber for every incoming client connection and the root Fiber for
6
- # scheduling and connection acceptance. This exports a streaming
7
- # "rack.input" with lightweight concurrency. Applications are
8
- # strongly advised to wrap all slow IO objects (sockets, pipes) using
9
- # the Rainbows::Fiber::IO class whenever possible.
4
+ # Simple Fiber-based concurrency model for 1.9. This spawns a new Fiber
5
+ # for every incoming client connection and the root Fiber for scheduling
6
+ # and connection acceptance.
7
+ #
8
+ # This concurrency model is difficult to use with existing applications,
9
+ # lacks third-party support, and is thus NOT recommended.
10
+ #
11
+ # This exports a streaming "rack.input" with lightweight concurrency.
12
+ # Applications are strongly advised to wrap all slow IO objects
13
+ # (sockets, pipes) using the Rainbows::Fiber::IO class whenever
14
+ # possible.
10
15
  module Rainbows::FiberSpawn
11
16
  include Rainbows::Fiber::Base
12
17
 
@@ -2,6 +2,13 @@
2
2
  # :enddoc:
3
3
 
4
4
  class Rainbows::HttpServer < Unicorn::HttpServer
5
+ attr_accessor :copy_stream
6
+ attr_accessor :worker_connections
7
+ attr_accessor :keepalive_timeout
8
+ attr_accessor :client_header_buffer_size
9
+ attr_accessor :client_max_body_size
10
+ attr_reader :use
11
+
5
12
  def self.setup(block)
6
13
  Rainbows.server.instance_eval(&block)
7
14
  end
@@ -9,9 +16,9 @@ class Rainbows::HttpServer < Unicorn::HttpServer
9
16
  def initialize(app, options)
10
17
  Rainbows.server = self
11
18
  @logger = Unicorn::Configurator::DEFAULTS[:logger]
12
- rv = super(app, options)
13
- defined?(@use) or use(:Base)
14
- @worker_connections ||= Rainbows::MODEL_WORKER_CONNECTIONS[@use]
19
+ super(app, options)
20
+ defined?(@use) or self.use = Rainbows::Base
21
+ @worker_connections ||= @use == :Base ? 1 : 50
15
22
  end
16
23
 
17
24
  def reopen_worker_logs(worker_nr)
@@ -33,80 +40,69 @@ class Rainbows::HttpServer < Unicorn::HttpServer
33
40
  end
34
41
 
35
42
  def load_config!
36
- use :Base
37
- Rainbows.keepalive_timeout = 5
38
- Rainbows.max_bytes = 1024 * 1024
39
- @worker_connections = nil
40
43
  super
41
- @worker_connections ||= Rainbows::MODEL_WORKER_CONNECTIONS[@use]
44
+ @worker_connections = 1 if @use == :Base
45
+ end
46
+
47
+ def worker_loop(worker)
48
+ Rainbows.forked = true
49
+ orig = method(:worker_loop)
50
+ extend(Rainbows.const_get(@use))
51
+ m = method(:worker_loop)
52
+ orig == m ? super(worker) : worker_loop(worker)
42
53
  end
43
54
 
44
- def ready_pipe=(v)
45
- # hacky hook got force Rainbows! to load modules only in workers
46
- if defined?(@master_pid) && @master_pid == Process.ppid
47
- extend(Rainbows.const_get(@use))
55
+ def spawn_missing_workers
56
+ # 5: std{in,out,err} + heartbeat FD + per-process listener
57
+ nofile = 5 + @worker_connections + LISTENERS.size
58
+ trysetrlimit(:RLIMIT_NOFILE, nofile)
59
+
60
+ case @use
61
+ when :ThreadSpawn, :ThreadPool, :ActorSpawn,
62
+ :CoolioThreadSpawn, :RevThreadSpawn,
63
+ :XEpollThreadSpawn, :WriterThreadPool, :WriterThreadSpawn
64
+ trysetrlimit(:RLIMIT_NPROC, @worker_connections + LISTENERS.size + 1)
65
+ when :XEpollThreadPool, :CoolioThreadPool
66
+ trysetrlimit(:RLIMIT_NPROC, Rainbows::O[:pool_size] + LISTENERS.size + 1)
48
67
  end
49
68
  super
50
69
  end
51
70
 
52
- def use(*args)
53
- model = args.shift or return @use
54
- mod = begin
55
- Rainbows.const_get(model)
56
- rescue NameError => e
57
- logger.error "error loading #{model.inspect}: #{e}"
58
- e.backtrace.each { |l| logger.error l }
59
- raise ArgumentError, "concurrency model #{model.inspect} not supported"
71
+ def trysetrlimit(resource, want)
72
+ var = Process.const_get(resource)
73
+ cur, max = Process.getrlimit(var)
74
+ cur <= want and Process.setrlimit(var, cur = max > want ? max : want)
75
+ if cur == want
76
+ @logger.warn "#{resource} rlim_cur=#{cur} is barely enough"
77
+ @logger.warn "#{svc} may monopolize resources dictated by #{resource}" \
78
+ " and leave none for your app"
60
79
  end
80
+ rescue => e
81
+ @logger.error e.message
82
+ @logger.error "#{resource} needs to be increased to >=#{want} before" \
83
+ " starting #{svc}"
84
+ end
61
85
 
62
- Module === mod or
63
- raise ArgumentError, "concurrency model #{model.inspect} not supported"
64
- args.each do |opt|
65
- case opt
66
- when Hash; Rainbows::O.update(opt)
67
- when Symbol; Rainbows::O[opt] = true
68
- else; raise ArgumentError, "can't handle option: #{opt.inspect}"
69
- end
70
- end
71
- mod.setup if mod.respond_to?(:setup)
86
+ def svc
87
+ File.basename($0)
88
+ end
89
+
90
+ def use=(mod)
91
+ @use = mod.to_s.split(/::/)[-1].to_sym
72
92
  new_defaults = {
73
- 'rainbows.model' => (@use = model.to_sym),
74
- 'rack.multithread' => !!(model.to_s =~ /Thread/),
93
+ 'rainbows.model' => @use,
94
+ 'rack.multithread' => !!(mod.to_s =~ /Thread/),
75
95
  'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
76
96
  :EventMachine,:NeverBlock].include?(@use),
77
97
  }
78
98
  Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
79
99
  end
80
100
 
81
- def worker_connections(*args)
82
- return @worker_connections if args.empty?
83
- nr = args[0]
84
- (Integer === nr && nr > 0) or
85
- raise ArgumentError, "worker_connections must be a positive Integer"
86
- @worker_connections = nr
87
- end
88
-
89
- def keepalive_timeout(nr)
90
- (Integer === nr && nr >= 0) or
91
- raise ArgumentError, "keepalive_timeout must be a non-negative Integer"
92
- Rainbows.keepalive_timeout = nr
93
- end
94
-
95
- def keepalive_requests(nr)
96
- Integer === nr or
97
- raise ArgumentError, "keepalive_requests must be a non-negative Integer"
101
+ def keepalive_requests=(nr)
98
102
  Unicorn::HttpRequest.keepalive_requests = nr
99
103
  end
100
104
 
101
- def client_max_body_size(nr)
102
- err = "client_max_body_size must be nil or a non-negative Integer"
103
- case nr
104
- when nil
105
- when Integer
106
- nr >= 0 or raise ArgumentError, err
107
- else
108
- raise ArgumentError, err
109
- end
110
- Rainbows.max_bytes = nr
105
+ def keepalive_requests
106
+ Unicorn::HttpRequest.keepalive_requests
111
107
  end
112
108
  end
@@ -5,9 +5,13 @@ module Rainbows::JoinThreads
5
5
 
6
6
  # blocking acceptor threads must be forced to run
7
7
  def self.acceptors(threads)
8
+ expire = Time.now + Rainbows.server.timeout
8
9
  threads.delete_if do |thr|
9
10
  Rainbows.tick
10
11
  begin
12
+ # blocking accept() may not wake up properly
13
+ thr.raise(Errno::EINTR) if Time.now > expire && thr.stop?
14
+
11
15
  thr.run
12
16
  thr.join(0.01)
13
17
  rescue
@@ -6,7 +6,7 @@
6
6
  # automatically be configured for you based on the client_max_body_size
7
7
  # setting.
8
8
  #
9
- # For more fine-grained conrol, you may also define it per-endpoint in
9
+ # For more fine-grained control, you may also define it per-endpoint in
10
10
  # your Rack config.ru like this:
11
11
  #
12
12
  # map "/limit_1M" do
@@ -17,16 +17,34 @@
17
17
  # use Rainbows::MaxBody, 1024*1024*10
18
18
  # run MyApp
19
19
  # end
20
-
20
+ #
21
+ # This is only compatible with concurrency models that expose a streaming
22
+ # "rack.input" to the Rack application. Thus it is NOT compatible with
23
+ # any of the following as they fully buffer the request body before
24
+ # the application dispatch:
25
+ #
26
+ # * :Coolio
27
+ # * :CoolioThreadPool
28
+ # * :CoolioThreadSpawn
29
+ # * :Epoll
30
+ # * :EventMachine
31
+ # * :NeverBlock
32
+ # * :Rev
33
+ # * :RevThreadPool
34
+ # * :RevThreadSpawn
35
+ # * :XEpoll
36
+ #
37
+ # However, the global Rainbows::Configurator#client_max_body_size is compatible
38
+ # with all concurrency models \Rainbows! supports.
21
39
  class Rainbows::MaxBody
22
40
 
23
-
24
- # :call-seq:
25
- # # in config.ru:
26
- # use Rainbows::MaxBody, 4096
27
- # run YourApplication.new
28
- def initialize(app, limit = Rainbows.max_bytes)
29
- Integer === limit or raise ArgumentError, "limit not an Integer"
41
+ # This is automatically called when used with Rack::Builder#use
42
+ def initialize(app, limit = nil)
43
+ case limit
44
+ when Integer, nil
45
+ else
46
+ raise ArgumentError, "limit not an Integer"
47
+ end
30
48
  @app, @limit = app, limit
31
49
  end
32
50
 
@@ -37,6 +55,7 @@ class Rainbows::MaxBody
37
55
 
38
56
  # our main Rack middleware endpoint
39
57
  def call(env)
58
+ @limit = Rainbows.server.client_max_body_size if nil == @limit
40
59
  catch(:rainbows_EFBIG) do
41
60
  len = env[CONTENT_LENGTH]
42
61
  if len && len.to_i > @limit
@@ -51,7 +70,7 @@ class Rainbows::MaxBody
51
70
  # this is called after forking, so it won't ever affect the master
52
71
  # if it's reconfigured
53
72
  def self.setup # :nodoc:
54
- Rainbows.max_bytes or return
73
+ Rainbows.server.client_max_body_size or return
55
74
  case Rainbows.server.use
56
75
  when :Rev, :Coolio, :EventMachine, :NeverBlock,
57
76
  :RevThreadSpawn, :RevThreadPool,