rainbows 0.97.0 → 1.0.0pre1

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 (55) hide show
  1. data/.manifest +14 -2
  2. data/ChangeLog +87 -118
  3. data/GIT-VERSION-FILE +1 -1
  4. data/GIT-VERSION-GEN +1 -1
  5. data/GNUmakefile +1 -1
  6. data/README +1 -1
  7. data/bin/rainbows +15 -20
  8. data/lib/rainbows/actor_spawn.rb +20 -22
  9. data/lib/rainbows/app_pool.rb +89 -93
  10. data/lib/rainbows/base.rb +4 -61
  11. data/lib/rainbows/client.rb +9 -0
  12. data/lib/rainbows/configurator.rb +37 -39
  13. data/lib/rainbows/const.rb +18 -18
  14. data/lib/rainbows/dev_fd_response.rb +2 -1
  15. data/lib/rainbows/error.rb +39 -37
  16. data/lib/rainbows/ev_core.rb +103 -109
  17. data/lib/rainbows/event_machine.rb +188 -196
  18. data/lib/rainbows/fiber/base.rb +69 -88
  19. data/lib/rainbows/fiber/io/compat.rb +13 -0
  20. data/lib/rainbows/fiber/io/methods.rb +49 -0
  21. data/lib/rainbows/fiber/io/pipe.rb +7 -0
  22. data/lib/rainbows/fiber/io/socket.rb +7 -0
  23. data/lib/rainbows/fiber/io.rb +125 -84
  24. data/lib/rainbows/fiber/rev/heartbeat.rb +8 -0
  25. data/lib/rainbows/fiber/rev/kato.rb +22 -0
  26. data/lib/rainbows/fiber/rev/methods.rb +55 -0
  27. data/lib/rainbows/fiber/rev/server.rb +32 -0
  28. data/lib/rainbows/fiber/rev/sleeper.rb +15 -0
  29. data/lib/rainbows/fiber/rev.rb +6 -164
  30. data/lib/rainbows/fiber.rb +23 -5
  31. data/lib/rainbows/fiber_pool.rb +31 -37
  32. data/lib/rainbows/fiber_spawn.rb +21 -28
  33. data/lib/rainbows/http_server.rb +80 -80
  34. data/lib/rainbows/max_body.rb +26 -28
  35. data/lib/rainbows/process_client.rb +61 -0
  36. data/lib/rainbows/queue_pool.rb +19 -22
  37. data/lib/rainbows/read_timeout.rb +28 -0
  38. data/lib/rainbows/rev/client.rb +10 -10
  39. data/lib/rainbows/rev/core.rb +2 -3
  40. data/lib/rainbows/rev/thread.rb +1 -1
  41. data/lib/rainbows/rev_fiber_spawn.rb +21 -24
  42. data/lib/rainbows/revactor.rb +18 -15
  43. data/lib/rainbows/thread_pool.rb +2 -4
  44. data/lib/rainbows/thread_spawn.rb +1 -2
  45. data/lib/rainbows/writer_thread_pool.rb +14 -4
  46. data/lib/rainbows/writer_thread_spawn.rb +14 -4
  47. data/lib/rainbows.rb +7 -15
  48. data/local.mk.sample +3 -11
  49. data/rainbows.gemspec +2 -4
  50. data/t/kgio-pipe-response.ru +10 -0
  51. data/t/t0035-kgio-pipe-response.sh +70 -0
  52. data/t/test_isolate.rb +2 -1
  53. metadata +46 -30
  54. data/lib/rainbows/acceptor.rb +0 -26
  55. data/lib/rainbows/byte_slice.rb +0 -17
@@ -2,32 +2,29 @@
2
2
  # :enddoc:
3
3
  require 'thread'
4
4
 
5
- module Rainbows
5
+ # Thread pool class based on pulling off a single Ruby Queue.
6
+ # This is NOT used for the ThreadPool class, since that class does not
7
+ # need a userspace Queue.
8
+ class Rainbows::QueuePool < Struct.new(:queue, :threads)
9
+ G = Rainbows::G
6
10
 
