rainbows 0.95.1 → 0.96.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Documentation/comparison.haml +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -1
- data/Rakefile +16 -3
- data/Static_Files +11 -11
- data/TODO +1 -6
- data/lib/rainbows.rb +3 -2
- data/lib/rainbows/base.rb +12 -9
- data/lib/rainbows/const.rb +2 -4
- data/lib/rainbows/dev_fd_response.rb +16 -13
- data/lib/rainbows/error.rb +2 -0
- data/lib/rainbows/ev_core.rb +13 -0
- data/lib/rainbows/event_machine.rb +85 -128
- data/lib/rainbows/event_machine/response_chunk_pipe.rb +25 -0
- data/lib/rainbows/event_machine/response_pipe.rb +30 -0
- data/lib/rainbows/event_machine/try_defer.rb +27 -0
- data/lib/rainbows/fiber/body.rb +6 -4
- data/lib/rainbows/fiber/io.rb +4 -4
- data/lib/rainbows/fiber/rev.rb +16 -8
- data/lib/rainbows/http_response.rb +5 -6
- data/lib/rainbows/response.rb +37 -22
- data/lib/rainbows/response/body.rb +19 -16
- data/lib/rainbows/response/range.rb +34 -0
- data/lib/rainbows/rev.rb +2 -1
- data/lib/rainbows/rev/client.rb +105 -77
- data/lib/rainbows/rev/deferred_chunk_response.rb +16 -0
- data/lib/rainbows/rev/deferred_response.rb +16 -24
- data/lib/rainbows/rev/sendfile.rb +4 -13
- data/lib/rainbows/rev/thread.rb +3 -12
- data/lib/rainbows/rev_thread_pool.rb +2 -2
- data/lib/rainbows/revactor.rb +16 -9
- data/lib/rainbows/revactor/body.rb +42 -0
- data/lib/rainbows/revactor/proxy.rb +55 -0
- data/lib/rainbows/sendfile.rb +12 -14
- data/lib/rainbows/stream_file.rb +3 -3
- data/lib/rainbows/writer_thread_pool.rb +12 -12
- data/lib/rainbows/writer_thread_spawn.rb +6 -7
- data/t/GNUmakefile +2 -2
- data/t/close-pipe-response.ru +25 -0
- data/t/cramp/rainsocket.ru +1 -1
- data/t/fast-pipe-response.ru +10 -0
- data/t/file-wrap-to_path.ru +24 -0
- data/t/t0015-working_directory.sh +5 -1
- data/t/t0020-large-sendfile-response.sh +3 -3
- data/t/t0021-sendfile-wrap-to_path.sh +108 -0
- data/t/t0022-copy_stream-byte-range.sh +139 -0
- data/t/t0023-sendfile-byte-range.sh +63 -0
- data/t/t0024-pipelined-sendfile-response.sh +89 -0
- data/t/t0030-fast-pipe-response.sh +63 -0
- data/t/t0031-close-pipe-response.sh +96 -0
- data/t/t0034-pipelined-pipe-response.sh +87 -0
- data/t/t0105-rack-input-limit-bigger.sh +5 -2
- data/t/t0500-cramp-streaming.sh +0 -1
- data/t/t0501-cramp-rainsocket.sh +1 -0
- data/t/t9000-rack-app-pool.sh +1 -1
- data/t/test_isolate.rb +1 -0
- metadata +29 -5
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
module Rainbows::EventMachine::ResponseChunkPipe
|
4
|
+
include Rainbows::EventMachine::ResponsePipe
|
5
|
+
|
6
|
+
def unbind
|
7
|
+
@client.write("0\r\n\r\n")
|
8
|
+
super
|
9
|
+
end
|
10
|
+
|
11
|
+
def notify_readable
|
12
|
+
begin
|
13
|
+
data = @io.read_nonblock(16384, BUF)
|
14
|
+
@client.write("#{data.size.to_s(16)}\r\n")
|
15
|
+
@client.write(data)
|
16
|
+
@client.write("\r\n")
|
17
|
+
rescue Errno::EINTR
|
18
|
+
rescue Errno::EAGAIN
|
19
|
+
return
|
20
|
+
rescue EOFError
|
21
|
+
detach
|
22
|
+
return
|
23
|
+
end while true
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
module Rainbows::EventMachine::ResponsePipe
|
4
|
+
# garbage avoidance, EM always uses this in a single thread,
|
5
|
+
# so a single buffer for all clients will work safely
|
6
|
+
BUF = ''
|
7
|
+
|
8
|
+
def initialize(client, alive, body)
|
9
|
+
@client, @alive, @body = client, alive, body
|
10
|
+
end
|
11
|
+
|
12
|
+
def notify_readable
|
13
|
+
begin
|
14
|
+
@client.write(@io.read_nonblock(16384, BUF))
|
15
|
+
rescue Errno::EINTR
|
16
|
+
rescue Errno::EAGAIN
|
17
|
+
return
|
18
|
+
rescue EOFError
|
19
|
+
detach
|
20
|
+
return
|
21
|
+
end while true
|
22
|
+
end
|
23
|
+
|
24
|
+
def unbind
|
25
|
+
@client.body = nil
|
26
|
+
@alive ? @client.on_read('') : @client.quit
|
27
|
+
@body.close if @body.respond_to?(:close)
|
28
|
+
@io.close unless @io.closed?
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
|
4
|
+
# Middleware that will run the app dispatch in a separate thread.
|
5
|
+
# This middleware is automatically loaded by Rainbows! when using
|
6
|
+
# EventMachine and if the app responds to the +deferred?+ method.
|
7
|
+
class Rainbows::EventMachine::TryDefer < Struct.new(:app)
|
8
|
+
# shortcuts
|
9
|
+
ASYNC_CALLBACK = Rainbows::EvCore::ASYNC_CALLBACK
|
10
|
+
|
11
|
+
def initialize(app)
|
12
|
+
# the entire app becomes multithreaded, even the root (non-deferred)
|
13
|
+
# thread since any thread can share processes with others
|
14
|
+
Rainbows::Const::RACK_DEFAULTS['rack.multithread'] = true
|
15
|
+
super
|
16
|
+
end
|
17
|
+
|
18
|
+
def call(env)
|
19
|
+
if app.deferred?(env)
|
20
|
+
EM.defer(proc { catch(:async) { app.call(env) } }, env[ASYNC_CALLBACK])
|
21
|
+
# all of the async/deferred stuff breaks Rack::Lint :<
|
22
|
+
nil
|
23
|
+
else
|
24
|
+
app.call(env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/rainbows/fiber/body.rb
CHANGED
@@ -12,15 +12,17 @@ module Rainbows::Fiber::Body # :nodoc:
|
|
12
12
|
|
13
13
|
# the sendfile 1.0.0+ gem includes IO#sendfile_nonblock
|
14
14
|
if ::IO.method_defined?(:sendfile_nonblock)
|
15
|
-
def write_body_file(client, body)
|
16
|
-
sock,
|
15
|
+
def write_body_file(client, body, range)
|
16
|
+
sock, n = client.to_io, nil
|
17
|
+
offset, count = range ? range : [ 0, body.stat.size ]
|
17
18
|
begin
|
18
|
-
|
19
|
+
offset += (n = sock.sendfile_nonblock(body, offset, count))
|
19
20
|
rescue Errno::EAGAIN
|
20
21
|
client.wait_writable
|
22
|
+
retry
|
21
23
|
rescue EOFError
|
22
24
|
break
|
23
|
-
end while
|
25
|
+
end while (count -= n) > 0
|
24
26
|
end
|
25
27
|
else
|
26
28
|
ALIASES[:write_body] = :write_body_each
|
data/lib/rainbows/fiber/io.rb
CHANGED
@@ -24,11 +24,11 @@ module Rainbows
|
|
24
24
|
|
25
25
|
# for wrapping output response bodies
|
26
26
|
def each(&block)
|
27
|
-
|
28
|
-
yield
|
27
|
+
if buf = readpartial(16384)
|
28
|
+
yield buf
|
29
|
+
yield buf while readpartial(16384, buf)
|
30
|
+
end
|
29
31
|
rescue EOFError
|
30
|
-
break
|
31
|
-
end while true
|
32
32
|
self
|
33
33
|
end
|
34
34
|
|
data/lib/rainbows/fiber/rev.rb
CHANGED
@@ -81,7 +81,6 @@ module Rainbows::Fiber
|
|
81
81
|
buf = client.read_timeout or return
|
82
82
|
hp = HttpParser.new
|
83
83
|
env = {}
|
84
|
-
alive = true
|
85
84
|
remote_addr = Rainbows.addr(io)
|
86
85
|
|
87
86
|
begin # loop
|
@@ -91,18 +90,27 @@ module Rainbows::Fiber
|
|
91
90
|
env[RACK_INPUT] = 0 == hp.content_length ?
|
92
91
|
HttpRequest::NULL_IO : TeeInput.new(client, env, hp, buf)
|
93
92
|
env[REMOTE_ADDR] = remote_addr
|
94
|
-
|
93
|
+
status, headers, body = APP.call(env.update(RACK_DEFAULTS))
|
95
94
|
|
96
|
-
if 100 ==
|
95
|
+
if 100 == status.to_i
|
97
96
|
client.write(EXPECT_100_RESPONSE)
|
98
97
|
env.delete(HTTP_EXPECT)
|
99
|
-
|
98
|
+
status, headers, body = APP.call(env)
|
100
99
|
end
|
101
100
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
101
|
+
if hp.headers?
|
102
|
+
headers = HH.new(headers)
|
103
|
+
range = make_range!(env, status, headers) and status = range.shift
|
104
|
+
headers[CONNECTION] = if hp.keepalive? && G.alive
|
105
|
+
KEEP_ALIVE
|
106
|
+
else
|
107
|
+
env = false
|
108
|
+
CLOSE
|
109
|
+
end
|
110
|
+
client.write(response_header(status, headers))
|
111
|
+
end
|
112
|
+
write_body(client, body, range)
|
113
|
+
end while env && env.clear && hp.reset.nil?
|
106
114
|
rescue => e
|
107
115
|
Error.write(io, e)
|
108
116
|
ensure
|
@@ -4,20 +4,19 @@
|
|
4
4
|
# Cramp 0.11 relies on this, and is only activated by Cramp
|
5
5
|
if defined?(Cramp) && defined?(Rainbows::EventMachine::Client)
|
6
6
|
class Rainbows::HttpResponse
|
7
|
-
|
8
|
-
|
9
|
-
alias write write_response
|
7
|
+
# dummy method for Cramp to alias_method_chain
|
8
|
+
def self.write(client, response, out)
|
10
9
|
end
|
11
10
|
end
|
12
11
|
|
13
12
|
module Rainbows::EventMachine::CrampSocket
|
14
|
-
def
|
13
|
+
def em_write_response(response, alive = false)
|
15
14
|
if websocket?
|
16
15
|
write web_socket_upgrade_data
|
17
16
|
web_socket_handshake!
|
18
|
-
|
17
|
+
response[1] = nil # disable response headers
|
19
18
|
end
|
20
|
-
super
|
19
|
+
super
|
21
20
|
end
|
22
21
|
end
|
23
22
|
|
data/lib/rainbows/response.rb
CHANGED
@@ -3,41 +3,56 @@
|
|
3
3
|
require 'time' # for Time#httpdate
|
4
4
|
|
5
5
|
module Rainbows::Response
|
6
|
+
autoload :Body, 'rainbows/response/body'
|
7
|
+
autoload :Range, 'rainbows/response/range'
|
6
8
|
|
7
9
|
CODES = Unicorn::HttpResponse::CODES
|
10
|
+
CRLF = "\r\n"
|
8
11
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
+
# freeze headers we may set as hash keys for a small speedup
|
13
|
+
CONNECTION = "Connection".freeze
|
14
|
+
CLOSE = "close"
|
15
|
+
KEEP_ALIVE = "keep-alive"
|
16
|
+
HH = Rack::Utils::HeaderHash
|
12
17
|
|
18
|
+
def response_header(status, headers)
|
19
|
+
status = CODES[status.to_i] || status
|
20
|
+
rv = "HTTP/1.1 #{status}\r\n" \
|
21
|
+
"Date: #{Time.now.httpdate}\r\n" \
|
22
|
+
"Status: #{status}\r\n"
|
13
23
|
headers.each do |key, value|
|
14
|
-
next if %r{\A(?:X-Rainbows-|
|
24
|
+
next if %r{\A(?:X-Rainbows-|Date\z|Status\z)}i =~ key
|
15
25
|
if value =~ /\n/
|
16
26
|
# avoiding blank, key-only cookies with /\n+/
|
17
|
-
|
27
|
+
rv << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join('')
|
18
28
|
else
|
19
|
-
|
29
|
+
rv << "#{key}: #{value}\r\n"
|
20
30
|
end
|
21
31
|
end
|
22
|
-
|
23
|
-
"HTTP/1.1 #{status}\r\n" \
|
24
|
-
"Date: #{Time.now.httpdate}\r\n" \
|
25
|
-
"Status: #{status}\r\n" \
|
26
|
-
"#{out.join('')}\r\n"
|
27
|
-
end
|
28
|
-
|
29
|
-
def write_header(socket, response, out)
|
30
|
-
out and socket.write(response_header(response, out))
|
31
|
-
end
|
32
|
-
|
33
|
-
def write_response(socket, response, out)
|
34
|
-
write_header(socket, response, out)
|
35
|
-
write_body(socket, response[2])
|
32
|
+
rv << CRLF
|
36
33
|
end
|
37
34
|
|
38
35
|
# called after forking
|
39
36
|
def self.setup(klass)
|
40
|
-
|
41
|
-
|
37
|
+
range_class = body_class = klass
|
38
|
+
case Rainbows::Const::RACK_DEFAULTS['rainbows.model']
|
39
|
+
when :WriterThreadSpawn
|
40
|
+
body_class = Rainbows::WriterThreadSpawn::MySocket
|
41
|
+
range_class = Rainbows::HttpServer
|
42
|
+
when :EventMachine, :NeverBlock
|
43
|
+
range_class = nil # :<
|
44
|
+
end
|
45
|
+
return if body_class.included_modules.include?(Body)
|
46
|
+
body_class.__send__(:include, Body)
|
47
|
+
sf = IO.respond_to?(:copy_stream) || IO.method_defined?(:sendfile_nonblock)
|
48
|
+
if range_class
|
49
|
+
range_class.__send__(:include, sf ? Range : NoRange)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module NoRange
|
54
|
+
# dummy method if we can't send range responses
|
55
|
+
def make_range!(env, status, headers)
|
56
|
+
end
|
42
57
|
end
|
43
58
|
end
|
@@ -41,27 +41,28 @@ module Rainbows::Response::Body # :nodoc:
|
|
41
41
|
# try to take advantage of Rainbows::DevFdResponse, calling File.open
|
42
42
|
# is a last resort
|
43
43
|
path = body.to_path
|
44
|
-
path =~ %r{\A/dev/fd/(\d+)\z} ? IO.new($1.to_i) : File.open(path
|
44
|
+
path =~ %r{\A/dev/fd/(\d+)\z} ? IO.new($1.to_i) : File.open(path)
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
if IO.method_defined?(:sendfile_nonblock)
|
49
|
-
def write_body_file(sock, body)
|
50
|
-
sock.sendfile(body, 0)
|
49
|
+
def write_body_file(sock, body, range)
|
50
|
+
range ? sock.sendfile(body, range[0], range[1]) : sock.sendfile(body, 0)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
54
|
if IO.respond_to?(:copy_stream)
|
55
55
|
unless method_defined?(:write_body_file)
|
56
56
|
# try to use sendfile() via IO.copy_stream, otherwise pread()+write()
|
57
|
-
def write_body_file(sock, body)
|
58
|
-
IO.copy_stream(body, sock,
|
57
|
+
def write_body_file(sock, body, range)
|
58
|
+
range ? IO.copy_stream(body, sock, range[1], range[0]) :
|
59
|
+
IO.copy_stream(body, sock, nil, 0)
|
59
60
|
end
|
60
61
|
end
|
61
62
|
|
62
63
|
# only used when body is a pipe or socket that can't handle
|
63
64
|
# pread() semantics
|
64
|
-
def write_body_stream(sock, body)
|
65
|
+
def write_body_stream(sock, body, range)
|
65
66
|
IO.copy_stream(body, sock)
|
66
67
|
ensure
|
67
68
|
body.respond_to?(:close) and body.close
|
@@ -74,38 +75,40 @@ module Rainbows::Response::Body # :nodoc:
|
|
74
75
|
if method_defined?(:write_body_file)
|
75
76
|
|
76
77
|
# middlewares/apps may return with a body that responds to +to_path+
|
77
|
-
def write_body_path(sock, body)
|
78
|
+
def write_body_path(sock, body, range)
|
78
79
|
inp = body_to_io(body)
|
79
80
|
if inp.stat.file?
|
80
81
|
begin
|
81
|
-
write_body_file(sock, inp)
|
82
|
+
write_body_file(sock, inp, range)
|
82
83
|
ensure
|
83
84
|
inp.close if inp != body
|
84
85
|
end
|
85
86
|
else
|
86
|
-
write_body_stream(sock, inp)
|
87
|
+
write_body_stream(sock, inp, range)
|
87
88
|
end
|
88
89
|
ensure
|
89
90
|
body.respond_to?(:close) && inp != body and body.close
|
90
91
|
end
|
91
|
-
|
92
|
-
def write_body_path(sock, body)
|
93
|
-
write_body_stream(sock, body_to_io(body))
|
92
|
+
elsif method_defined?(:write_body_stream)
|
93
|
+
def write_body_path(sock, body, range)
|
94
|
+
write_body_stream(sock, inp = body_to_io(body), range)
|
95
|
+
ensure
|
96
|
+
body.respond_to?(:close) && inp != body and body.close
|
94
97
|
end
|
95
98
|
end
|
96
99
|
|
97
100
|
if method_defined?(:write_body_path)
|
98
|
-
def write_body(client, body)
|
101
|
+
def write_body(client, body, range)
|
99
102
|
body.respond_to?(:to_path) ?
|
100
|
-
write_body_path(client, body) :
|
101
|
-
write_body_each(client, body)
|
103
|
+
write_body_path(client, body, range) :
|
104
|
+
write_body_each(client, body, range)
|
102
105
|
end
|
103
106
|
else
|
104
107
|
ALIASES[:write_body] = :write_body_each
|
105
108
|
end
|
106
109
|
|
107
110
|
# generic body writer, used for most dynamically generated responses
|
108
|
-
def write_body_each(socket, body)
|
111
|
+
def write_body_each(socket, body, range = nil)
|
109
112
|
body.each { |chunk| socket.write(chunk) }
|
110
113
|
ensure
|
111
114
|
body.respond_to?(:close) and body.close
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
module Rainbows::Response::Range
|
4
|
+
HTTP_RANGE = 'HTTP_RANGE'
|
5
|
+
Content_Range = 'Content-Range'.freeze
|
6
|
+
Content_Length = 'Content-Length'.freeze
|
7
|
+
|
8
|
+
# This does not support multipart responses (does anybody actually
|
9
|
+
# use those?) +headers+ is always a Rack::Utils::HeaderHash
|
10
|
+
def make_range!(env, status, headers)
|
11
|
+
if 200 == status.to_i &&
|
12
|
+
(clen = headers[Content_Length]) &&
|
13
|
+
/\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ env[HTTP_RANGE]
|
14
|
+
a, b = $1.split(/-/)
|
15
|
+
clen = clen.to_i
|
16
|
+
if b.nil? # bytes=M-
|
17
|
+
offset = a.to_i
|
18
|
+
count = clen - offset
|
19
|
+
elsif a.empty? # bytes=-N
|
20
|
+
offset = clen - b.to_i
|
21
|
+
count = clen - offset
|
22
|
+
else # bytes=M-N
|
23
|
+
offset = a.to_i
|
24
|
+
count = b.to_i + 1 - offset
|
25
|
+
end
|
26
|
+
raise Rainbows::Response416 if count <= 0 || offset >= clen
|
27
|
+
count = clen if count > clen
|
28
|
+
headers[Content_Length] = count.to_s
|
29
|
+
headers[Content_Range] = "bytes #{offset}-#{offset+count-1}/#{clen}"
|
30
|
+
[ status, offset, count ]
|
31
|
+
end
|
32
|
+
# nil if no status
|
33
|
+
end
|
34
|
+
end
|
data/lib/rainbows/rev.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'rainbows/rev/core'
|
3
3
|
require 'rainbows/rev/client'
|
4
|
-
require 'rainbows/rev/deferred_response'
|
5
4
|
|
6
5
|
module Rainbows
|
7
6
|
|
@@ -37,6 +36,8 @@ module Rainbows
|
|
37
36
|
KATO.compare_by_identity
|
38
37
|
end
|
39
38
|
|
39
|
+
autoload :DeferredResponse,'rainbows/rev/deferred_response'
|
40
|
+
autoload :DeferredChunkResponse,'rainbows/rev/deferred_chunk_response'
|
40
41
|
include Core
|
41
42
|
# :startdoc:
|
42
43
|
end
|
data/lib/rainbows/rev/client.rb
CHANGED
@@ -7,20 +7,19 @@ module Rainbows
|
|
7
7
|
class Client < ::Rev::IO
|
8
8
|
include Rainbows::ByteSlice
|
9
9
|
include Rainbows::EvCore
|
10
|
-
include Rainbows::Response
|
11
10
|
G = Rainbows::G
|
12
|
-
|
11
|
+
F = Rainbows::StreamFile
|
13
12
|
|
14
13
|
def initialize(io)
|
15
14
|
CONN[self] = false
|
16
15
|
super(io)
|
17
16
|
post_init
|
18
|
-
@
|
17
|
+
@deferred = nil
|
19
18
|
end
|
20
19
|
|
21
20
|
def quit
|
22
21
|
super
|
23
|
-
close if @
|
22
|
+
close if @deferred.nil? && @_write_buffer.empty?
|
24
23
|
end
|
25
24
|
|
26
25
|
# override the ::Rev::IO#write method try to write directly to the
|
@@ -30,16 +29,14 @@ module Rainbows
|
|
30
29
|
if @_write_buffer.empty?
|
31
30
|
begin
|
32
31
|
w = @_io.write_nonblock(buf)
|
33
|
-
if w == Rack::Utils.bytesize(buf)
|
34
|
-
return on_write_complete
|
35
|
-
end
|
32
|
+
return enable_write_watcher if w == Rack::Utils.bytesize(buf)
|
36
33
|
# we never care for the return value, but yes, we may return
|
37
34
|
# a "fake" short write from super(buf) if anybody cares.
|
38
35
|
buf = byte_slice(buf, w..-1)
|
39
36
|
rescue Errno::EAGAIN
|
40
37
|
break # fall through to super(buf)
|
41
|
-
rescue
|
42
|
-
return
|
38
|
+
rescue => e
|
39
|
+
return handle_error(e)
|
43
40
|
end while true
|
44
41
|
end
|
45
42
|
super(buf)
|
@@ -49,98 +46,129 @@ module Rainbows
|
|
49
46
|
# devices where read(2) is uninterruptable. Unfortunately, NFS and ilk
|
50
47
|
# are also part of this. We'll also stick DeferredResponse bodies in
|
51
48
|
# here to prevent connections from being closed on us.
|
52
|
-
def defer_body(io
|
53
|
-
@
|
54
|
-
|
49
|
+
def defer_body(io)
|
50
|
+
@deferred = io
|
51
|
+
enable_write_watcher
|
55
52
|
end
|
56
53
|
|
57
|
-
|
58
|
-
|
54
|
+
# allows enabling of write watcher even when read watcher is disabled
|
55
|
+
def evloop
|
56
|
+
Rainbows::Rev::Server::LOOP
|
59
57
|
end
|
60
58
|
|
61
|
-
def
|
62
|
-
|
59
|
+
def next!
|
60
|
+
@deferred = nil
|
61
|
+
on_write_complete
|
62
|
+
end
|
63
63
|
|
64
|
-
|
65
|
-
|
64
|
+
def timeout?
|
65
|
+
@deferred.nil? && @_write_buffer.empty? and close.nil?
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
|
68
|
+
# used for streaming sockets and pipes
|
69
|
+
def stream_response(status, headers, io, body)
|
70
|
+
c = stream_response_headers(status, headers) if headers
|
71
|
+
# we only want to attach to the Rev::Loop belonging to the
|
72
|
+
# main thread in Ruby 1.9
|
73
|
+
io = (c ? DeferredChunkResponse : DeferredResponse).new(io, self, body)
|
74
|
+
defer_body(io.attach(Server::LOOP))
|
75
|
+
end
|
70
76
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
77
|
+
def rev_write_response(response, alive)
|
78
|
+
status, headers, body = response
|
79
|
+
headers = @hp.headers? ? HH.new(headers) : nil
|
80
|
+
|
81
|
+
headers[CONNECTION] = alive ? KEEP_ALIVE : CLOSE if headers
|
82
|
+
if body.respond_to?(:to_path)
|
83
|
+
io = body_to_io(body)
|
84
|
+
st = io.stat
|
85
|
+
|
86
|
+
if st.file?
|
87
|
+
offset, count = 0, st.size
|
88
|
+
if headers
|
89
|
+
if range = make_range!(@env, status, headers)
|
90
|
+
status, offset, count = range
|
91
|
+
end
|
92
|
+
write(response_header(status, headers))
|
93
|
+
end
|
94
|
+
return defer_body(F.new(offset, count, io, body))
|
95
|
+
elsif st.socket? || st.pipe?
|
96
|
+
return stream_response(status, headers, io, body)
|
80
97
|
end
|
81
|
-
|
82
|
-
# we only want to attach to the Rev::Loop belonging to the
|
83
|
-
# main thread in Ruby 1.9
|
84
|
-
io = DeferredResponse.new(io, self, do_chunk, body).
|
85
|
-
attach(Server::LOOP)
|
86
|
-
elsif st.file?
|
87
|
-
headers.delete('Transfer-Encoding')
|
88
|
-
headers['Content-Length'] ||= st.size.to_s
|
89
|
-
io = to_sendfile(io)
|
90
|
-
else # char/block device, directory, whatever... nobody cares
|
91
|
-
return write_response(self, response, out)
|
98
|
+
# char or block device... WTF? fall through to body.each
|
92
99
|
end
|
93
|
-
|
94
|
-
|
100
|
+
write(response_header(status, headers)) if headers
|
101
|
+
write_body_each(self, body, nil)
|
95
102
|
end
|
96
103
|
|
97
104
|
def app_call
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
105
|
+
KATO.delete(self)
|
106
|
+
@env[RACK_INPUT] = @input
|
107
|
+
@env[REMOTE_ADDR] = @remote_addr
|
108
|
+
response = APP.call(@env.update(RACK_DEFAULTS))
|
109
|
+
|
110
|
+
rev_write_response(response, alive = @hp.keepalive? && G.alive)
|
111
|
+
return quit unless alive && :close != @state
|
112
|
+
@env.clear
|
113
|
+
@hp.reset
|
114
|
+
@state = :headers
|
115
|
+
disable if enabled?
|
116
|
+
end
|
117
|
+
|
118
|
+
def on_write_complete
|
119
|
+
case @deferred
|
120
|
+
when DeferredResponse then return
|
121
|
+
when NilClass # fall through
|
122
|
+
else
|
123
|
+
begin
|
124
|
+
return rev_sendfile(@deferred)
|
125
|
+
rescue EOFError # expected at file EOF
|
126
|
+
close_deferred
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
case @state
|
131
|
+
when :close
|
132
|
+
close if @_write_buffer.empty?
|
133
|
+
when :headers
|
134
|
+
if @hp.headers(@env, @buf)
|
135
|
+
app_call
|
114
136
|
else
|
115
|
-
|
137
|
+
unless enabled?
|
138
|
+
enable
|
139
|
+
KATO[self] = Time.now
|
140
|
+
end
|
116
141
|
end
|
117
|
-
|
118
|
-
|
142
|
+
end
|
143
|
+
rescue => e
|
144
|
+
handle_error(e)
|
119
145
|
end
|
120
146
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
147
|
+
def handle_error(e)
|
148
|
+
close_deferred
|
149
|
+
if msg = Error.response(e)
|
150
|
+
@_io.write_nonblock(msg) rescue nil
|
151
|
+
end
|
152
|
+
@_write_buffer.clear
|
153
|
+
ensure
|
154
|
+
quit
|
155
|
+
end
|
125
156
|
|
157
|
+
def close_deferred
|
158
|
+
case @deferred
|
159
|
+
when DeferredResponse, NilClass
|
160
|
+
else
|
126
161
|
begin
|
127
|
-
|
128
|
-
rescue EOFError # expected at file EOF
|
129
|
-
@deferred_bodies.shift
|
130
|
-
body.close
|
131
|
-
close if :close == @state && @deferred_bodies.empty?
|
162
|
+
@deferred.close
|
132
163
|
rescue => e
|
133
|
-
|
164
|
+
G.server.logger.error("closing #@deferred: #{e}")
|
134
165
|
end
|
135
|
-
|
136
|
-
close if :close == @state
|
166
|
+
@deferred = nil
|
137
167
|
end
|
138
168
|
end
|
139
169
|
|
140
170
|
def on_close
|
141
|
-
|
142
|
-
DeferredResponse === f or f.close
|
143
|
-
end
|
171
|
+
close_deferred
|
144
172
|
CONN.delete(self)
|
145
173
|
end
|
146
174
|
|