rainbows 4.7.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitattributes +5 -0
- data/GIT-VERSION-GEN +1 -1
- data/README +5 -9
- data/bin/rainbows +3 -3
- data/lib/rainbows.rb +18 -6
- data/lib/rainbows/configurator.rb +8 -8
- data/lib/rainbows/const.rb +0 -3
- data/lib/rainbows/coolio.rb +2 -7
- data/lib/rainbows/coolio/client.rb +6 -6
- data/lib/rainbows/coolio/heartbeat.rb +2 -2
- data/lib/rainbows/coolio/thread_client.rb +3 -3
- data/lib/rainbows/dev_fd_response.rb +8 -14
- data/lib/rainbows/epoll/client.rb +9 -10
- data/lib/rainbows/error.rb +2 -2
- data/lib/rainbows/ev_core.rb +11 -17
- data/lib/rainbows/event_machine/client.rb +7 -7
- data/lib/rainbows/event_machine/try_defer.rb +1 -4
- data/lib/rainbows/fiber.rb +1 -1
- data/lib/rainbows/fiber/base.rb +3 -3
- data/lib/rainbows/fiber/coolio/heartbeat.rb +1 -1
- data/lib/rainbows/fiber/io.rb +1 -1
- data/lib/rainbows/http_parser.rb +24 -0
- data/lib/rainbows/http_server.rb +5 -4
- data/lib/rainbows/join_threads.rb +2 -2
- data/lib/rainbows/max_body.rb +4 -9
- data/lib/rainbows/process_client.rb +11 -12
- data/lib/rainbows/response.rb +20 -37
- data/lib/rainbows/revactor.rb +0 -1
- data/lib/rainbows/revactor/client.rb +2 -3
- data/lib/rainbows/revactor/proxy.rb +1 -1
- data/lib/rainbows/reverse_proxy.rb +9 -19
- data/lib/rainbows/reverse_proxy/coolio.rb +3 -3
- data/lib/rainbows/reverse_proxy/ev_client.rb +2 -5
- data/lib/rainbows/reverse_proxy/event_machine.rb +1 -1
- data/lib/rainbows/sendfile.rb +3 -9
- data/lib/rainbows/server_token.rb +1 -6
- data/lib/rainbows/stream_response_epoll.rb +8 -9
- data/lib/rainbows/thread_timeout.rb +4 -4
- data/lib/rainbows/writer_thread_pool.rb +2 -2
- data/lib/rainbows/xepoll_thread_pool/client.rb +3 -4
- data/lib/rainbows/xepoll_thread_spawn/client.rb +3 -4
- data/rainbows.gemspec +1 -1
- data/t/t0105-rack-input-limit-bigger.sh +10 -2
- data/t/test_isolate.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b971749285f91c7a3a1b8c6340e8e349ee3d0468
|
4
|
+
data.tar.gz: a8e7bf3756f75ceafc3c4bd9333f6885079055ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5e3e727d58a0916e748c1029aeb8433623406a4e8816e24db77452bde792974f066558322a7d38b1360aaa63f5451dbf6eb4f5ef7b9ef5667e45ccd2619ff92
|
7
|
+
data.tar.gz: 7ff15b6b42d53fc040a4bd1a46382dbfbcd6ccefacf12fc266bed97f10e6b49d2f0947f64db9db8b54103710273f5dfbd576b0a505afd8ad3430e8e82eb28f7f
|
data/.gitattributes
ADDED
data/GIT-VERSION-GEN
CHANGED
data/README
CHANGED
@@ -6,18 +6,13 @@ request/response times and/or slow clients.
|
|
6
6
|
|
7
7
|
If you're on GNU/Linux and overwhelmed by options in \Rainbows!,
|
8
8
|
consider {yahns}[http://yahns.yhbt.net/] as it has fewer options
|
9
|
-
and more energy-efficient during non-peak traffic
|
9
|
+
and more energy-efficient during non-peak traffic and may also
|
10
|
+
be configured as a single worker process.
|
10
11
|
|
11
12
|
For Rack applications not heavily bound by slow external network
|
12
13
|
dependencies, consider unicorn instead as it simpler and easier to
|
13
14
|
debug.
|
14
15
|
|
15
|
-
If you're on a small system, or write extremely tight and reliable code
|
16
|
-
and don't want multiple worker processes, check out
|
17
|
-
{Zbatery}[http://zbatery.bogomip.org/], too. Zbatery can use all the
|
18
|
-
crazy network concurrency options of \Rainbows! in a single worker
|
19
|
-
process.
|
20
|
-
|
21
16
|
== \Rainbows! is about Diversity
|
22
17
|
|
23
18
|
We aim to support as many concurrency models as we can because they all
|
@@ -169,9 +164,10 @@ and we'll try our best to fix it.
|
|
169
164
|
All feedback (bug reports, user/development discussion, patches, pull
|
170
165
|
requests) go to the mailing list. Patches must be sent inline
|
171
166
|
(git format-patch -M + git send-email). No subscription is necessary
|
172
|
-
to post on the mailing list. No top posting.
|
173
|
-
the mailing list.
|
167
|
+
to post on the mailing list. No top posting.
|
174
168
|
|
175
169
|
* email: mailto:rainbows-public@bogomips.org
|
176
170
|
* subscribe: mailto:rainbows-public+subscribe@bogomips.org
|
177
171
|
* archives: http://bogomips.org/rainbows-public/
|
172
|
+
nntp://news.public-inbox.org/inbox.comp.lang.ruby.rainbows
|
173
|
+
nntp://news.gmane.org/gmane.comp.lang.ruby.rainbows.general
|
data/bin/rainbows
CHANGED
data/lib/rainbows.rb
CHANGED
@@ -11,8 +11,7 @@ module Rainbows
|
|
11
11
|
|
12
12
|
# map of numeric file descriptors to IO objects to avoid using IO.new
|
13
13
|
# and potentially causing race conditions when using /dev/fd/
|
14
|
-
FD_MAP = {}
|
15
|
-
FD_MAP.compare_by_identity if FD_MAP.respond_to?(:compare_by_identity)
|
14
|
+
FD_MAP = {}.compare_by_identity
|
16
15
|
|
17
16
|
require 'rainbows/const'
|
18
17
|
require 'rainbows/http_parser'
|
@@ -75,8 +74,8 @@ def self.at_quit(&block)
|
|
75
74
|
end
|
76
75
|
|
77
76
|
def self.tick
|
78
|
-
@worker.tick =
|
79
|
-
exit!(2) if @expire &&
|
77
|
+
@worker.tick = now.to_i
|
78
|
+
exit!(2) if @expire && now >= @expire
|
80
79
|
@alive && @server.master_pid == Process.ppid or quit!
|
81
80
|
end
|
82
81
|
|
@@ -88,11 +87,11 @@ def self.quit!
|
|
88
87
|
unless @expire
|
89
88
|
@alive = false
|
90
89
|
Rainbows::HttpParser.quit
|
91
|
-
@expire =
|
90
|
+
@expire = now + (@server.timeout * 2.0)
|
92
91
|
tmp = @readers.dup
|
93
92
|
@readers.clear
|
94
93
|
tmp.each { |s| s.close rescue nil }.clear
|
95
|
-
@at_quit.each
|
94
|
+
@at_quit.each(&:call)
|
96
95
|
|
97
96
|
# XXX hack to break out of IO.select in worker_loop for some models
|
98
97
|
Process.kill(:QUIT, $$)
|
@@ -100,6 +99,19 @@ def self.quit!
|
|
100
99
|
false
|
101
100
|
end
|
102
101
|
|
102
|
+
# try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
|
103
|
+
# offset adjustments and generates less garbage (Float vs Time object)
|
104
|
+
begin
|
105
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
106
|
+
def self.now
|
107
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
108
|
+
end
|
109
|
+
rescue NameError, NoMethodError
|
110
|
+
def self.now # Ruby <= 2.0
|
111
|
+
Time.now.to_f
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
103
115
|
autoload :Base, "rainbows/base"
|
104
116
|
autoload :WriterThreadPool, "rainbows/writer_thread_pool"
|
105
117
|
autoload :WriterThreadSpawn, "rainbows/writer_thread_spawn"
|
@@ -21,14 +21,14 @@
|
|
21
21
|
# stdout_path "/path/to/output.log"
|
22
22
|
module Rainbows::Configurator
|
23
23
|
Unicorn::Configurator::DEFAULTS.merge!({
|
24
|
-
:
|
25
|
-
:
|
26
|
-
:
|
27
|
-
:
|
28
|
-
:
|
29
|
-
:
|
30
|
-
:
|
31
|
-
|
24
|
+
use: Rainbows::Base,
|
25
|
+
worker_connections: 50,
|
26
|
+
keepalive_timeout: 5,
|
27
|
+
keepalive_requests: 100,
|
28
|
+
client_max_body_size: 1024 * 1024,
|
29
|
+
client_header_buffer_size: 1024,
|
30
|
+
client_max_header_size: 112 * 1024,
|
31
|
+
copy_stream: IO,
|
32
32
|
})
|
33
33
|
|
34
34
|
# Configures \Rainbows! with a given concurrency model to +use+ and
|
data/lib/rainbows/const.rb
CHANGED
data/lib/rainbows/coolio.rb
CHANGED
@@ -27,15 +27,10 @@
|
|
27
27
|
module Rainbows::Coolio
|
28
28
|
# :stopdoc:
|
29
29
|
# keep-alive timeout scoreboard
|
30
|
-
KATO = {}
|
30
|
+
KATO = {}.compare_by_identity
|
31
31
|
|
32
32
|
# all connected clients
|
33
|
-
CONN = {}
|
34
|
-
|
35
|
-
if {}.respond_to?(:compare_by_identity)
|
36
|
-
CONN.compare_by_identity
|
37
|
-
KATO.compare_by_identity
|
38
|
-
end
|
33
|
+
CONN = {}.compare_by_identity
|
39
34
|
|
40
35
|
autoload :Client, 'rainbows/coolio/client'
|
41
36
|
autoload :Master, 'rainbows/coolio/master'
|
@@ -122,10 +122,10 @@ def ev_write_response(status, headers, body, alive)
|
|
122
122
|
def app_call input
|
123
123
|
KATO.delete(self)
|
124
124
|
disable if enabled?
|
125
|
-
@env[
|
126
|
-
@env[REMOTE_ADDR] = @_io.kgio_addr
|
127
|
-
@env[
|
128
|
-
@hp.hijack_setup(@
|
125
|
+
@env['rack.input'] = input
|
126
|
+
@env['REMOTE_ADDR'] = @_io.kgio_addr
|
127
|
+
@env['async.callback'] = method(:write_async_response)
|
128
|
+
@hp.hijack_setup(@_io)
|
129
129
|
status, headers, body = catch(:async) {
|
130
130
|
APP.call(@env.merge!(RACK_DEFAULTS))
|
131
131
|
}
|
@@ -154,10 +154,10 @@ def on_write_complete
|
|
154
154
|
# buf == :wait_readable
|
155
155
|
unless enabled?
|
156
156
|
enable
|
157
|
-
KATO[self] =
|
157
|
+
KATO[self] = Rainbows.now
|
158
158
|
end
|
159
159
|
else
|
160
|
-
on_read(
|
160
|
+
on_read(''.freeze)
|
161
161
|
end
|
162
162
|
end
|
163
163
|
rescue => e
|
@@ -9,11 +9,11 @@ class Rainbows::Coolio::Heartbeat < Coolio::TimerWatcher
|
|
9
9
|
KATO = Rainbows::Coolio::KATO
|
10
10
|
CONN = Rainbows::Coolio::CONN
|
11
11
|
Rainbows.config!(self, :keepalive_timeout)
|
12
|
-
Rainbows.at_quit { KATO.each_key
|
12
|
+
Rainbows.at_quit { KATO.each_key(&:timeout?).clear }
|
13
13
|
|
14
14
|
def on_timer
|
15
15
|
if (ot = KEEPALIVE_TIMEOUT) >= 0
|
16
|
-
ot =
|
16
|
+
ot = Rainbows.now - ot
|
17
17
|
KATO.delete_if { |client, time| time < ot and client.timeout? }
|
18
18
|
end
|
19
19
|
exit if (! Rainbows.tick && CONN.size <= 0)
|
@@ -8,7 +8,7 @@ class Rainbows::Coolio::ThreadClient < Rainbows::Coolio::Client
|
|
8
8
|
def app_call input
|
9
9
|
KATO.delete(self)
|
10
10
|
disable if enabled?
|
11
|
-
@env[
|
11
|
+
@env['rack.input'] = input
|
12
12
|
app_dispatch # must be implemented by subclass
|
13
13
|
end
|
14
14
|
|
@@ -25,8 +25,8 @@ def response_write(response)
|
|
25
25
|
# here because that could cause a deadlock and we'd leak FDs
|
26
26
|
def app_response
|
27
27
|
begin
|
28
|
-
@env[REMOTE_ADDR] = @_io.kgio_addr
|
29
|
-
@hp.hijack_setup(@
|
28
|
+
@env['REMOTE_ADDR'] = @_io.kgio_addr
|
29
|
+
@hp.hijack_setup(@_io)
|
30
30
|
APP.call(@env.merge!(RACK_DEFAULTS))
|
31
31
|
rescue => e
|
32
32
|
Rainbows::Error.app(e) # we guarantee this does not raise
|
@@ -11,12 +11,6 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
11
11
|
|
12
12
|
# :stopdoc:
|
13
13
|
FD_MAP = Rainbows::FD_MAP
|
14
|
-
Content_Length = "Content-Length".freeze
|
15
|
-
Transfer_Encoding = "Transfer-Encoding".freeze
|
16
|
-
Rainbows_autochunk = "rainbows.autochunk".freeze
|
17
|
-
Rainbows_model = "rainbows.model"
|
18
|
-
HTTP_VERSION = "HTTP_VERSION"
|
19
|
-
Chunked = "chunked"
|
20
14
|
include Rack::Utils
|
21
15
|
|
22
16
|
# Rack middleware entry point, we'll just pass through responses
|
@@ -40,23 +34,23 @@ def call(env)
|
|
40
34
|
fileno = io.fileno
|
41
35
|
FD_MAP[fileno] = io
|
42
36
|
if st.file?
|
43
|
-
headers[
|
44
|
-
headers.delete(
|
37
|
+
headers['Content-Length'.freeze] ||= st.size.to_s
|
38
|
+
headers.delete('Transfer-Encoding'.freeze)
|
45
39
|
elsif st.pipe? || st.socket? # epoll-able things
|
46
|
-
unless headers.include?(
|
47
|
-
if env[
|
48
|
-
case env[HTTP_VERSION]
|
40
|
+
unless headers.include?('Content-Length'.freeze)
|
41
|
+
if env['rainbows.autochunk']
|
42
|
+
case env['HTTP_VERSION']
|
49
43
|
when "HTTP/1.0", nil
|
50
44
|
else
|
51
|
-
headers[
|
45
|
+
headers['Transfer-Encoding'.freeze] = 'chunked'
|
52
46
|
end
|
53
47
|
else
|
54
|
-
env[
|
48
|
+
env['rainbows.autochunk'] = false
|
55
49
|
end
|
56
50
|
end
|
57
51
|
|
58
52
|
# we need to make sure our pipe output is Fiber-compatible
|
59
|
-
case env[
|
53
|
+
case env['rainbows.model']
|
60
54
|
when :FiberSpawn, :FiberPool, :RevFiberSpawn, :CoolioFiberSpawn
|
61
55
|
io.respond_to?(:kgio_wait_readable) or
|
62
56
|
io = Rainbows::Fiber::IO.new(io)
|
@@ -9,15 +9,14 @@ module Rainbows::Epoll::Client
|
|
9
9
|
IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ONESHOT
|
10
10
|
OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ONESHOT
|
11
11
|
EPINOUT = IN | OUT
|
12
|
-
KATO = {}
|
13
|
-
|
14
|
-
Rainbows.at_quit { KATO.each_key { |k| k.timeout! }.clear }
|
12
|
+
KATO = {}.compare_by_identity
|
13
|
+
Rainbows.at_quit { KATO.each_key(&:timeout!).clear }
|
15
14
|
Rainbows.config!(self, :keepalive_timeout)
|
16
15
|
EP = Rainbows::EP
|
17
|
-
@@last_expire =
|
16
|
+
@@last_expire = Rainbows.now
|
18
17
|
|
19
18
|
def self.expire
|
20
|
-
return if ((now =
|
19
|
+
return if ((now = Rainbows.now) - @@last_expire) < 1.0
|
21
20
|
if (ot = KEEPALIVE_TIMEOUT) >= 0
|
22
21
|
ot = now - ot
|
23
22
|
KATO.delete_if { |client, time| time < ot and client.timeout! }
|
@@ -63,9 +62,9 @@ def on_readable
|
|
63
62
|
end
|
64
63
|
|
65
64
|
def app_call input # called by on_read()
|
66
|
-
@env[
|
67
|
-
@env[REMOTE_ADDR] = kgio_addr
|
68
|
-
@hp.hijack_setup(
|
65
|
+
@env['rack.input'] = input
|
66
|
+
@env['REMOTE_ADDR'] = kgio_addr
|
67
|
+
@hp.hijack_setup(self)
|
69
68
|
status, headers, body = APP.call(@env.merge!(RACK_DEFAULTS))
|
70
69
|
return hijacked if @hp.hijacked?
|
71
70
|
ev_write_response(status, headers, body, @hp.next?)
|
@@ -93,7 +92,7 @@ def stream_response_body(body, io, chunk)
|
|
93
92
|
Rainbows::Epoll::ResponsePipe).new(io, self, body)
|
94
93
|
return @wr_queue << pipe if @wr_queue[0]
|
95
94
|
stream_pipe(pipe) or return
|
96
|
-
@wr_queue[0] or @wr_queue <<
|
95
|
+
@wr_queue[0] or @wr_queue << ''.freeze
|
97
96
|
end
|
98
97
|
|
99
98
|
def ev_write_response(status, headers, body, alive)
|
@@ -120,7 +119,7 @@ def next_request
|
|
120
119
|
want_more
|
121
120
|
else
|
122
121
|
# pipelined request (already in buffer)
|
123
|
-
on_read(
|
122
|
+
on_read(''.freeze)
|
124
123
|
return if @wr_queue[0] || closed?
|
125
124
|
return hijacked if @hp.hijacked?
|
126
125
|
close if :close == @state
|
data/lib/rainbows/error.rb
CHANGED
@@ -28,11 +28,11 @@ def self.response(e)
|
|
28
28
|
Errno::EBADF, Errno::ENOTCONN, Errno::ETIMEDOUT, Errno::EHOSTUNREACH
|
29
29
|
# swallow error if client shuts down one end or disconnects
|
30
30
|
when Unicorn::HttpParserError
|
31
|
-
|
31
|
+
"HTTP/1.1 400 Bad Request\r\n\r\n" # try to tell the client they're bad
|
32
32
|
when IOError # HttpParserError is an IOError
|
33
33
|
else
|
34
34
|
app(e)
|
35
|
-
|
35
|
+
"HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
36
36
|
end
|
37
37
|
end
|
38
38
|
end
|
data/lib/rainbows/ev_core.rb
CHANGED
@@ -8,13 +8,9 @@ module Rainbows::EvCore
|
|
8
8
|
HttpParser = Rainbows::HttpParser
|
9
9
|
autoload :CapInput, 'rainbows/ev_core/cap_input'
|
10
10
|
RBUF = ""
|
11
|
-
Z = "".freeze
|
12
11
|
Rainbows.config!(self, :client_header_buffer_size)
|
13
|
-
HTTP_VERSION = "HTTP_VERSION"
|
14
12
|
|
15
13
|
# Apps may return this Rack response: AsyncResponse = [ -1, {}, [] ]
|
16
|
-
ASYNC_CALLBACK = "async.callback".freeze
|
17
|
-
ASYNC_CLOSE = "async.close".freeze
|
18
14
|
|
19
15
|
def write_async_response(response)
|
20
16
|
status, headers, body = response
|
@@ -23,8 +19,8 @@ def write_async_response(response)
|
|
23
19
|
# "Transfer-Encoding: chunked", and the async.callback stuff
|
24
20
|
# isn't Rack::Lint-compatible, so we have to enforce it here.
|
25
21
|
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
26
|
-
alive = headers.include?(
|
27
|
-
!!(%r{\Achunked\z}i =~ headers[
|
22
|
+
alive = headers.include?('Content-Length'.freeze) ||
|
23
|
+
!!(%r{\Achunked\z}i =~ headers['Transfer-Encoding'.freeze])
|
28
24
|
end
|
29
25
|
@deferred = nil
|
30
26
|
ev_write_response(status, headers, body, alive)
|
@@ -55,12 +51,12 @@ def handle_error(e)
|
|
55
51
|
# returns nil if request was hijacked in response stage
|
56
52
|
def stream_response_headers(status, headers, alive, body)
|
57
53
|
headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
|
58
|
-
if headers.include?(
|
54
|
+
if headers.include?('Content-Length'.freeze)
|
59
55
|
write_headers(status, headers, alive, body) or return
|
60
56
|
return false
|
61
57
|
end
|
62
58
|
|
63
|
-
case @env[HTTP_VERSION]
|
59
|
+
case @env['HTTP_VERSION']
|
64
60
|
when "HTTP/1.0" # disable HTTP/1.0 keepalive to stream
|
65
61
|
write_headers(status, headers, false, body) or return
|
66
62
|
@hp.clear
|
@@ -68,7 +64,7 @@ def stream_response_headers(status, headers, alive, body)
|
|
68
64
|
when nil # "HTTP/0.9"
|
69
65
|
false
|
70
66
|
else
|
71
|
-
rv = !!(headers[
|
67
|
+
rv = !!(headers['Transfer-Encoding'] =~ %r{\Achunked\z}i)
|
72
68
|
rv = false unless @env["rainbows.autochunk"]
|
73
69
|
write_headers(status, headers, alive, body) or return
|
74
70
|
rv
|
@@ -78,14 +74,14 @@ def stream_response_headers(status, headers, alive, body)
|
|
78
74
|
def prepare_request_body
|
79
75
|
# since we don't do streaming input, we have no choice but
|
80
76
|
# to take over 100-continue handling from the Rack application
|
81
|
-
if @env[HTTP_EXPECT] =~ /\A100-continue\z/i
|
82
|
-
write(
|
83
|
-
@env.delete(HTTP_EXPECT)
|
77
|
+
if @env['HTTP_EXPECT'] =~ /\A100-continue\z/i
|
78
|
+
write("HTTP/1.1 100 Continue\r\n\r\n".freeze)
|
79
|
+
@env.delete('HTTP_EXPECT'.freeze)
|
84
80
|
end
|
85
81
|
@input = mkinput
|
86
82
|
@hp.filter_body(@buf2 = "", @buf)
|
87
83
|
@input << @buf2
|
88
|
-
on_read(
|
84
|
+
on_read(''.freeze)
|
89
85
|
end
|
90
86
|
|
91
87
|
# TeeInput doesn't map too well to this right now...
|
@@ -111,7 +107,7 @@ def on_read(data)
|
|
111
107
|
elsif data.size > 0
|
112
108
|
@hp.filter_body(@buf2, @buf << data)
|
113
109
|
@input << @buf2
|
114
|
-
on_read(
|
110
|
+
on_read(''.freeze)
|
115
111
|
else
|
116
112
|
want_more
|
117
113
|
end
|
@@ -127,10 +123,8 @@ def on_read(data)
|
|
127
123
|
handle_error(e)
|
128
124
|
end
|
129
125
|
|
130
|
-
ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
|
131
|
-
|
132
126
|
def err_413(msg)
|
133
|
-
write(
|
127
|
+
write("HTTP/1.1 413 Request Entity Too Large\r\n\r\n".freeze)
|
134
128
|
quit
|
135
129
|
# zip back up the stack
|
136
130
|
raise IOError, msg, []
|
@@ -23,7 +23,7 @@ def receive_data(data)
|
|
23
23
|
end
|
24
24
|
EM.next_tick { receive_data(nil) } unless @buf.empty?
|
25
25
|
else
|
26
|
-
on_read(data ||
|
26
|
+
on_read(data || ''.freeze) if (@buf.size > 0) || data
|
27
27
|
end
|
28
28
|
end
|
29
29
|
|
@@ -34,11 +34,11 @@ def quit
|
|
34
34
|
|
35
35
|
def app_call input
|
36
36
|
set_comm_inactivity_timeout 0
|
37
|
-
@env[
|
38
|
-
@env[REMOTE_ADDR] = @_io.kgio_addr
|
39
|
-
@env[
|
40
|
-
@env[
|
41
|
-
@hp.hijack_setup(@
|
37
|
+
@env['rack.input'] = input
|
38
|
+
@env['REMOTE_ADDR'] = @_io.kgio_addr
|
39
|
+
@env['async.callback'] = method(:write_async_response)
|
40
|
+
@env['async.close'] = EM::DefaultDeferrable.new
|
41
|
+
@hp.hijack_setup(@_io)
|
42
42
|
status, headers, body = catch(:async) {
|
43
43
|
APP.call(@env.merge!(RACK_DEFAULTS))
|
44
44
|
}
|
@@ -117,7 +117,7 @@ def next!
|
|
117
117
|
|
118
118
|
def unbind
|
119
119
|
return if @hp.hijacked?
|
120
|
-
async_close = @env[
|
120
|
+
async_close = @env['async.close'] and async_close.succeed
|
121
121
|
@deferred.respond_to?(:fail) and @deferred.fail
|
122
122
|
begin
|
123
123
|
@_io.close
|