7
- # Thread pool class based on pulling off a single Ruby Queue.
8
- # This is NOT used for the ThreadPool class, since that class does not
9
- # need a userspace Queue.
10
- class QueuePool < Struct.new(:queue, :threads)
11
- G = Rainbows::G
12
-
13
- def initialize(size = 20, &block)
14
- q = Queue.new
15
- self.threads = (1..size).map do
16
- Thread.new do
17
- while job = q.shift
18
- block.call(job)
19
- end
11
+ def initialize(size = 20, &block)
12
+ q = Queue.new
13
+ self.threads = (1..size).map do
14
+ Thread.new do
15
+ while job = q.shift
16
+ block.call(job)
20
17
  end
21
18
  end
22
- self.queue = q
23
19
  end
20
+ self.queue = q
21
+ end
24
22
 
25
- def quit!
26
- threads.each { |_| queue << nil }
27
- threads.delete_if do |t|
28
- G.tick
29
- t.alive? ? t.join(0.01) : true
30
- end until threads.empty?
31
- end
23
+ def quit!
24
+ threads.each { |_| queue << nil }
25
+ threads.delete_if do |t|
26
+ G.tick
27
+ t.alive? ? t.join(0.01) : true
28
+ end until threads.empty?
32
29
  end
33
30
  end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ module Rainbows::ReadTimeout
4
+ G = Rainbows::G # :nodoc:
5
+
6
+ def wait_readable
7
+ IO.select([self], nil, nil, G.kato)
8
+ end
9
+
10
+ # used for reading headers (respecting keepalive_timeout)
11
+ def read_timeout(buf = "")
12
+ expire = nil
13
+ begin
14
+ case rv = kgio_tryread(16384, buf)
15
+ when :wait_readable
16
+ now = Time.now.to_f
17
+ if expire
18
+ now > expire and return
19
+ else
20
+ expire = now + G.kato
21
+ end
22
+ wait_readable
23
+ else
24
+ return rv
25
+ end
26
+ end while true
27
+ end
28
+ end
@@ -5,7 +5,6 @@ module Rainbows
5
5
  module Rev
6
6
 
7
7
  class Client < ::Rev::IO
8
- include Rainbows::ByteSlice
9
8
  include Rainbows::EvCore
10
9
  G = Rainbows::G
11
10
  F = Rainbows::StreamFile
@@ -28,13 +27,14 @@ module Rainbows
28
27
  def write(buf)
29
28
  if @_write_buffer.empty?
30
29
  begin
31
- w = @_io.write_nonblock(buf)
32
- return enable_write_watcher if w == Rack::Utils.bytesize(buf)
33
- # we never care for the return value, but yes, we may return
34
- # a "fake" short write from super(buf) if anybody cares.
35
- buf = byte_slice(buf, w..-1)
36
- rescue Errno::EAGAIN
37
- break # fall through to super(buf)
30
+ case rv = @_io.kgio_trywrite(buf)
31
+ when nil
32
+ return enable_write_watcher
33
+ when Kgio::WaitWritable
34
+ break # fall through to super(buf)
35
+ when String
36
+ buf = rv # retry, skb could grow or been drained
37
+ end
38
38
  rescue => e
39
39
  return handle_error(e)
40
40
  end while true
@@ -104,7 +104,7 @@ module Rainbows
104
104
  def app_call
105
105
  KATO.delete(self)
106
106
  @env[RACK_INPUT] = @input
107
- @env[REMOTE_ADDR] = @remote_addr
107
+ @env[REMOTE_ADDR] = @_io.kgio_addr
108
108
  response = APP.call(@env.update(RACK_DEFAULTS))
109
109
 
110
110
  rev_write_response(response, alive = @hp.keepalive? && G.alive)
@@ -147,7 +147,7 @@ module Rainbows
147
147
  def handle_error(e)
148
148
  close_deferred
149
149
  if msg = Error.response(e)
150
- @_io.write_nonblock(msg) rescue nil
150
+ @_io.kgio_trywrite(msg) rescue nil
151
151
  end
152
152
  @_write_buffer.clear
153
153
  ensure
@@ -7,12 +7,11 @@ require 'rainbows/rev/heartbeat'
7
7
  module Rainbows
8
8
  module Rev
9
9
  class Server < ::Rev::IO
10
- include Rainbows::Acceptor
11
10
  # CL and MAX will be defined in the corresponding worker loop
12
11
 
13
12
  def on_readable
14
13
  return if CONN.size >= MAX
15
- io = accept(@_io) and CL.new(io).attach(LOOP)
14
+ io = @_io.kgio_tryaccept and CL.new(io).attach(LOOP)
16
15
  end
17
16
  end # class Server
18
17
 
