rainbows 2.1.0 → 3.0.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/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -3
- data/Rakefile +5 -2
- data/lib/rainbows.rb +72 -54
- data/lib/rainbows/base.rb +7 -9
- data/lib/rainbows/client.rb +25 -4
- data/lib/rainbows/const.rb +1 -1
- data/lib/rainbows/coolio.rb +6 -3
- data/lib/rainbows/coolio/client.rb +78 -57
- data/lib/rainbows/coolio/core.rb +1 -4
- data/lib/rainbows/coolio/heartbeat.rb +2 -3
- data/lib/rainbows/coolio/master.rb +3 -2
- data/lib/rainbows/coolio/{deferred_chunk_response.rb → response_chunk_pipe.rb} +1 -2
- data/lib/rainbows/coolio/{deferred_response.rb → response_pipe.rb} +1 -1
- data/lib/rainbows/coolio/thread_client.rb +4 -6
- data/lib/rainbows/coolio_fiber_spawn.rb +1 -1
- data/lib/rainbows/coolio_thread_pool.rb +1 -1
- data/lib/rainbows/coolio_thread_pool/watcher.rb +2 -4
- data/lib/rainbows/coolio_thread_spawn.rb +1 -1
- data/lib/rainbows/dev_fd_response.rb +2 -2
- data/lib/rainbows/error.rb +5 -7
- data/lib/rainbows/ev_core.rb +20 -7
- data/lib/rainbows/event_machine.rb +6 -5
- data/lib/rainbows/event_machine/client.rb +46 -53
- data/lib/rainbows/event_machine/response_pipe.rb +2 -3
- data/lib/rainbows/fiber/base.rb +5 -5
- data/lib/rainbows/fiber/body.rb +4 -13
- data/lib/rainbows/fiber/coolio/heartbeat.rb +1 -3
- data/lib/rainbows/fiber/coolio/server.rb +4 -7
- data/lib/rainbows/fiber_pool.rb +1 -1
- data/lib/rainbows/fiber_spawn.rb +2 -2
- data/lib/rainbows/http_parser.rb +12 -0
- data/lib/rainbows/http_server.rb +5 -7
- data/lib/rainbows/max_body.rb +2 -2
- data/lib/rainbows/never_block/core.rb +1 -1
- data/lib/rainbows/process_client.rb +15 -29
- data/lib/rainbows/queue_pool.rb +1 -3
- data/lib/rainbows/rack_input.rb +3 -3
- data/lib/rainbows/response.rb +164 -38
- data/lib/rainbows/revactor.rb +5 -65
- data/lib/rainbows/revactor/client.rb +60 -0
- data/lib/rainbows/revactor/{body.rb → client/methods.rb} +14 -14
- data/lib/rainbows/revactor/{tee_socket.rb → client/tee_socket.rb} +1 -1
- data/lib/rainbows/sendfile.rb +1 -2
- data/lib/rainbows/server_token.rb +1 -1
- data/lib/rainbows/thread_pool.rb +9 -9
- data/lib/rainbows/thread_spawn.rb +7 -6
- data/lib/rainbows/thread_timeout.rb +1 -1
- data/lib/rainbows/writer_thread_pool.rb +9 -25
- data/lib/rainbows/writer_thread_pool/client.rb +44 -1
- data/lib/rainbows/writer_thread_spawn.rb +2 -11
- data/lib/rainbows/writer_thread_spawn/client.rb +53 -13
- data/rainbows.gemspec +3 -12
- data/t/async_chunk_app.ru +62 -0
- data/t/byte-range-common.sh +142 -0
- data/t/t0022-copy_stream-byte-range.sh +2 -111
- data/t/t0023-sendfile-byte-range.sh +2 -32
- data/t/t0025-write-on-close.sh +23 -0
- data/t/t0040-keepalive_requests-setting.sh +0 -5
- data/t/t0402-async-keepalive.sh +146 -0
- data/t/t0500-cramp-streaming.sh +2 -0
- data/t/t0501-cramp-rainsocket.sh +2 -0
- data/t/t9000-rack-app-pool.sh +1 -1
- data/t/test_isolate.rb +5 -10
- data/t/test_isolate_cramp.rb +26 -0
- data/t/write-on-close.ru +11 -0
- metadata +33 -30
- data/lib/rainbows/coolio/sendfile.rb +0 -17
- data/lib/rainbows/response/body.rb +0 -127
- data/lib/rainbows/response/range.rb +0 -34
- data/lib/rainbows/timed_read.rb +0 -28
data/lib/rainbows/coolio/core.rb
CHANGED
@@ -8,15 +8,12 @@ module Rainbows::Coolio::Core
|
|
8
8
|
# given a INT, QUIT, or TERM signal)
|
9
9
|
def worker_loop(worker)
|
10
10
|
Rainbows::Response.setup(Rainbows::Coolio::Client)
|
11
|
-
require 'rainbows/coolio/sendfile'
|
12
|
-
Rainbows::Coolio::Client.__send__(:include, Rainbows::Coolio::Sendfile)
|
13
11
|
init_worker_process(worker)
|
14
12
|
mod = Rainbows.const_get(@use)
|
15
13
|
rloop = Rainbows::Coolio::Server.const_set(:LOOP, Coolio::Loop.default)
|
16
|
-
Rainbows::Coolio::Client.const_set(:LOOP, rloop)
|
17
14
|
Rainbows::Coolio::Server.const_set(:MAX, @worker_connections)
|
18
15
|
Rainbows::Coolio::Server.const_set(:CL, mod.const_get(:Client))
|
19
|
-
Rainbows::EvCore.const_set(:APP,
|
16
|
+
Rainbows::EvCore.const_set(:APP, Rainbows.server.app)
|
20
17
|
Rainbows::EvCore.setup
|
21
18
|
Rainbows::Coolio::Heartbeat.new(1, true).attach(rloop)
|
22
19
|
LISTENERS.map! { |s| Rainbows::Coolio::Server.new(s).attach(rloop) }
|
@@ -8,13 +8,12 @@
|
|
8
8
|
class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
|
9
9
|
KATO = Rainbows::Coolio::KATO
|
10
10
|
CONN = Rainbows::Coolio::CONN
|
11
|
-
G = Rainbows::G
|
12
11
|
|
13
12
|
def on_timer
|
14
|
-
if (ot =
|
13
|
+
if (ot = Rainbows.keepalive_timeout) >= 0
|
15
14
|
ot = Time.now - ot
|
16
15
|
KATO.delete_if { |client, time| time < ot and client.timeout? }
|
17
16
|
end
|
18
|
-
exit if (!
|
17
|
+
exit if (! Rainbows.tick && CONN.size <= 0)
|
19
18
|
end
|
20
19
|
end
|
@@ -7,15 +7,16 @@ class Rainbows::Coolio::Master < Coolio::IOWatcher
|
|
7
7
|
@reader, @writer = Kgio::Pipe.new
|
8
8
|
super(@reader)
|
9
9
|
@queue = queue
|
10
|
+
@wbuf, @rbuf = "\0", "\0"
|
10
11
|
end
|
11
12
|
|
12
13
|
def <<(output)
|
13
14
|
@queue << output
|
14
|
-
@writer.kgio_trywrite(
|
15
|
+
@writer.kgio_trywrite(@wbuf)
|
15
16
|
end
|
16
17
|
|
17
18
|
def on_readable
|
18
|
-
if String === @reader.kgio_tryread(1)
|
19
|
+
if String === @reader.kgio_tryread(1, @rbuf)
|
19
20
|
client, response = @queue.pop
|
20
21
|
client.response_write(response)
|
21
22
|
end
|
@@ -2,8 +2,7 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
#
|
4
4
|
# this is class is specific to Coolio for proxying IO-derived objects
|
5
|
-
class Rainbows::Coolio::
|
6
|
-
Rainbows::Coolio::DeferredResponse
|
5
|
+
class Rainbows::Coolio::ResponseChunkPipe < Rainbows::Coolio::ResponsePipe
|
7
6
|
def on_read(data)
|
8
7
|
@client.write("#{data.size.to_s(16)}\r\n")
|
9
8
|
@client.write(data)
|
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# this is class is specific to Coolio for writing large static files
|
5
5
|
# or proxying IO-derived objects
|
6
|
-
class Rainbows::Coolio::
|
6
|
+
class Rainbows::Coolio::ResponsePipe < Coolio::IO
|
7
7
|
def initialize(io, client, body)
|
8
8
|
super(io)
|
9
9
|
@client, @body = client, body
|
@@ -14,11 +14,9 @@ class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
|
14
14
|
|
15
15
|
# this is only called in the master thread
|
16
16
|
def response_write(response)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
@state = :headers
|
17
|
+
ev_write_response(*response, @hp.next?)
|
18
|
+
rescue => e
|
19
|
+
handle_error(e)
|
22
20
|
end
|
23
21
|
|
24
22
|
# fails-safe application dispatch, we absolutely cannot
|
@@ -27,7 +25,7 @@ class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
|
27
25
|
def app_response
|
28
26
|
begin
|
29
27
|
@env[REMOTE_ADDR] = @_io.kgio_addr
|
30
|
-
APP.call(@env.
|
28
|
+
APP.call(@env.merge!(RACK_DEFAULTS))
|
31
29
|
rescue => e
|
32
30
|
Rainbows::Error.app(e) # we guarantee this does not raise
|
33
31
|
[ 500, {}, [] ]
|
@@ -18,7 +18,7 @@ module Rainbows::CoolioFiberSpawn
|
|
18
18
|
init_worker_process(worker)
|
19
19
|
Server.const_set(:MAX, @worker_connections)
|
20
20
|
Rainbows::Fiber::Base.setup(Server, nil)
|
21
|
-
Server.const_set(:APP,
|
21
|
+
Server.const_set(:APP, Rainbows.server.app)
|
22
22
|
Heartbeat.new(1, true).attach(Coolio::Loop.default)
|
23
23
|
LISTENERS.map! { |s| Server.new(s).attach(Coolio::Loop.default) }
|
24
24
|
Coolio::Loop.default.run
|
@@ -16,6 +16,7 @@
|
|
16
16
|
# users are NOT advised to use this due to high CPU usage.
|
17
17
|
module Rainbows::CoolioThreadPool
|
18
18
|
# :stopdoc:
|
19
|
+
autoload :Client, 'rainbows/coolio_thread_pool/client'
|
19
20
|
DEFAULTS = {
|
20
21
|
:pool_size => 20, # same default size as ThreadPool (w/o Coolio)
|
21
22
|
}
|
@@ -53,5 +54,4 @@ module Rainbows::CoolioThreadPool
|
|
53
54
|
end
|
54
55
|
end
|
55
56
|
# :enddoc:
|
56
|
-
require 'rainbows/coolio_thread_pool/client'
|
57
57
|
require 'rainbows/coolio_thread_pool/watcher'
|
@@ -1,14 +1,12 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
3
|
class Rainbows::CoolioThreadPool::Watcher < Coolio::TimerWatcher
|
4
|
-
G = Rainbows::G
|
5
|
-
|
6
4
|
def initialize(threads)
|
7
5
|
@threads = threads
|
8
|
-
super(
|
6
|
+
super(Rainbows.server.timeout, true)
|
9
7
|
end
|
10
8
|
|
11
9
|
def on_timer
|
12
|
-
@threads.each { |t| t.join(0) and
|
10
|
+
@threads.each { |t| t.join(0) and Rainbows.quit! }
|
13
11
|
end
|
14
12
|
end
|
@@ -15,6 +15,7 @@
|
|
15
15
|
# users are NOT advised to use this due to high CPU usage.
|
16
16
|
module Rainbows::CoolioThreadSpawn
|
17
17
|
include Rainbows::Coolio::Core
|
18
|
+
autoload :Client, 'rainbows/coolio_thread_spawn/client'
|
18
19
|
|
19
20
|
def init_worker_process(worker) # :nodoc:
|
20
21
|
super
|
@@ -24,4 +25,3 @@ module Rainbows::CoolioThreadSpawn
|
|
24
25
|
end
|
25
26
|
end
|
26
27
|
# :enddoc:
|
27
|
-
require 'rainbows/coolio_thread_spawn/client'
|
@@ -39,7 +39,7 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
39
39
|
io ||= File.open(body.to_path) if body.respond_to?(:to_path)
|
40
40
|
return response if io.nil?
|
41
41
|
|
42
|
-
headers = HeaderHash.new(headers)
|
42
|
+
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
43
43
|
st = io.stat
|
44
44
|
fileno = io.fileno
|
45
45
|
FD_MAP[fileno] = io
|
@@ -47,7 +47,7 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
47
47
|
headers['Content-Length'] ||= st.size.to_s
|
48
48
|
headers.delete('Transfer-Encoding')
|
49
49
|
elsif st.pipe? || st.socket? # epoll-able things
|
50
|
-
unless headers
|
50
|
+
unless headers.include?('Content-Length')
|
51
51
|
if env['rainbows.autochunk']
|
52
52
|
headers['Transfer-Encoding'] = 'chunked'
|
53
53
|
else
|
data/lib/rainbows/error.rb
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
module Rainbows::Error
|
4
4
|
|
5
|
-
G = Rainbows::G
|
6
|
-
|
7
5
|
# if we get any error, try to write something back to the client
|
8
6
|
# assuming we haven't closed the socket, but don't get hung up
|
9
7
|
# if the socket is already closed or broken. We'll always ensure
|
@@ -20,15 +18,15 @@ module Rainbows::Error
|
|
20
18
|
end
|
21
19
|
|
22
20
|
def self.app(e)
|
23
|
-
|
24
|
-
|
21
|
+
Rainbows.server.logger.error "app error: #{e.inspect}"
|
22
|
+
Rainbows.server.logger.error e.backtrace.join("\n")
|
25
23
|
rescue
|
26
24
|
end
|
27
25
|
|
28
26
|
def self.listen_loop(e)
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
Rainbows.alive or return
|
28
|
+
Rainbows.server.logger.error "listen loop error: #{e.inspect}."
|
29
|
+
Rainbows.server.logger.error e.backtrace.join("\n")
|
32
30
|
rescue
|
33
31
|
end
|
34
32
|
|
data/lib/rainbows/ev_core.rb
CHANGED
@@ -4,16 +4,28 @@
|
|
4
4
|
module Rainbows::EvCore
|
5
5
|
include Rainbows::Const
|
6
6
|
include Rainbows::Response
|
7
|
-
G = Rainbows::G
|
8
7
|
NULL_IO = Unicorn::HttpRequest::NULL_IO
|
9
|
-
HttpParser =
|
8
|
+
HttpParser = Rainbows::HttpParser
|
10
9
|
autoload :CapInput, 'rainbows/ev_core/cap_input'
|
11
10
|
|
12
11
|
# Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
|
13
12
|
ASYNC_CALLBACK = "async.callback".freeze
|
14
|
-
|
15
13
|
ASYNC_CLOSE = "async.close".freeze
|
16
14
|
|
15
|
+
def write_async_response(response)
|
16
|
+
status, headers, body = response
|
17
|
+
if alive = @hp.next?
|
18
|
+
# we can't do HTTP keepalive without Content-Length or
|
19
|
+
# "Transfer-Encoding: chunked", and the async.callback stuff
|
20
|
+
# isn't Rack::Lint-compatible, so we have to enforce it here.
|
21
|
+
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
22
|
+
alive = headers.include?(Content_Length) ||
|
23
|
+
!!(%r{\Achunked\z}i =~ headers[Transfer_Encoding])
|
24
|
+
end
|
25
|
+
@deferred = nil
|
26
|
+
ev_write_response(status, headers, body, alive)
|
27
|
+
end
|
28
|
+
|
17
29
|
def post_init
|
18
30
|
@hp = HttpParser.new
|
19
31
|
@env = @hp.env
|
@@ -36,14 +48,15 @@ module Rainbows::EvCore
|
|
36
48
|
end
|
37
49
|
|
38
50
|
# returns whether to enable response chunking for autochunk models
|
39
|
-
def stream_response_headers(status, headers)
|
40
|
-
|
51
|
+
def stream_response_headers(status, headers, alive)
|
52
|
+
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
53
|
+
if headers.include?(Content_Length)
|
41
54
|
rv = false
|
42
55
|
else
|
43
|
-
rv = !!(headers[
|
56
|
+
rv = !!(headers[Transfer_Encoding] =~ %r{\Achunked\z}i)
|
44
57
|
rv = false if headers.delete('X-Rainbows-Autochunk') == 'no'
|
45
58
|
end
|
46
|
-
|
59
|
+
write_headers(status, headers, alive)
|
47
60
|
rv
|
48
61
|
end
|
49
62
|
|
@@ -44,6 +44,7 @@ module Rainbows::EventMachine
|
|
44
44
|
autoload :ResponsePipe, 'rainbows/event_machine/response_pipe'
|
45
45
|
autoload :ResponseChunkPipe, 'rainbows/event_machine/response_chunk_pipe'
|
46
46
|
autoload :TryDefer, 'rainbows/event_machine/try_defer'
|
47
|
+
autoload :Client, 'rainbows/event_machine/client'
|
47
48
|
|
48
49
|
include Rainbows::Base
|
49
50
|
|
@@ -57,8 +58,9 @@ module Rainbows::EventMachine
|
|
57
58
|
# given a INT, QUIT, or TERM signal)
|
58
59
|
def worker_loop(worker) # :nodoc:
|
59
60
|
init_worker_process(worker)
|
60
|
-
|
61
|
-
|
61
|
+
server = Rainbows.server
|
62
|
+
server.app.respond_to?(:deferred?) and
|
63
|
+
server.app = TryDefer.new(server.app)
|
62
64
|
|
63
65
|
# enable them both, should be non-fatal if not supported
|
64
66
|
EM.epoll
|
@@ -68,14 +70,14 @@ module Rainbows::EventMachine
|
|
68
70
|
max = worker_connections + LISTENERS.size
|
69
71
|
Rainbows::EventMachine::Server.const_set(:MAX, max)
|
70
72
|
Rainbows::EventMachine::Server.const_set(:CL, client_class)
|
71
|
-
client_class.const_set(:APP,
|
73
|
+
client_class.const_set(:APP, Rainbows.server.app)
|
72
74
|
Rainbows::EvCore.setup
|
73
75
|
EM.run {
|
74
76
|
conns = EM.instance_variable_get(:@conns) or
|
75
77
|
raise RuntimeError, "EM @conns instance variable not accessible!"
|
76
78
|
Rainbows::EventMachine::Server.const_set(:CUR, conns)
|
77
79
|
EM.add_periodic_timer(1) do
|
78
|
-
unless
|
80
|
+
unless Rainbows.tick
|
79
81
|
conns.each_value { |c| client_class === c and c.quit }
|
80
82
|
EM.stop if conns.empty? && EM.reactor_running?
|
81
83
|
end
|
@@ -89,5 +91,4 @@ module Rainbows::EventMachine
|
|
89
91
|
end
|
90
92
|
end
|
91
93
|
# :enddoc:
|
92
|
-
require 'rainbows/event_machine/client'
|
93
94
|
require 'rainbows/event_machine/server'
|
@@ -1,12 +1,11 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
# :enddoc:
|
3
3
|
class Rainbows::EventMachine::Client < EM::Connection
|
4
|
-
attr_writer :body
|
5
4
|
include Rainbows::EvCore
|
6
5
|
|
7
6
|
def initialize(io)
|
8
7
|
@_io = io
|
9
|
-
@
|
8
|
+
@deferred = nil
|
10
9
|
end
|
11
10
|
|
12
11
|
alias write send_data
|
@@ -15,7 +14,7 @@ class Rainbows::EventMachine::Client < EM::Connection
|
|
15
14
|
# To avoid clobbering the current streaming response
|
16
15
|
# (often a static file), we do not attempt to process another
|
17
16
|
# request on the same connection until the first is complete
|
18
|
-
if @
|
17
|
+
if @deferred
|
19
18
|
if data
|
20
19
|
@buf << data
|
21
20
|
@_io.shutdown(Socket::SHUT_RD) if @buf.size > 0x1c000
|
@@ -35,83 +34,77 @@ class Rainbows::EventMachine::Client < EM::Connection
|
|
35
34
|
set_comm_inactivity_timeout 0
|
36
35
|
@env[RACK_INPUT] = @input
|
37
36
|
@env[REMOTE_ADDR] = @_io.kgio_addr
|
38
|
-
@env[ASYNC_CALLBACK] = method(:
|
37
|
+
@env[ASYNC_CALLBACK] = method(:write_async_response)
|
39
38
|
@env[ASYNC_CLOSE] = EM::DefaultDeferrable.new
|
39
|
+
status, headers, body = catch(:async) {
|
40
|
+
APP.call(@env.merge!(RACK_DEFAULTS))
|
41
|
+
}
|
40
42
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
# second (pipelined) request could be a stuck behind a
|
45
|
-
# long-running async response
|
46
|
-
(response.nil? || -1 == response[0]) and return @state = :close
|
43
|
+
(nil == status || -1 == status) ? @deferred = true :
|
44
|
+
ev_write_response(status, headers, body, @hp.next?)
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
set_comm_inactivity_timeout(G.kato)
|
53
|
-
elsif @body.nil?
|
54
|
-
EM.next_tick { receive_data(nil) }
|
55
|
-
end
|
56
|
-
else
|
57
|
-
em_write_response(response, false)
|
47
|
+
def deferred_errback(orig_body)
|
48
|
+
@deferred.errback do
|
49
|
+
orig_body.close if orig_body.respond_to?(:close)
|
50
|
+
quit
|
58
51
|
end
|
59
52
|
end
|
60
53
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
else
|
67
|
-
headers = nil
|
54
|
+
def deferred_callback(orig_body, alive)
|
55
|
+
@deferred.callback do
|
56
|
+
orig_body.close if orig_body.respond_to?(:close)
|
57
|
+
@deferred = nil
|
58
|
+
alive ? receive_data(nil) : quit
|
68
59
|
end
|
60
|
+
end
|
69
61
|
|
62
|
+
def ev_write_response(status, headers, body, alive)
|
63
|
+
@state = :headers if alive
|
70
64
|
if body.respond_to?(:errback) && body.respond_to?(:callback)
|
71
|
-
@
|
72
|
-
body
|
73
|
-
body
|
74
|
-
# async response, this could be a trickle as is in comet-style apps
|
75
|
-
headers[CONNECTION] = CLOSE if headers
|
76
|
-
alive = true
|
65
|
+
@deferred = body
|
66
|
+
deferred_errback(body)
|
67
|
+
deferred_callback(body, alive)
|
77
68
|
elsif body.respond_to?(:to_path)
|
78
69
|
st = File.stat(path = body.to_path)
|
79
70
|
|
80
71
|
if st.file?
|
81
|
-
|
82
|
-
@
|
83
|
-
|
84
|
-
|
85
|
-
quit
|
86
|
-
end
|
87
|
-
@body.callback do
|
88
|
-
body.close if body.respond_to?(:close)
|
89
|
-
@body = nil
|
90
|
-
alive ? receive_data(nil) : quit
|
91
|
-
end
|
72
|
+
write_headers(status, headers, alive)
|
73
|
+
@deferred = stream_file_data(path)
|
74
|
+
deferred_errback(body)
|
75
|
+
deferred_callback(body, alive)
|
92
76
|
return
|
93
77
|
elsif st.socket? || st.pipe?
|
94
|
-
|
95
|
-
chunk = stream_response_headers(status, headers)
|
78
|
+
io = body_to_io(@deferred = body)
|
79
|
+
chunk = stream_response_headers(status, headers, alive)
|
96
80
|
m = chunk ? Rainbows::EventMachine::ResponseChunkPipe :
|
97
81
|
Rainbows::EventMachine::ResponsePipe
|
98
|
-
return EM.watch(io, m, self
|
82
|
+
return EM.watch(io, m, self).notify_readable = true
|
99
83
|
end
|
100
84
|
# char or block device... WTF? fall through to body.each
|
101
85
|
end
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
86
|
+
write_response(status, headers, body, alive)
|
87
|
+
if alive
|
88
|
+
if @deferred.nil?
|
89
|
+
if @buf.empty?
|
90
|
+
set_comm_inactivity_timeout(Rainbows.keepalive_timeout)
|
91
|
+
else
|
92
|
+
EM.next_tick { receive_data(nil) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
else
|
96
|
+
quit unless @deferred
|
97
|
+
end
|
106
98
|
end
|
107
99
|
|
108
100
|
def next!
|
109
|
-
@
|
101
|
+
@deferred.close if @deferred.respond_to?(:close)
|
102
|
+
@hp.keepalive? ? receive_data(@deferred = nil) : quit
|
110
103
|
end
|
111
104
|
|
112
105
|
def unbind
|
113
106
|
async_close = @env[ASYNC_CLOSE] and async_close.succeed
|
114
|
-
@
|
107
|
+
@deferred.respond_to?(:fail) and @deferred.fail
|
115
108
|
begin
|
116
109
|
@_io.close
|
117
110
|
rescue Errno::EBADF
|