rainbows 2.1.0 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|