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.
- data/.manifest +14 -2
- data/ChangeLog +87 -118
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +1 -1
- data/README +1 -1
- data/bin/rainbows +15 -20
- data/lib/rainbows/actor_spawn.rb +20 -22
- data/lib/rainbows/app_pool.rb +89 -93
- data/lib/rainbows/base.rb +4 -61
- data/lib/rainbows/client.rb +9 -0
- data/lib/rainbows/configurator.rb +37 -39
- data/lib/rainbows/const.rb +18 -18
- data/lib/rainbows/dev_fd_response.rb +2 -1
- data/lib/rainbows/error.rb +39 -37
- data/lib/rainbows/ev_core.rb +103 -109
- data/lib/rainbows/event_machine.rb +188 -196
- data/lib/rainbows/fiber/base.rb +69 -88
- data/lib/rainbows/fiber/io/compat.rb +13 -0
- data/lib/rainbows/fiber/io/methods.rb +49 -0
- data/lib/rainbows/fiber/io/pipe.rb +7 -0
- data/lib/rainbows/fiber/io/socket.rb +7 -0
- data/lib/rainbows/fiber/io.rb +125 -84
- data/lib/rainbows/fiber/rev/heartbeat.rb +8 -0
- data/lib/rainbows/fiber/rev/kato.rb +22 -0
- data/lib/rainbows/fiber/rev/methods.rb +55 -0
- data/lib/rainbows/fiber/rev/server.rb +32 -0
- data/lib/rainbows/fiber/rev/sleeper.rb +15 -0
- data/lib/rainbows/fiber/rev.rb +6 -164
- data/lib/rainbows/fiber.rb +23 -5
- data/lib/rainbows/fiber_pool.rb +31 -37
- data/lib/rainbows/fiber_spawn.rb +21 -28
- data/lib/rainbows/http_server.rb +80 -80
- data/lib/rainbows/max_body.rb +26 -28
- data/lib/rainbows/process_client.rb +61 -0
- data/lib/rainbows/queue_pool.rb +19 -22
- data/lib/rainbows/read_timeout.rb +28 -0
- data/lib/rainbows/rev/client.rb +10 -10
- data/lib/rainbows/rev/core.rb +2 -3
- data/lib/rainbows/rev/thread.rb +1 -1
- data/lib/rainbows/rev_fiber_spawn.rb +21 -24
- data/lib/rainbows/revactor.rb +18 -15
- data/lib/rainbows/thread_pool.rb +2 -4
- data/lib/rainbows/thread_spawn.rb +1 -2
- data/lib/rainbows/writer_thread_pool.rb +14 -4
- data/lib/rainbows/writer_thread_spawn.rb +14 -4
- data/lib/rainbows.rb +7 -15
- data/local.mk.sample +3 -11
- data/rainbows.gemspec +2 -4
- data/t/kgio-pipe-response.ru +10 -0
- data/t/t0035-kgio-pipe-response.sh +70 -0
- data/t/test_isolate.rb +2 -1
- metadata +46 -30
- data/lib/rainbows/acceptor.rb +0 -26
- data/lib/rainbows/byte_slice.rb +0 -17
data/lib/rainbows/queue_pool.rb
CHANGED
@@ -2,32 +2,29 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
require 'thread'
|
4
4
|
|
5
|
-
|
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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
data/lib/rainbows/rev/client.rb
CHANGED
@@ -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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
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] = @
|
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.
|
150
|
+
@_io.kgio_trywrite(msg) rescue nil
|
151
151
|
end
|
152
152
|
@_write_buffer.clear
|
153
153
|
ensure
|
data/lib/rainbows/rev/core.rb
CHANGED
@@ -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 =
|
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 =
|
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))
|
data/lib/rainbows/rev/thread.rb
CHANGED
@@ -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] = @
|
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
|
-
|
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
|
-
|
7
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
data/lib/rainbows/revactor.rb
CHANGED
@@ -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 =
|
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
|
-
|
44
|
-
|
45
|
-
env = {}
|
44
|
+
hp = Unicorn::HttpParser.new
|
45
|
+
buf = hp.buf
|
46
46
|
|
47
47
|
begin
|
48
|
-
|
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(
|
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 =
|
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 &&
|
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
|
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
|
-
|
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
|
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
|
|
data/lib/rainbows/thread_pool.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
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
|
29
|
-
to_io.
|
29
|
+
def kgio_addr
|
30
|
+
to_io.kgio_addr
|
30
31
|
end
|
31
32
|
|
32
|
-
def
|
33
|
-
to_io.
|
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
|
34
|
-
to_io.
|
34
|
+
def kgio_addr
|
35
|
+
to_io.kgio_addr
|
35
36
|
end
|
36
37
|
|
37
|
-
def
|
38
|
-
to_io.
|
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
|
-
|
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
|
-
|
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-
|
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
|
-
|
48
|
-
|
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