@@ -27,7 +26,7 @@ module Rainbows
27
26
  require 'rainbows/rev/sendfile'
28
27
  Rainbows::Rev::Client.__send__(:include, Rainbows::Rev::Sendfile)
29
28
  init_worker_process(worker)
30
- mod = self.class.const_get(@use)
29
+ mod = Rainbows.const_get(@use)
31
30
  rloop = Server.const_set(:LOOP, ::Rev::Loop.default)
32
31
  Server.const_set(:MAX, @worker_connections)
33
32
  Server.const_set(:CL, mod.const_get(:Client))
@@ -34,7 +34,7 @@ module Rainbows
34
34
  # here because that could cause a deadlock and we'd leak FDs
35
35
  def app_response
36
36
  begin
37
- @env[REMOTE_ADDR] = @remote_addr
37
+ @env[REMOTE_ADDR] = @_io.kgio_addr
38
38
  APP.call(@env.update(RACK_DEFAULTS))
39
39
  rescue => e
40
40
  Error.app(e) # we guarantee this does not raise
@@ -1,31 +1,28 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'rainbows/fiber/rev'
3
3
 
4
- module Rainbows
4
+ # A combination of the Rev and FiberSpawn models. This allows Ruby
5
+ # 1.9 Fiber-based concurrency for application processing while
6
+ # exposing a synchronous execution model and using scalable network
7
+ # concurrency provided by Rev. A "rack.input" is exposed as well
8
+ # being Sunshowers-compatible. Applications are strongly advised to
9
+ # wrap all slow IO objects (sockets, pipes) using the
10
+ # Rainbows::Fiber::IO or a Rev-compatible class whenever possible.
11
+ module Rainbows::RevFiberSpawn
5
12
 
6
- # A combination of the Rev and FiberSpawn models. This allows Ruby
7
- # 1.9 Fiber-based concurrency for application processing while
8
- # exposing a synchronous execution model and using scalable network
9
- # concurrency provided by Rev. A "rack.input" is exposed as well
10
- # being Sunshowers-compatible. Applications are strongly advised to
11
- # wrap all slow IO objects (sockets, pipes) using the
12
- # Rainbows::Fiber::IO or a Rev-compatible class whenever possible.
13
- module RevFiberSpawn
13
+ include Rainbows::Base
14
+ include Rainbows::Fiber::Rev
14
15
 
15
- include Base
16
- include Fiber::Rev
17
-
18
- def worker_loop(worker) # :nodoc:
19
- Rainbows::Response.setup(Rainbows::Fiber::Rev::Server)
20
- init_worker_process(worker)
21
- Server.const_set(:MAX, @worker_connections)
22
- Rainbows::Fiber::Base.setup(Rainbows::Fiber::Rev::Server, nil)
23
- Server.const_set(:APP, G.server.app)
24
- Heartbeat.new(1, true).attach(::Rev::Loop.default)
25
- kato = Kato.new.attach(::Rev::Loop.default)
26
- Rainbows::Fiber::IO.const_set(:KATO, kato)
27
- LISTENERS.map! { |s| Server.new(s).attach(::Rev::Loop.default) }
28
- ::Rev::Loop.default.run
29
- end
16
+ def worker_loop(worker) # :nodoc:
17
+ Rainbows::Response.setup(Server)
18
+ init_worker_process(worker)
19
+ Server.const_set(:MAX, @worker_connections)
20
+ Rainbows::Fiber::Base.setup(Server, nil)
21
+ Server.const_set(:APP, G.server.app)
22
+ Heartbeat.new(1, true).attach(Rev::Loop.default)
23
+ kato = Kato.new.attach(Rev::Loop.default)
24
+ Rainbows::Fiber::Rev::Methods.const_set(:KATO, kato)
25
+ LISTENERS.map! { |s| Server.new(s).attach(Rev::Loop.default) }
26
+ Rev::Loop.default.run
30
27
  end
31
28
  end
@@ -1,5 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'revactor'
3
+ require 'fcntl'
3
4
  Revactor::VERSION >= '0.1.5' or abort 'revactor 0.1.5 is required'
4
5
 
5
6
  # Enables use of the Actor model through
@@ -25,7 +26,7 @@ module Rainbows::Revactor
25
26
  autoload :Proxy, 'rainbows/revactor/proxy'
26
27
 
27
28
  include Rainbows::Base
