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
@@ -2,12 +2,12 @@
|
|
2
2
|
require 'rainbows/fiber/coolio'
|
3
3
|
|
4
4
|
# A combination of the Coolio and FiberSpawn models. This allows Ruby
|
5
|
-
# 1.9 Fiber-based concurrency for application processing while
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
5
|
+
# 1.9 Fiber-based concurrency for application processing while exposing
|
6
|
+
# a synchronous execution model and using scalable network concurrency
|
7
|
+
# provided by Cool.io. A streaming "rack.input" is exposed.
|
8
|
+
# Applications are strongly advised to wrap all slow IO objects
|
9
|
+
# (sockets, pipes) using the Rainbows::Fiber::IO or a Cool.io-compatible
|
10
|
+
# class whenever possible.
|
11
11
|
module Rainbows::CoolioFiberSpawn
|
12
12
|
|
13
13
|
include Rainbows::Base
|
@@ -15,6 +15,13 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
15
15
|
|
16
16
|
# :stopdoc:
|
17
17
|
FD_MAP = Rainbows::FD_MAP
|
18
|
+
Content_Length = "Content-Length".freeze
|
19
|
+
Transfer_Encoding = "Transfer-Encoding".freeze
|
20
|
+
Rainbows_autochunk = "rainbows.autochunk".freeze
|
21
|
+
Rainbows_model = "rainbows.model"
|
22
|
+
HTTP_1_0 = "HTTP/1.0"
|
23
|
+
HTTP_VERSION = "HTTP_VERSION"
|
24
|
+
Chunked = "chunked"
|
18
25
|
|
19
26
|
# make this a no-op under Rubinius, it's pointless anyways
|
20
27
|
# since Rubinius doesn't have IO.copy_stream
|
@@ -44,19 +51,19 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
44
51
|
fileno = io.fileno
|
45
52
|
FD_MAP[fileno] = io
|
46
53
|
if st.file?
|
47
|
-
headers[
|
48
|
-
headers.delete(
|
54
|
+
headers[Content_Length] ||= st.size.to_s
|
55
|
+
headers.delete(Transfer_Encoding)
|
49
56
|
elsif st.pipe? || st.socket? # epoll-able things
|
50
|
-
unless headers.include?(
|
51
|
-
if env[
|
52
|
-
headers[
|
57
|
+
unless headers.include?(Content_Length)
|
58
|
+
if env[Rainbows_autochunk] && HTTP_1_0 != env[HTTP_VERSION]
|
59
|
+
headers[Transfer_Encoding] = Chunked
|
53
60
|
else
|
54
|
-
|
61
|
+
env[Rainbows_autochunk] = false
|
55
62
|
end
|
56
63
|
end
|
57
64
|
|
58
65
|
# we need to make sure our pipe output is Fiber-compatible
|
59
|
-
case env[
|
66
|
+
case env[Rainbows_model]
|
60
67
|
when :FiberSpawn, :FiberPool, :RevFiberSpawn, :CoolioFiberSpawn
|
61
68
|
io.respond_to?(:kgio_wait_readable) or
|
62
69
|
io = Rainbows::Fiber::IO.new(io)
|
@@ -72,8 +79,8 @@ class Rainbows::DevFdResponse < Struct.new(:app)
|
|
72
79
|
class Body < Struct.new(:to_io, :to_path, :orig_body)
|
73
80
|
# called by the webserver or other middlewares if they can't
|
74
81
|
# handle #to_path
|
75
|
-
def each
|
76
|
-
to_io.each
|
82
|
+
def each
|
83
|
+
to_io.each { |x| yield x }
|
77
84
|
end
|
78
85
|
|
79
86
|
# remain Rack::Lint-compatible for people with wonky systems :P
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
require 'sleepy_penguin'
|
4
|
+
require 'sendfile'
|
5
|
+
|
6
|
+
# Edge-triggered epoll concurrency model. This is extremely unfair
|
7
|
+
# and optimized for throughput at the expense of fairness
|
8
|
+
module Rainbows::Epoll
|
9
|
+
include Rainbows::Base
|
10
|
+
ReRun = []
|
11
|
+
autoload :Server, 'rainbows/epoll/server'
|
12
|
+
autoload :Client, 'rainbows/epoll/client'
|
13
|
+
autoload :ResponsePipe, 'rainbows/epoll/response_pipe'
|
14
|
+
autoload :ResponseChunkPipe, 'rainbows/epoll/response_chunk_pipe'
|
15
|
+
class << self
|
16
|
+
attr_writer :nr_clients
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.loop
|
20
|
+
timeout = Rainbows.server.timeout
|
21
|
+
begin
|
22
|
+
EP.wait(nil, timeout) { |flags, obj| obj.epoll_run }
|
23
|
+
while obj = ReRun.shift
|
24
|
+
obj.epoll_run
|
25
|
+
end
|
26
|
+
Rainbows::Epoll::Client.expire
|
27
|
+
rescue Errno::EINTR
|
28
|
+
rescue => e
|
29
|
+
Rainbows::Error.listen_loop(e)
|
30
|
+
end while Rainbows.tick || @nr_clients.call > 0
|
31
|
+
end
|
32
|
+
|
33
|
+
def init_worker_process(worker)
|
34
|
+
super
|
35
|
+
Rainbows::Epoll.const_set :EP, SleepyPenguin::Epoll.new
|
36
|
+
Rainbows::Client.__send__ :include, Client
|
37
|
+
end
|
38
|
+
|
39
|
+
def worker_loop(worker) # :nodoc:
|
40
|
+
init_worker_process(worker)
|
41
|
+
Server.run
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
|
4
|
+
module Rainbows::Epoll::Client
|
5
|
+
|
6
|
+
include Rainbows::EvCore
|
7
|
+
APP = Rainbows.server.app
|
8
|
+
Server = Rainbows::Epoll::Server
|
9
|
+
IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
|
10
|
+
OUT = SleepyPenguin::Epoll::OUT | SleepyPenguin::Epoll::ET
|
11
|
+
KATO = {}
|
12
|
+
KATO.compare_by_identity if KATO.respond_to?(:compare_by_identity)
|
13
|
+
KEEPALIVE_TIMEOUT = Rainbows.keepalive_timeout
|
14
|
+
EP = Rainbows::Epoll::EP
|
15
|
+
@@last_expire = Time.now
|
16
|
+
|
17
|
+
def self.expire
|
18
|
+
return if ((now = Time.now) - @@last_expire) < 1.0
|
19
|
+
if (ot = KEEPALIVE_TIMEOUT) >= 0
|
20
|
+
ot = now - ot
|
21
|
+
KATO.delete_if { |client, time| time < ot and client.timeout! }
|
22
|
+
end
|
23
|
+
@@last_expire = now
|
24
|
+
end
|
25
|
+
|
26
|
+
# only call this once
|
27
|
+
def epoll_once
|
28
|
+
@wr_queue = [] # may contain String, ResponsePipe, and StreamFile objects
|
29
|
+
post_init
|
30
|
+
on_readable
|
31
|
+
rescue => e
|
32
|
+
handle_error(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_readable
|
36
|
+
case rv = kgio_tryread(16384, RBUF)
|
37
|
+
when String
|
38
|
+
on_read(rv)
|
39
|
+
return if @wr_queue[0] || closed?
|
40
|
+
when :wait_readable
|
41
|
+
KATO[self] = @@last_expire if :headers == @state
|
42
|
+
return EP.set(self, IN)
|
43
|
+
else
|
44
|
+
break
|
45
|
+
end until :close == @state
|
46
|
+
close unless closed?
|
47
|
+
rescue Errno::ECONNRESET
|
48
|
+
close
|
49
|
+
rescue IOError
|
50
|
+
end
|
51
|
+
|
52
|
+
def app_call input # called by on_read()
|
53
|
+
@env[RACK_INPUT] = input
|
54
|
+
@env[REMOTE_ADDR] = kgio_addr
|
55
|
+
status, headers, body = APP.call(@env.merge!(RACK_DEFAULTS))
|
56
|
+
ev_write_response(status, headers, body, @hp.next?)
|
57
|
+
end
|
58
|
+
|
59
|
+
def write_response_path(status, headers, body, alive)
|
60
|
+
io = body_to_io(body)
|
61
|
+
st = io.stat
|
62
|
+
|
63
|
+
if st.file?
|
64
|
+
defer_file(status, headers, body, alive, io, st)
|
65
|
+
elsif st.socket? || st.pipe?
|
66
|
+
chunk = stream_response_headers(status, headers, alive)
|
67
|
+
stream_response_body(body, io, chunk)
|
68
|
+
else
|
69
|
+
# char or block device... WTF?
|
70
|
+
write_response(status, headers, body, alive)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# used for streaming sockets and pipes
|
75
|
+
def stream_response_body(body, io, chunk)
|
76
|
+
pipe = (chunk ? Rainbows::Epoll::ResponseChunkPipe :
|
77
|
+
Rainbows::Epoll::ResponsePipe).new(io, self, body)
|
78
|
+
return @wr_queue << pipe if @wr_queue[0]
|
79
|
+
stream_pipe(pipe) or return
|
80
|
+
@wr_queue[0] or @wr_queue << Z
|
81
|
+
end
|
82
|
+
|
83
|
+
def ev_write_response(status, headers, body, alive)
|
84
|
+
if body.respond_to?(:to_path)
|
85
|
+
write_response_path(status, headers, body, alive)
|
86
|
+
else
|
87
|
+
write_response(status, headers, body, alive)
|
88
|
+
end
|
89
|
+
@state = alive ? :headers : :close
|
90
|
+
on_read(Z) if alive && 0 == @wr_queue.size && 0 != @buf.size
|
91
|
+
end
|
92
|
+
|
93
|
+
def epoll_run
|
94
|
+
if @wr_queue[0]
|
95
|
+
on_writable
|
96
|
+
else
|
97
|
+
KATO.delete self
|
98
|
+
on_readable
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def want_more
|
103
|
+
Rainbows::Epoll::ReRun << self
|
104
|
+
end
|
105
|
+
|
106
|
+
def on_deferred_write_complete
|
107
|
+
:close == @state and return close
|
108
|
+
0 == @buf.size ? on_readable : on_read(Z)
|
109
|
+
end
|
110
|
+
|
111
|
+
def handle_error(e)
|
112
|
+
msg = Rainbows::Error.response(e) and kgio_trywrite(msg) rescue nil
|
113
|
+
ensure
|
114
|
+
close
|
115
|
+
end
|
116
|
+
|
117
|
+
def write_deferred(obj)
|
118
|
+
Rainbows::StreamFile === obj ? stream_file(obj) : stream_pipe(obj)
|
119
|
+
end
|
120
|
+
|
121
|
+
# writes until our write buffer is empty or we block
|
122
|
+
# returns true if we're done writing everything
|
123
|
+
def on_writable
|
124
|
+
obj = @wr_queue.shift
|
125
|
+
|
126
|
+
case rv = String === obj ? kgio_trywrite(obj) : write_deferred(obj)
|
127
|
+
when nil
|
128
|
+
obj = @wr_queue.shift or return on_deferred_write_complete
|
129
|
+
when String
|
130
|
+
obj = rv # retry
|
131
|
+
when :wait_writable # Strings and StreamFiles only
|
132
|
+
@wr_queue.unshift(obj)
|
133
|
+
EP.set(self, OUT)
|
134
|
+
return
|
135
|
+
when :deferred
|
136
|
+
return
|
137
|
+
end while true
|
138
|
+
rescue => e
|
139
|
+
handle_error(e)
|
140
|
+
end
|
141
|
+
|
142
|
+
def write(buf)
|
143
|
+
unless @wr_queue[0]
|
144
|
+
case rv = kgio_trywrite(buf)
|
145
|
+
when nil
|
146
|
+
return # all written
|
147
|
+
when String
|
148
|
+
buf = rv # retry
|
149
|
+
when :wait_writable
|
150
|
+
@wr_queue << buf.dup # >3-word 1.9 strings are copy-on-write
|
151
|
+
return EP.set(self, OUT)
|
152
|
+
end while true
|
153
|
+
end
|
154
|
+
@wr_queue << buf.dup # >3-word 1.9 strings are copy-on-write
|
155
|
+
end
|
156
|
+
|
157
|
+
def close
|
158
|
+
@wr_queue.each { |x| x.respond_to?(:close) and x.close rescue nil }
|
159
|
+
super
|
160
|
+
on_close
|
161
|
+
end
|
162
|
+
|
163
|
+
def on_close
|
164
|
+
KATO.delete(self)
|
165
|
+
Server.decr
|
166
|
+
end
|
167
|
+
|
168
|
+
def timeout!
|
169
|
+
close
|
170
|
+
true
|
171
|
+
end
|
172
|
+
|
173
|
+
def defer_file(status, headers, body, alive, io, st)
|
174
|
+
if r = sendfile_range(status, headers)
|
175
|
+
status, headers, range = r
|
176
|
+
write_headers(status, headers, alive)
|
177
|
+
range and defer_file_stream(range[0], range[1], io, body)
|
178
|
+
else
|
179
|
+
write_headers(status, headers, alive)
|
180
|
+
defer_file_stream(0, st.size, io, body)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# returns +nil+ on EOF, :wait_writable if the client blocks
|
185
|
+
def stream_file(sf) # +sf+ is a Rainbows::StreamFile object
|
186
|
+
begin
|
187
|
+
sf.offset += (n = sendfile_nonblock(sf, sf.offset, sf.count))
|
188
|
+
0 == (sf.count -= n) and return sf.close
|
189
|
+
rescue Errno::EAGAIN
|
190
|
+
return :wait_writable
|
191
|
+
rescue
|
192
|
+
sf.close
|
193
|
+
raise
|
194
|
+
end while true
|
195
|
+
end
|
196
|
+
|
197
|
+
def defer_file_stream(offset, count, io, body)
|
198
|
+
sf = Rainbows::StreamFile.new(offset, count, io, body)
|
199
|
+
unless @wr_queue[0]
|
200
|
+
stream_file(sf) or return
|
201
|
+
end
|
202
|
+
@wr_queue << sf
|
203
|
+
EP.set(self, OUT)
|
204
|
+
end
|
205
|
+
|
206
|
+
# this alternates between a push and pull model from the pipe -> client
|
207
|
+
# to avoid having too much data in userspace on either end.
|
208
|
+
def stream_pipe(pipe)
|
209
|
+
case buf = pipe.tryread
|
210
|
+
when String
|
211
|
+
write(buf)
|
212
|
+
if @wr_queue[0]
|
213
|
+
# client is blocked on write, client will pull from pipe later
|
214
|
+
EP.delete pipe
|
215
|
+
@wr_queue << pipe
|
216
|
+
EP.set(self, OUT)
|
217
|
+
return :deferred
|
218
|
+
end
|
219
|
+
# continue looping...
|
220
|
+
when :wait_readable
|
221
|
+
# pipe blocked on read, let the pipe push to the client in the future
|
222
|
+
EP.delete self
|
223
|
+
EP.set(pipe, IN)
|
224
|
+
return :deferred
|
225
|
+
else # nil => EOF
|
226
|
+
return pipe.close # nil
|
227
|
+
end while true
|
228
|
+
rescue => e
|
229
|
+
pipe.close
|
230
|
+
raise
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
#
|
4
|
+
class Rainbows::Epoll::ResponseChunkPipe < Rainbows::Epoll::ResponsePipe
|
5
|
+
def tryread
|
6
|
+
@io or return
|
7
|
+
|
8
|
+
case rv = super
|
9
|
+
when String
|
10
|
+
"#{rv.size.to_s(16)}\r\n#{rv}\r\n"
|
11
|
+
when nil
|
12
|
+
close
|
13
|
+
"0\r\n\r\n"
|
14
|
+
else
|
15
|
+
rv
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
#
|
4
|
+
class Rainbows::Epoll::ResponsePipe
|
5
|
+
attr_reader :io
|
6
|
+
alias to_io io
|
7
|
+
RBUF = Rainbows::EvCore::RBUF
|
8
|
+
EP = Rainbows::Epoll::EP
|
9
|
+
|
10
|
+
def initialize(io, client, body)
|
11
|
+
@io, @client, @body = io, client, body
|
12
|
+
end
|
13
|
+
|
14
|
+
def epoll_run
|
15
|
+
return close if @client.closed?
|
16
|
+
@client.stream_pipe(self) or @client.on_deferred_write_complete
|
17
|
+
rescue => e
|
18
|
+
close
|
19
|
+
@client.handle_error(e)
|
20
|
+
end
|
21
|
+
|
22
|
+
def close
|
23
|
+
@io or return
|
24
|
+
EP.delete self
|
25
|
+
@body.respond_to?(:close) and @body.close
|
26
|
+
@io = @body = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def tryread
|
30
|
+
Kgio.tryread(@io, 16384, RBUF)
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# :enddoc:
|
3
|
+
module Rainbows::Epoll::Server
|
4
|
+
@@nr = 0
|
5
|
+
Rainbows::Epoll.nr_clients = lambda { @@nr }
|
6
|
+
IN = SleepyPenguin::Epoll::IN | SleepyPenguin::Epoll::ET
|
7
|
+
MAX = Rainbows.server.worker_connections
|
8
|
+
THRESH = MAX - 1
|
9
|
+
LISTENERS = Rainbows::HttpServer::LISTENERS
|
10
|
+
EP = Rainbows::Epoll::EP
|
11
|
+
|
12
|
+
def self.run
|
13
|
+
LISTENERS.each { |sock| EP.add(sock.extend(self), IN) }
|
14
|
+
Rainbows::Epoll.loop
|
15
|
+
end
|
16
|
+
|
17
|
+
# rearms all listeners when there's a free slot
|
18
|
+
def self.decr
|
19
|
+
THRESH == (@@nr -= 1) and LISTENERS.each { |sock| EP.set(sock, IN) }
|
20
|
+
end
|
21
|
+
|
22
|
+
def epoll_run
|
23
|
+
return EP.delete(self) if @@nr >= MAX
|
24
|
+
while io = kgio_tryaccept
|
25
|
+
@@nr += 1
|
26
|
+
# there's a chance the client never even sees epoll for simple apps
|
27
|
+
io.epoll_once
|
28
|
+
return EP.delete(self) if @@nr >= MAX
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/rainbows/error.rb
CHANGED
@@ -7,13 +7,7 @@ module Rainbows::Error
|
|
7
7
|
# if the socket is already closed or broken. We'll always ensure
|
8
8
|
# the socket is closed at the end of this function
|
9
9
|
def self.write(io, e)
|
10
|
-
|
11
|
-
if io.respond_to?(:kgio_trywrite)
|
12
|
-
io.kgio_trywrite(msg)
|
13
|
-
else
|
14
|
-
io.write_nonblock(msg)
|
15
|
-
end
|
16
|
-
end
|
10
|
+
msg = response(e) and Kgio.trywrite(io, msg)
|
17
11
|
rescue
|
18
12
|
end
|
19
13
|
|
@@ -35,8 +29,6 @@ module Rainbows::Error
|
|
35
29
|
when EOFError, Errno::ECONNRESET, Errno::EPIPE, Errno::EINVAL,
|
36
30
|
Errno::EBADF, Errno::ENOTCONN
|
37
31
|
# swallow error if client shuts down one end or disconnects
|
38
|
-
when Rainbows::Response416
|
39
|
-
Rainbows::Const::ERROR_416_RESPONSE
|
40
32
|
when Unicorn::HttpParserError
|
41
33
|
Rainbows::Const::ERROR_400_RESPONSE # try to tell the client they're bad
|
42
34
|
when IOError # HttpParserError is an IOError
|