rainbows 3.0.0 → 3.1.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.
- data/.wrongdoc.yml +2 -2
- data/Documentation/comparison.haml +6 -6
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +16 -128
- data/README +2 -3
- data/Rakefile +3 -3
- data/examples/reverse_proxy.ru +9 -0
- data/lib/rainbows.rb +14 -6
- data/lib/rainbows/base.rb +0 -1
- data/lib/rainbows/const.rb +1 -10
- data/lib/rainbows/coolio/client.rb +8 -4
- data/lib/rainbows/coolio/core.rb +0 -3
- data/lib/rainbows/coolio/thread_client.rb +2 -2
- data/lib/rainbows/coolio_fiber_spawn.rb +6 -6
- data/lib/rainbows/dev_fd_response.rb +16 -9
- data/lib/rainbows/epoll.rb +43 -0
- data/lib/rainbows/epoll/client.rb +232 -0
- data/lib/rainbows/epoll/response_chunk_pipe.rb +18 -0
- data/lib/rainbows/epoll/response_pipe.rb +32 -0
- data/lib/rainbows/epoll/server.rb +31 -0
- data/lib/rainbows/error.rb +1 -9
- data/lib/rainbows/ev_core.rb +12 -12
- data/lib/rainbows/ev_core/cap_input.rb +1 -1
- data/lib/rainbows/event_machine.rb +0 -6
- data/lib/rainbows/event_machine/client.rb +3 -3
- data/lib/rainbows/event_machine/response_chunk_pipe.rb +5 -7
- data/lib/rainbows/event_machine/response_pipe.rb +7 -8
- data/lib/rainbows/fiber/base.rb +2 -2
- data/lib/rainbows/fiber/io.rb +21 -63
- data/lib/rainbows/fiber/io/methods.rb +1 -1
- data/lib/rainbows/http_server.rb +4 -4
- data/lib/rainbows/join_threads.rb +18 -0
- data/lib/rainbows/max_body.rb +2 -1
- data/lib/rainbows/max_body/wrapper.rb +1 -1
- data/lib/rainbows/never_block/event_machine.rb +2 -2
- data/lib/rainbows/process_client.rb +9 -1
- data/lib/rainbows/queue_pool.rb +2 -2
- data/lib/rainbows/response.rb +1 -1
- data/lib/rainbows/rev_fiber_spawn.rb +4 -4
- data/lib/rainbows/revactor/client.rb +4 -5
- data/lib/rainbows/revactor/proxy.rb +1 -1
- data/lib/rainbows/reverse_proxy.rb +189 -0
- data/lib/rainbows/reverse_proxy/coolio.rb +61 -0
- data/lib/rainbows/reverse_proxy/ev_client.rb +39 -0
- data/lib/rainbows/reverse_proxy/event_machine.rb +46 -0
- data/lib/rainbows/reverse_proxy/multi_thread.rb +7 -0
- data/lib/rainbows/reverse_proxy/synchronous.rb +21 -0
- data/lib/rainbows/sendfile.rb +1 -1
- data/lib/rainbows/sync_close.rb +2 -2
- data/lib/rainbows/thread_pool.rb +1 -1
- data/lib/rainbows/writer_thread_pool.rb +1 -1
- data/lib/rainbows/xepoll.rb +24 -0
- data/lib/rainbows/xepoll/client.rb +45 -0
- data/pkg.mk +171 -0
- data/rainbows.gemspec +2 -4
- data/t/GNUmakefile +4 -0
- data/t/bin/content-md5-put +1 -0
- data/t/kgio-pipe-response.ru +9 -1
- data/t/rack-fiber_pool/app.ru +6 -1
- data/t/simple-http_Epoll.ru +9 -0
- data/t/simple-http_XEpoll.ru +9 -0
- data/t/t0014-config-conflict.sh +5 -3
- data/t/t0023-sendfile-byte-range.sh +1 -7
- data/t/t0034-pipelined-pipe-response.sh +2 -1
- data/t/t0035-kgio-pipe-response.sh +0 -7
- data/t/t0041-optional-pool-size.sh +51 -0
- data/t/t0050-response-body-close-has-env.sh +2 -1
- data/t/t0104-rack-input-limit-tiny.sh +6 -4
- data/t/t0105-rack-input-limit-bigger.sh +6 -4
- data/t/t0106-rack-input-keepalive.sh +2 -0
- data/t/t0107-rack-input-limit-zero.sh +60 -0
- data/t/t0113-rewindable-input-false.sh +1 -0
- data/t/t0114-rewindable-input-true.sh +1 -0
- data/t/t0202-async-response-one-oh.sh +56 -0
- data/t/test_isolate.rb +5 -2
- metadata +42 -37
- data/lib/rainbows/rack_input.rb +0 -17
data/lib/rainbows/ev_core.rb
CHANGED
@@ -7,6 +7,8 @@ module Rainbows::EvCore
|
|
7
7
|
NULL_IO = Unicorn::HttpRequest::NULL_IO
|
8
8
|
HttpParser = Rainbows::HttpParser
|
9
9
|
autoload :CapInput, 'rainbows/ev_core/cap_input'
|
10
|
+
RBUF = ""
|
11
|
+
Z = "".freeze
|
10
12
|
|
11
13
|
# Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
|
12
14
|
ASYNC_CALLBACK = "async.callback".freeze
|
@@ -54,7 +56,7 @@ module Rainbows::EvCore
|
|
54
56
|
rv = false
|
55
57
|
else
|
56
58
|
rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
|
57
|
-
rv = false
|
59
|
+
rv = false unless @env["rainbows.autochunk"]
|
58
60
|
end
|
59
61
|
write_headers(status, headers, alive)
|
60
62
|
rv
|
@@ -70,7 +72,7 @@ module Rainbows::EvCore
|
|
70
72
|
@input = mkinput
|
71
73
|
@hp.filter_body(@buf2 = "", @buf)
|
72
74
|
@input << @buf2
|
73
|
-
on_read(
|
75
|
+
on_read(Z)
|
74
76
|
end
|
75
77
|
|
76
78
|
# TeeInput doesn't map too well to this right now...
|
@@ -81,8 +83,7 @@ module Rainbows::EvCore
|
|
81
83
|
@hp.parse or return want_more
|
82
84
|
@state = :body
|
83
85
|
if 0 == @hp.content_length
|
84
|
-
|
85
|
-
app_call # common case
|
86
|
+
app_call NULL_IO # common case
|
86
87
|
else # nil or len > 0
|
87
88
|
prepare_request_body
|
88
89
|
end
|
@@ -90,7 +91,7 @@ module Rainbows::EvCore
|
|
90
91
|
if @hp.body_eof?
|
91
92
|
if @hp.content_length
|
92
93
|
@input.rewind
|
93
|
-
app_call
|
94
|
+
app_call @input
|
94
95
|
else
|
95
96
|
@state = :trailers
|
96
97
|
on_read(data)
|
@@ -98,14 +99,14 @@ module Rainbows::EvCore
|
|
98
99
|
elsif data.size > 0
|
99
100
|
@hp.filter_body(@buf2, @buf << data)
|
100
101
|
@input << @buf2
|
101
|
-
on_read(
|
102
|
+
on_read(Z)
|
102
103
|
else
|
103
104
|
want_more
|
104
105
|
end
|
105
106
|
when :trailers
|
106
107
|
if @hp.trailers(@env, @buf << data)
|
107
108
|
@input.rewind
|
108
|
-
app_call
|
109
|
+
app_call @input
|
109
110
|
else
|
110
111
|
want_more
|
111
112
|
end
|
@@ -114,14 +115,17 @@ module Rainbows::EvCore
|
|
114
115
|
handle_error(e)
|
115
116
|
end
|
116
117
|
|
118
|
+
ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
|
119
|
+
|
117
120
|
def err_413(msg)
|
118
|
-
write(
|
121
|
+
write(ERROR_413_RESPONSE)
|
119
122
|
quit
|
120
123
|
# zip back up the stack
|
121
124
|
raise IOError, msg, []
|
122
125
|
end
|
123
126
|
|
124
127
|
TmpIO = Unicorn::TmpIO
|
128
|
+
CBB = Unicorn::TeeInput.client_body_buffer_size
|
125
129
|
|
126
130
|
def io_for(bytes)
|
127
131
|
bytes <= CBB ? StringIO.new("") : TmpIO.new
|
@@ -139,8 +143,4 @@ module Rainbows::EvCore
|
|
139
143
|
max ? CapInput.new(io_for(max), self, max) : TmpIO.new
|
140
144
|
end
|
141
145
|
end
|
142
|
-
|
143
|
-
def self.setup
|
144
|
-
const_set :CBB, Unicorn::TeeInput.client_body_buffer_size
|
145
|
-
end
|
146
146
|
end
|
@@ -48,11 +48,6 @@ module Rainbows::EventMachine
|
|
48
48
|
|
49
49
|
include Rainbows::Base
|
50
50
|
|
51
|
-
def init_worker_process(worker) # :nodoc:
|
52
|
-
Rainbows::Response.setup(Rainbows::EventMachine::Client)
|
53
|
-
super
|
54
|
-
end
|
55
|
-
|
56
51
|
# runs inside each forked worker, this sits around and waits
|
57
52
|
# for connections and doesn't die until the parent dies (or is
|
58
53
|
# given a INT, QUIT, or TERM signal)
|
@@ -71,7 +66,6 @@ module Rainbows::EventMachine
|
|
71
66
|
Rainbows::EventMachine::Server.const_set(:MAX, max)
|
72
67
|
Rainbows::EventMachine::Server.const_set(:CL, client_class)
|
73
68
|
client_class.const_set(:APP, Rainbows.server.app)
|
74
|
-
Rainbows::EvCore.setup
|
75
69
|
EM.run {
|
76
70
|
conns = EM.instance_variable_get(:@conns) or
|
77
71
|
raise RuntimeError, "EM @conns instance variable not accessible!"
|
@@ -21,7 +21,7 @@ class Rainbows::EventMachine::Client < EM::Connection
|
|
21
21
|
end
|
22
22
|
EM.next_tick { receive_data(nil) } unless @buf.empty?
|
23
23
|
else
|
24
|
-
on_read(data ||
|
24
|
+
on_read(data || Z) if (@buf.size > 0) || data
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
@@ -30,9 +30,9 @@ class Rainbows::EventMachine::Client < EM::Connection
|
|
30
30
|
close_connection_after_writing
|
31
31
|
end
|
32
32
|
|
33
|
-
def app_call
|
33
|
+
def app_call input
|
34
34
|
set_comm_inactivity_timeout 0
|
35
|
-
@env[RACK_INPUT] =
|
35
|
+
@env[RACK_INPUT] = input
|
36
36
|
@env[REMOTE_ADDR] = @_io.kgio_addr
|
37
37
|
@env[ASYNC_CALLBACK] = method(:write_async_response)
|
38
38
|
@env[ASYNC_CLOSE] = EM::DefaultDeferrable.new
|
@@ -9,17 +9,15 @@ module Rainbows::EventMachine::ResponseChunkPipe
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def notify_readable
|
12
|
-
|
13
|
-
|
12
|
+
case data = Kgio.tryread(@io, 16384, RBUF)
|
13
|
+
when String
|
14
14
|
@client.write("#{data.size.to_s(16)}\r\n")
|
15
15
|
@client.write(data)
|
16
16
|
@client.write("\r\n")
|
17
|
-
|
18
|
-
rescue Errno::EAGAIN
|
19
|
-
return
|
20
|
-
rescue EOFError
|
21
|
-
detach
|
17
|
+
when :wait_readable
|
22
18
|
return
|
19
|
+
when nil
|
20
|
+
return detach
|
23
21
|
end while true
|
24
22
|
end
|
25
23
|
end
|
@@ -3,21 +3,20 @@
|
|
3
3
|
module Rainbows::EventMachine::ResponsePipe
|
4
4
|
# garbage avoidance, EM always uses this in a single thread,
|
5
5
|
# so a single buffer for all clients will work safely
|
6
|
-
|
6
|
+
RBUF = Rainbows::EvCore::RBUF
|
7
7
|
|
8
8
|
def initialize(client)
|
9
9
|
@client = client
|
10
10
|
end
|
11
11
|
|
12
12
|
def notify_readable
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
return
|
18
|
-
rescue EOFError
|
19
|
-
detach
|
13
|
+
case data = Kgio.tryread(@io, 16384, RBUF)
|
14
|
+
when String
|
15
|
+
@client.write(data)
|
16
|
+
when :wait_readable
|
20
17
|
return
|
18
|
+
when nil
|
19
|
+
return detach
|
21
20
|
end while true
|
22
21
|
end
|
23
22
|
|
data/lib/rainbows/fiber/base.rb
CHANGED
@@ -17,7 +17,7 @@ module Rainbows::Fiber::Base
|
|
17
17
|
# schedules ones that were blocked on I/O. At most it'll sleep
|
18
18
|
# for one second (returned by the schedule_sleepers method) which
|
19
19
|
# will cause it.
|
20
|
-
def schedule
|
20
|
+
def schedule
|
21
21
|
begin
|
22
22
|
Rainbows.tick
|
23
23
|
t = schedule_sleepers
|
@@ -33,7 +33,7 @@ module Rainbows::Fiber::Base
|
|
33
33
|
ret[1].concat(RD.compact & ret[0]).each { |c| c.f.resume }
|
34
34
|
|
35
35
|
# accept is an expensive syscall, filter out listeners we don't want
|
36
|
-
(ret[0] & LISTENERS).each
|
36
|
+
(ret[0] & LISTENERS).each { |x| yield x }
|
37
37
|
end
|
38
38
|
|
39
39
|
# wakes up any sleepers or keepalive-timeout violators that need to be
|
data/lib/rainbows/fiber/io.rb
CHANGED
@@ -28,7 +28,7 @@ class Rainbows::Fiber::IO
|
|
28
28
|
end
|
29
29
|
|
30
30
|
# for wrapping output response bodies
|
31
|
-
def each
|
31
|
+
def each
|
32
32
|
buf = readpartial(16384)
|
33
33
|
yield buf
|
34
34
|
yield buf while readpartial(16384, buf)
|
@@ -45,78 +45,36 @@ class Rainbows::Fiber::IO
|
|
45
45
|
end
|
46
46
|
|
47
47
|
def write(buf)
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
buf = rv
|
55
|
-
when :wait_writable
|
56
|
-
kgio_wait_writable
|
57
|
-
end
|
58
|
-
end while true
|
59
|
-
else
|
60
|
-
begin
|
61
|
-
(rv = @to_io.write_nonblock(buf)) == buf.bytesize and return
|
62
|
-
buf = byte_slice(buf, rv)
|
63
|
-
rescue Errno::EAGAIN
|
64
|
-
kgio_wait_writable
|
65
|
-
end while true
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def byte_slice(buf, start) # :nodoc:
|
70
|
-
buf.encoding == Encoding::BINARY or
|
71
|
-
buf = buf.dup.force_encoding(Encoding::BINARY)
|
72
|
-
buf.slice(start, buf.size)
|
48
|
+
case rv = Kgio.trywrite(buf)
|
49
|
+
when String
|
50
|
+
buf = rv
|
51
|
+
when :wait_writable
|
52
|
+
kgio_wait_writable
|
53
|
+
end until nil == rv
|
73
54
|
end
|
74
55
|
|
75
56
|
# used for reading headers (respecting keepalive_timeout)
|
76
57
|
def timed_read(buf)
|
77
58
|
expire = nil
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
expire ||= read_expire
|
84
|
-
kgio_wait_readable
|
85
|
-
else
|
86
|
-
return rv
|
87
|
-
end
|
88
|
-
end while true
|
59
|
+
case rv = Kgio.tryread(@to_io, 16384, buf)
|
60
|
+
when :wait_readable
|
61
|
+
return if expire && expire < Time.now
|
62
|
+
expire ||= read_expire
|
63
|
+
kgio_wait_readable
|
89
64
|
else
|
90
|
-
|
91
|
-
|
92
|
-
rescue Errno::EAGAIN
|
93
|
-
return if expire && expire < Time.now
|
94
|
-
expire ||= read_expire
|
95
|
-
kgio_wait_readable
|
96
|
-
end while true
|
97
|
-
end
|
65
|
+
return rv
|
66
|
+
end while true
|
98
67
|
end
|
99
68
|
|
100
69
|
def readpartial(length, buf = "")
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
raise EOFError, "end of file reached", []
|
107
|
-
when :wait_readable
|
108
|
-
kgio_wait_readable
|
109
|
-
else
|
110
|
-
return rv
|
111
|
-
end
|
112
|
-
end while true
|
70
|
+
case rv = Kgio.tryread(@to_io, length, buf)
|
71
|
+
when nil
|
72
|
+
raise EOFError, "end of file reached", []
|
73
|
+
when :wait_readable
|
74
|
+
kgio_wait_readable
|
113
75
|
else
|
114
|
-
|
115
|
-
|
116
|
-
rescue Errno::EAGAIN
|
117
|
-
kgio_wait_readable
|
118
|
-
end while true
|
119
|
-
end
|
76
|
+
return rv
|
77
|
+
end while true
|
120
78
|
end
|
121
79
|
|
122
80
|
def kgio_read(*args)
|
data/lib/rainbows/http_server.rb
CHANGED
@@ -43,7 +43,7 @@ class Rainbows::HttpServer < Unicorn::HttpServer
|
|
43
43
|
|
44
44
|
def ready_pipe=(v)
|
45
45
|
# hacky hook got force Rainbows! to load modules only in workers
|
46
|
-
if @master_pid && @master_pid == Process.ppid
|
46
|
+
if defined?(@master_pid) && @master_pid == Process.ppid
|
47
47
|
extend(Rainbows.const_get(@use))
|
48
48
|
end
|
49
49
|
super
|
@@ -63,8 +63,8 @@ class Rainbows::HttpServer < Unicorn::HttpServer
|
|
63
63
|
raise ArgumentError, "concurrency model #{model.inspect} not supported"
|
64
64
|
args.each do |opt|
|
65
65
|
case opt
|
66
|
-
when Hash; O.update(opt)
|
67
|
-
when Symbol; O[opt] = true
|
66
|
+
when Hash; Rainbows::O.update(opt)
|
67
|
+
when Symbol; Rainbows::O[opt] = true
|
68
68
|
else; raise ArgumentError, "can't handle option: #{opt.inspect}"
|
69
69
|
end
|
70
70
|
end
|
@@ -72,7 +72,7 @@ class Rainbows::HttpServer < Unicorn::HttpServer
|
|
72
72
|
new_defaults = {
|
73
73
|
'rainbows.model' => (@use = model.to_sym),
|
74
74
|
'rack.multithread' => !!(model.to_s =~ /Thread/),
|
75
|
-
'rainbows.autochunk' => [:Coolio,:Rev,
|
75
|
+
'rainbows.autochunk' => [:Coolio,:Rev,:Epoll,:XEpoll,
|
76
76
|
:EventMachine,:NeverBlock].include?(@use),
|
77
77
|
}
|
78
78
|
Rainbows::Const::RACK_DEFAULTS.update(new_defaults)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
# This module only gets loaded on shutdown
|
4
|
+
module Rainbows::JoinThreads
|
5
|
+
|
6
|
+
# blocking acceptor threads must be forced to run
|
7
|
+
def self.acceptors(threads)
|
8
|
+
threads.delete_if do |thr|
|
9
|
+
Rainbows.tick
|
10
|
+
begin
|
11
|
+
thr.run
|
12
|
+
thr.join(0.01)
|
13
|
+
rescue
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end until threads.empty?
|
17
|
+
end
|
18
|
+
end
|
data/lib/rainbows/max_body.rb
CHANGED
@@ -55,7 +55,8 @@ class Rainbows::MaxBody
|
|
55
55
|
case Rainbows.server.use
|
56
56
|
when :Rev, :Coolio, :EventMachine, :NeverBlock,
|
57
57
|
:RevThreadSpawn, :RevThreadPool,
|
58
|
-
:CoolioThreadSpawn, :CoolioThreadPool
|
58
|
+
:CoolioThreadSpawn, :CoolioThreadPool,
|
59
|
+
:Epoll, :XEpoll
|
59
60
|
return
|
60
61
|
end
|
61
62
|
|
@@ -1,9 +1,13 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
2
3
|
module Rainbows::ProcessClient
|
3
4
|
include Rainbows::Response
|
4
|
-
include Rainbows::RackInput
|
5
5
|
include Rainbows::Const
|
6
6
|
|
7
|
+
NULL_IO = Unicorn::HttpRequest::NULL_IO
|
8
|
+
RACK_INPUT = Unicorn::HttpRequest::RACK_INPUT
|
9
|
+
IC = Unicorn::HttpRequest.input_class
|
10
|
+
|
7
11
|
def process_loop
|
8
12
|
@hp = hp = Rainbows::HttpParser.new
|
9
13
|
kgio_read!(16384, buf = hp.buf) or return
|
@@ -38,4 +42,8 @@ module Rainbows::ProcessClient
|
|
38
42
|
def handle_error(e)
|
39
43
|
Rainbows::Error.write(self, e)
|
40
44
|
end
|
45
|
+
|
46
|
+
def set_input(env, hp)
|
47
|
+
env[RACK_INPUT] = 0 == hp.content_length ? NULL_IO : IC.new(self, hp)
|
48
|
+
end
|
41
49
|
end
|
data/lib/rainbows/queue_pool.rb
CHANGED
@@ -6,12 +6,12 @@ require 'thread'
|
|
6
6
|
# This is NOT used for the ThreadPool class, since that class does not
|
7
7
|
# need a userspace Queue.
|
8
8
|
class Rainbows::QueuePool < Struct.new(:queue, :threads)
|
9
|
-
def initialize(size = 20
|
9
|
+
def initialize(size = 20)
|
10
10
|
q = Queue.new
|
11
11
|
self.threads = (1..size).map do
|
12
12
|
Thread.new do
|
13
13
|
while job = q.shift
|
14
|
-
|
14
|
+
yield job
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|