28
- LOCALHOST = Unicorn::HttpRequest::LOCALHOST
29
+ LOCALHOST = Kgio::LOCALHOST
29
30
  TCP = ::Revactor::TCP::Socket
30
31
 
31
32
  # once a client is accepted, it is processed in its entirety here
@@ -40,16 +41,17 @@ module Rainbows::Revactor
40
41
  else
41
42
  LOCALHOST
42
43
  end
43
- buf = client.read(*rd_args)
44
- hp = HttpParser.new
45
- env = {}
44
+ hp = Unicorn::HttpParser.new
45
+ buf = hp.buf
46
46
 
47
47
  begin
48
- buf << client.read(*rd_args) until hp.headers(env, buf)
48
+ until env = hp.parse
49
+ buf << client.read(*rd_args)
50
+ end
49
51
 
50
52
  env[CLIENT_IO] = client
51
53
  env[RACK_INPUT] = 0 == hp.content_length ?
52
- NULL_IO : TeeInput.new(PartialSocket.new(client), env, hp, buf)
54
+ NULL_IO : TeeInput.new(TeeSocket.new(client), hp)
53
55
  env[REMOTE_ADDR] = remote_addr
54
56
  status, headers, body = app.call(env.update(RACK_DEFAULTS))
55
57
 
@@ -62,12 +64,12 @@ module Rainbows::Revactor
62
64
  if hp.headers?
63
65
  headers = HH.new(headers)
64
66
  range = make_range!(env, status, headers) and status = range.shift
65
- env = false unless hp.keepalive? && G.alive && G.kato > 0
67
+ env = hp.keepalive? && G.alive && G.kato > 0
66
68
  headers[CONNECTION] = env ? KEEP_ALIVE : CLOSE
67
69
  client.write(response_header(status, headers))
68
70
  end
69
71
  write_body(client, body, range)
70
- end while env && env.clear && hp.reset.nil?
72
+ end while env && hp.reset.nil?
71
73
  rescue ::Revactor::TCP::ReadError
72
74
  rescue => e
73
75
  Rainbows::Error.write(io, e)
@@ -145,36 +147,37 @@ module Rainbows::Revactor
145
147
  # enough to avoid mucking with TeeInput internals. Fortunately
146
148
  # this code is not heavily used so we can usually avoid the overhead
147
149
  # of adding a userspace buffer.
148
- class PartialSocket < Struct.new(:socket, :rbuf)
150
+ class TeeSocket
149
151
  def initialize(socket)
150
152
  # IO::Buffer is used internally by Rev which Revactor is based on
151
153
  # so we'll always have it available
152
- super(socket, IO::Buffer.new)
154
+ @socket, @rbuf = socket, IO::Buffer.new
153
155
  end
154
156
 
155
157
  # Revactor socket reads always return an unspecified amount,
156
158
  # sometimes too much
157
- def readpartial(length, dst = "")
159
+ def kgio_read(length, dst = "")
158
160
  return dst.replace("") if length == 0
159
161
 
160
162
  # always check and return from the userspace buffer first
161
- rbuf.size > 0 and return dst.replace(rbuf.read(length))
163
+ @rbuf.size > 0 and return dst.replace(@rbuf.read(length))
162
164
 
163
165
  # read off the socket since there was nothing in rbuf
164
- tmp = socket.read
166
+ tmp = @socket.read
165
167
 
166
168
  # we didn't read too much, good, just return it straight back
167
169
  # to avoid needlessly wasting memory bandwidth
168
170
  tmp.size <= length and return dst.replace(tmp)
169
171
 
170
172
  # ugh, read returned too much
171
- rbuf << tmp[length, tmp.size]
173
+ @rbuf << tmp[length, tmp.size]
172
174
  dst.replace(tmp[0, length])
175
+ rescue EOFError
173
176
  end
174
177
 
175
178
  # just proxy any remaining methods TeeInput may use
176
179
  def close
177
- socket.close
180
+ @socket.close
178
181
  end
179
182
  end
180
183
 
@@ -22,9 +22,7 @@ module Rainbows
22
22
  # others and thus a lower +worker_connections+ setting is recommended.
23
23
 
24
24
  module ThreadPool
25
-
26
25
  include Base
27
- include Rainbows::Acceptor
28
26
 
29
27
  def worker_loop(worker) # :nodoc:
30
28
  init_worker_process(worker)
@@ -45,7 +43,7 @@ module Rainbows
45
43
  def sync_worker # :nodoc:
46
44
  s = LISTENERS[0]
47
45
  begin
48
- c = sync_accept(s) and process_client(c)
46
+ c = s.kgio_accept and process_client(c)
49
47
  rescue => e
50
48
  Error.listen_loop(e)
51
49
  end while G.alive
@@ -59,7 +57,7 @@ module Rainbows
59
57
  # problem. On the other hand, a thundering herd may not
60
58
  # even incur as much overhead as an extra Mutex#synchronize
61
59
  ret = IO.select(LISTENERS, nil, nil, 1) and ret[0].each do |s|
62
- s = accept(s) and process_client(s)
60
+ s = s.kgio_tryaccept and process_client(s)
63
61
  end
64
62
  rescue Errno::EINTR
65
63
  rescue => e
@@ -18,7 +18,6 @@ module Rainbows
18
18
 
19
19
  module ThreadSpawn
20
20
  include Base
21
- include Rainbows::Acceptor
22
21
 
23
22
  def accept_loop(klass) #:nodoc:
24
23
  lock = Mutex.new
@@ -37,7 +36,7 @@ module Rainbows
37
36
  # CPU during I/O wait, CPU cycles that can be better used
38
37
  # by other worker _processes_.
39
38
  sleep(0.01)
40
- elsif c = sync_accept(l)
39
+ elsif c = l.kgio_accept
41
40
  klass.new(c) do |c|
42
41
  begin
43
42
  lock.synchronize { G.cur += 1 }
@@ -20,17 +20,26 @@ module Rainbows
20
20
  # slow client denial-of-service attacks.
21
21
 
22
22
  module WriterThreadPool
23
+ # :stopdoc:
23
24
  include Base
24
25
 
25
26
  # used to wrap a BasicSocket to use with +q+ for all writes
26
27
  # this is compatible with IO.select
27
28
  class QueueSocket < Struct.new(:to_io, :q) # :nodoc:
28
- def readpartial(size, buf = "")
29
- to_io.readpartial(size, buf)
29
+ def kgio_addr
30
+ to_io.kgio_addr
30
31
  end
31
32
 
32
- def write_nonblock(buf)
33
- to_io.write_nonblock(buf)
33
+ def kgio_read(size, buf = "")
34
+ to_io.kgio_read(size, buf)
35
+ end
36
+
37
+ def kgio_read!(size, buf = "")
38
+ to_io.kgio_read!(size, buf)
39
+ end
40
+
41
+ def kgio_trywrite(buf)
42
+ to_io.kgio_trywrite(buf)
34
43
  end
35
44
 
36
45
  def write(buf)
@@ -87,5 +96,6 @@ module Rainbows
87
96
  super(worker) # accept loop from Unicorn
88
97
  qp.map { |q| q.quit! }
89
98
  end
99
+ # :startdoc:
90
100
  end
91
101
  end
@@ -21,6 +21,7 @@ module Rainbows
21
21
  # vulnerable to slow client denial-of-service attacks.
22
22
 
23
23
  module WriterThreadSpawn
24
+ # :stopdoc:
24
25
  include Base
25
26
 
26
27
  CUR = {} # :nodoc:
@@ -30,12 +31,20 @@ module Rainbows
30
31
  class MySocket < Struct.new(:to_io, :q, :thr) # :nodoc: all
31
32
  include Rainbows::Response
32
33
 
33
- def readpartial(size, buf = "")
34
- to_io.readpartial(size, buf)
34
+ def kgio_addr
35
+ to_io.kgio_addr
35
36
  end
36
37
 
37
- def write_nonblock(buf)
38
- to_io.write_nonblock(buf)
38
+ def kgio_read(size, buf = "")
39
+ to_io.kgio_read(size, buf)
40
+ end
41
+
42
+ def kgio_read!(size, buf = "")
43
+ to_io.kgio_read!(size, buf)
44
+ end
45
+
46
+ def kgio_trywrite(buf)
47
+ to_io.kgio_trywrite(buf)
39
48
  end
40
49
 
41
50
  def queue_writer
@@ -107,5 +116,6 @@ module Rainbows
107
116
  t.alive? ? t.join(0.01) : true
108
117
  end until CUR.empty?
109
118
  end
119
+ # :startdoc:
110
120
  end
111
121
  end
data/lib/rainbows.rb CHANGED
@@ -1,12 +1,9 @@
1
1
  # -*- encoding: binary -*-
2
+ require 'kgio'
2
3
  require 'unicorn'
3
4
  # the value passed to TCP_DEFER_ACCEPT actually matters in Linux 2.6.32+
4
5
  Unicorn::SocketHelper::DEFAULTS[:tcp_defer_accept] = 60
5
6
 
6
- require 'rainbows/error'
7
- require 'rainbows/configurator'
8
- require 'fcntl'
9
-
10
7
  module Rainbows
11
8
 
12
9
  # global vars because class/instance variables are confusing me :<
@@ -41,8 +38,10 @@ module Rainbows
41
38
  require 'rainbows/const'
42
39
  require 'rainbows/http_server'
43
40
  require 'rainbows/response'
41
+ require 'rainbows/client'
42
+ require 'rainbows/tee_input'
43
+ require 'rainbows/process_client'
44
44
  autoload :Base, 'rainbows/base'
45
- autoload :TeeInput, 'rainbows/tee_input'
46
45
  autoload :Sendfile, 'rainbows/sendfile'
47
46
  autoload :AppPool, 'rainbows/app_pool'
48
47
  autoload :DevFdResponse, 'rainbows/dev_fd_response'
@@ -75,14 +74,6 @@ module Rainbows
75
74
  HttpServer.new(app, options).start.join
76
75
  end
77
76
 
78
- # returns a string representing the address of the given client +io+
79
- # For local UNIX domain sockets, this will return a string referred
80
- # to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant.
81
- def addr(io) # :nodoc:
82
- io.respond_to?(:peeraddr) ?
83
- io.peeraddr[-1] : Unicorn::HttpRequest::LOCALHOST
84
- end
85
-
86
77
  # :stopdoc:
87
78
  # the default max body size is 1 megabyte (1024 * 1024 bytes)
88
79
  @@max_bytes = 1024 * 1024
@@ -118,9 +109,10 @@ module Rainbows
118
109
  end
119
110
  # :startdoc:
120
111
  autoload :Fiber, 'rainbows/fiber' # core class
121
- autoload :ByteSlice, 'rainbows/byte_slice'
122
112
  autoload :StreamFile, 'rainbows/stream_file'
123
113
  autoload :HttpResponse, 'rainbows/http_response' # deprecated
124
114
  autoload :ThreadTimeout, 'rainbows/thread_timeout'
125
115
  end
126
- require 'rainbows/acceptor'
116
+
117
+ require 'rainbows/error'
118
+ require 'rainbows/configurator'
data/local.mk.sample CHANGED
@@ -17,13 +17,7 @@ DLEXT := so
17
17
  prefix = $(HOME)
18
18
 
19
19
  ifeq ($(r192),)
20
- ifeq ($(r19),)
21
- RUBY := $(prefix)/bin/ruby
22
- else
23
- prefix := $(prefix)/ruby-1.9
24
- export PATH := $(prefix)/bin:$(PATH)
25
- RUBY := $(prefix)/bin/ruby --disable-gems
26
- endif
20
+ RUBY := $(prefix)/bin/ruby
27
21
  else
28
22
  prefix := $(prefix)/ruby-1.9.2
29
23
  export PATH := $(prefix)/bin:$(PATH)
@@ -36,12 +30,10 @@ SHELL := /bin/ksh93 -e -o pipefail
36
30
 
37
31
  # trace execution of tests
38
32
  # TRACER = strace -f -o $(t_pfx).strace -s 100000
39
- TRACER = /usr/bin/time -v -o $(t_pfx).time
33
+ # TRACER = /usr/bin/time -v -o $(t_pfx).time
40
34
 
41
- full-test: test-18 test-191 test-192
35
+ full-test: test-18 test-192
42
36
  test-18:
43
37
  $(MAKE) test 2>&1 | sed -e 's!^!1.8 !'
44
- test-191:
45
- $(MAKE) test r19=T 2>&1 | sed -e 's!^!1.9.1 !'
46
38
  test-192:
47
39
  $(MAKE) test r192=T 2>&1 | sed -e 's!^!1.9.2 !'
data/rainbows.gemspec CHANGED
@@ -44,10 +44,8 @@ Gem::Specification.new do |s|
44
44
  s.add_dependency(%q<rack>, ['~> 1.1'])
45
45
 
46
46
  # we need Unicorn for the HTTP parser and process management
47
- # Unicorn 0.991.0 handles config.ru when started outside of
48
- # the prespecified working_directory
49
- s.add_dependency(%q<unicorn>, [">= 1.1.3", "< 2.0.0"])
50
- s.add_development_dependency(%q<isolate>, "~> 2.1.0")
47
+ s.add_dependency(%q<unicorn>, ["~> 2.0.0pre3"])
48
+ s.add_development_dependency(%q<isolate>, "~> 3.0.0")
51
49
 
52
50
  # optional runtime dependencies depending on configuration
53
51
  # see t/test_isolate.rb for the exact versions we've tested with
@@ -0,0 +1,10 @@
1
+ # must be run without Rack::Lint since that clobbers to_path
2
+ use Rainbows::DevFdResponse
3
+ run(lambda { |env|
4
+ [ 200,
5
+ {
6
+ 'Content-Length' => ::File.stat('random_blob').size.to_s,
7
+ 'Content-Type' => 'application/octet-stream',
8
+ },
9
+ Rainbows::Fiber::IO::Pipe.popen('cat random_blob', 'rb') ]
10
+ })
@@ -0,0 +1,70 @@
1
+ #!/bin/sh
2
+ . ./test-lib.sh
3
+ test -r random_blob || die "random_blob required, run with 'make $0'"
4
+ case $model in
5
+ *Fiber* ) ;;
6
+ *)
7
+ t_info "skipping $T since it's not compatible with $model"
8
+ exit 0
9
+ ;;
10
+ esac
11
+
12
+ t_plan 10 "fast Kgio pipe response for $model"
13
+
14
+ t_begin "setup and startup" && {
15
+ rtmpfiles err out
16
+ rainbows_setup $model
17
+ rainbows -E none -D kgio-pipe-response.ru -c $unicorn_config
18
+ rainbows_wait_start
19
+ }
20
+
21
+ t_begin "read random blob sha1" && {
22
+ random_blob_sha1=$(rsha1 < random_blob)
23
+ three_sha1=$(cat random_blob random_blob random_blob | rsha1)
24
+ }
25
+
26
+ t_begin "single request matches" && {
27
+ sha1=$(curl -sSfv 2> $err http://$listen/ | rsha1)
28
+ test -n "$sha1"
29
+ test x"$sha1" = x"$random_blob_sha1"
30
+ }
31
+
32
+ t_begin "Content-Length header preserved in response" && {
33
+ grep "^< Content-Length:" $err
34
+ }
35
+
36
+ t_begin "send three keep-alive requests" && {
37
+ sha1=$(curl -vsSf 2> $err \
38
+ http://$listen/ http://$listen/ http://$listen/ | rsha1)
39
+ test -n "$sha1"
40
+ test x"$sha1" = x"$three_sha1"
41
+ }
42
+
43
+ t_begin "ensure responses were all keep-alive" && {
44
+ test 3 -eq $(grep '< Connection: keep-alive' < $err | wc -l)
45
+ }
46
+
47
+ t_begin "HTTP/1.0 test" && {
48
+ sha1=$(curl -0 -v 2> $err -sSf http://$listen/ | rsha1)
49
+ test $sha1 = $random_blob_sha1
50
+ grep '< Connection: close' < $err
51
+ }
52
+
53
+ t_begin "HTTP/0.9 test" && {
54
+ (
55
+ printf 'GET /\r\n'
56
+ rsha1 < $fifo > $tmp &
57
+ wait
58
+ echo ok > $ok
59
+ ) | socat - TCP:$listen > $fifo
60
+ test $(cat $tmp) = $random_blob_sha1
61
+ test xok = x$(cat $ok)
62
+ }
63
+
64
+ t_begin "shutdown server" && {
65
+ kill -QUIT $rainbows_pid
66
+ }
67
+
68
+ t_begin "check stderr" && check_stderr
69
+
70
+ t_done
data/t/test_isolate.rb CHANGED
@@ -15,7 +15,8 @@ $stdout.reopen($stderr)
15
15
 
16
16
  Isolate.now!(opts) do
17
17
  gem 'rack', '1.1.0' # Cramp currently requires ~> 1.1.0
18
- gem 'unicorn', '1.1.3'
18
+ gem 'kgio', '1.3.1'
19
+ gem 'unicorn', '2.0.0pre3'
19
20
  gem 'kcar', '0.1.1'
20
21
 
21
22
  if engine == "ruby"