yahns 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/lib/yahns/acceptor.rb +5 -3
- data/lib/yahns/cap_input.rb +22 -0
- data/lib/yahns/config.rb +8 -4
- data/lib/yahns/daemon.rb +1 -0
- data/lib/yahns/fdmap.rb +0 -8
- data/lib/yahns/http_client.rb +14 -6
- data/lib/yahns/http_context.rb +20 -4
- data/lib/yahns/http_response.rb +3 -3
- data/lib/yahns/max_body.rb +60 -0
- data/lib/yahns/max_body/rewindable_wrapper.rb +19 -0
- data/lib/yahns/max_body/wrapper.rb +71 -0
- data/lib/yahns/queue_egg.rb +2 -5
- data/lib/yahns/queue_epoll.rb +27 -29
- data/lib/yahns/server.rb +29 -19
- data/lib/yahns/server_mp.rb +3 -1
- data/lib/yahns/socket_helper.rb +1 -0
- data/lib/yahns/stream_input.rb +3 -4
- data/lib/yahns/wbuf_common.rb +1 -1
- data/test/helper.rb +45 -45
- data/test/server_helper.rb +3 -2
- data/test/test_bin.rb +96 -0
- data/test/test_client_max_body_size.rb +163 -0
- data/test/test_config.rb +39 -33
- data/test/test_rack_hijack.rb +74 -0
- data/test/test_reopen_logs.rb +64 -0
- data/test/test_server.rb +6 -2
- metadata +10 -4
- data/test/test_queue.rb +0 -59
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c03354f508bb8d5ece9deaac06b265c9f97c7254
|
4
|
+
data.tar.gz: e0bcd2f85d316a6ff76b9e7f38b9449f088e59aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1cf5fae66ee58b4a5435d2d760c6b4283a327e58919c67bcfc7a6ec4805b3a80747e47a9565dab2042bebcfd9ec210ee7f6665397e863ab7cd03cddb9b34493
|
7
|
+
data.tar.gz: 761d334654d9b44ba58f96fbe78d087a8e9c323ce55ef5d1597e6681d5d0076d34f41918858aca66ab3056ce2b52f524d5a2d5ba4f70fc60916f815c1213710b
|
data/GIT-VERSION-GEN
CHANGED
data/GNUmakefile
CHANGED
@@ -12,11 +12,11 @@ all:: test
|
|
12
12
|
test_units := $(wildcard test/test_*.rb)
|
13
13
|
test: $(test_units)
|
14
14
|
$(test_units):
|
15
|
-
$(RUBY) -
|
15
|
+
$(RUBY) -I $(lib) $@ -v
|
16
16
|
|
17
17
|
test-mt: export N = $(shell nproc 2>/dev/null || echo 4)
|
18
18
|
test-mt:
|
19
|
-
$(RUBY) -
|
19
|
+
$(RUBY) -I $(lib) $(addprefix -r./,$(test_units)) -eTime.now --
|
20
20
|
|
21
21
|
check-warnings:
|
22
22
|
@(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \
|
data/lib/yahns/acceptor.rb
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
# License: GPLv3 or later (see COPYING for details)
|
3
3
|
module Yahns::Acceptor # :nodoc:
|
4
4
|
def spawn_acceptor(logger, client_class, queue)
|
5
|
-
accept_flags = Kgio::SOCK_NONBLOCK | Kgio::SOCK_CLOEXEC
|
6
5
|
Thread.new do
|
6
|
+
accept_flags = Kgio::SOCK_NONBLOCK | Kgio::SOCK_CLOEXEC
|
7
7
|
Thread.current.abort_on_exception = true
|
8
8
|
qev_flags = client_class.superclass::QEV_FLAGS
|
9
9
|
begin
|
@@ -21,8 +21,10 @@ module Yahns::Acceptor # :nodoc:
|
|
21
21
|
queue.fdmap.desperate_expire_for(self, 5)
|
22
22
|
sleep 1 # let other threads do some work
|
23
23
|
rescue => e
|
24
|
-
|
25
|
-
|
24
|
+
# sleep since this check is racy (and uncommon)
|
25
|
+
break if closed? || (sleep(0.01) && closed?)
|
26
|
+
Yahns::Log.exception(logger, "accept loop", e)
|
27
|
+
end while true
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2009-2013, Eric Wong <normalperson@yhbt.net> et. al.
|
3
|
+
# License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
|
4
|
+
|
5
|
+
# This is used as the @input/env["rack.input"] when
|
6
|
+
# input_buffering == true or :lazy
|
7
|
+
class Yahns::CapInput < Yahns::TmpIO # :nodoc:
|
8
|
+
attr_writer :bytes_left
|
9
|
+
|
10
|
+
def self.new(limit)
|
11
|
+
rv = super()
|
12
|
+
rv.bytes_left = limit
|
13
|
+
rv
|
14
|
+
end
|
15
|
+
|
16
|
+
def write(buf)
|
17
|
+
if (@bytes_left -= buf.size) < 0
|
18
|
+
raise Unicorn::RequestEntityTooLargeError, "chunked body too big", []
|
19
|
+
end
|
20
|
+
super(buf)
|
21
|
+
end
|
22
|
+
end
|
data/lib/yahns/config.rb
CHANGED
@@ -285,7 +285,6 @@ class Yahns::Config # :nodoc:
|
|
285
285
|
{
|
286
286
|
# config name, minimum value
|
287
287
|
client_body_buffer_size: 1,
|
288
|
-
client_max_body_size: 0,
|
289
288
|
client_header_buffer_size: 1,
|
290
289
|
client_max_header_size: 1,
|
291
290
|
client_timeout: 0,
|
@@ -298,6 +297,12 @@ class Yahns::Config # :nodoc:
|
|
298
297
|
)
|
299
298
|
end
|
300
299
|
|
300
|
+
def client_max_body_size(val)
|
301
|
+
var = _check_in_block(:app, :client_max_body_size)
|
302
|
+
val = _check_int(var, val, 0) if val != nil
|
303
|
+
@block.ctx.__send__("#{var}=", val)
|
304
|
+
end
|
305
|
+
|
301
306
|
def input_buffering(val)
|
302
307
|
var = _check_in_block(:app, :input_buffering)
|
303
308
|
ok = [ :lazy, true, false ]
|
@@ -312,8 +317,8 @@ class Yahns::Config # :nodoc:
|
|
312
317
|
if String === val
|
313
318
|
# we've already bound working_directory by the time we get here
|
314
319
|
val = File.open(File.expand_path(val), "a")
|
320
|
+
val.close_on_exec = val.sync = true
|
315
321
|
val.binmode
|
316
|
-
val.sync = true
|
317
322
|
else
|
318
323
|
rt = %w(puts write flush).map(&:to_sym) # match Rack::Lint
|
319
324
|
rt.all? { |m| val.respond_to?(m) } or raise ArgumentError,
|
@@ -330,7 +335,7 @@ class Yahns::Config # :nodoc:
|
|
330
335
|
@set[key] = path = "/dev/null"
|
331
336
|
end
|
332
337
|
File.open(path, 'a') { |fp| io.reopen(fp) } if String === path
|
333
|
-
io.sync = true
|
338
|
+
io.close_on_exec = io.sync = true
|
334
339
|
end
|
335
340
|
|
336
341
|
[ :logger, :pid, :worker_processes ].each do |var|
|
@@ -338,7 +343,6 @@ class Yahns::Config # :nodoc:
|
|
338
343
|
server.__send__("#{var}=", val) if val != :unset
|
339
344
|
end
|
340
345
|
queue(:default) if @qeggs.empty?
|
341
|
-
@qeggs.each_value { |qegg| qegg.logger ||= server.logger }
|
342
346
|
@app_ctx.each { |app| app.logger ||= server.logger }
|
343
347
|
end
|
344
348
|
end
|
data/lib/yahns/daemon.rb
CHANGED
@@ -25,6 +25,7 @@ module Yahns::Daemon # :nodoc:
|
|
25
25
|
# We cannot use Yahns::Sigevent (eventfd) here because we need
|
26
26
|
# to detect EOF on unexpected death, not just read/write
|
27
27
|
rd, wr = IO.pipe
|
28
|
+
rd.close_on_exec = wr.close_on_exec = true
|
28
29
|
grandparent = $$
|
29
30
|
if fork
|
30
31
|
wr.close # grandparent does not write
|
data/lib/yahns/fdmap.rb
CHANGED
@@ -54,14 +54,6 @@ class Yahns::Fdmap # :nodoc:
|
|
54
54
|
@fdmap_mtx.synchronize { @count -= 1 }
|
55
55
|
end
|
56
56
|
|
57
|
-
def delete(io) # use with rack.hijack (via yahns)
|
58
|
-
fd = io.fileno
|
59
|
-
@fdmap_mtx.synchronize do
|
60
|
-
@fdmap_ary[fd] = nil
|
61
|
-
@count -= 1
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
57
|
# expire a bunch of idle clients and register the current one
|
66
58
|
# We should not be calling this too frequently, it is expensive
|
67
59
|
# This is called while @fdmap_mtx is held
|
data/lib/yahns/http_client.rb
CHANGED
@@ -33,9 +33,9 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
33
33
|
case rv = @state.wbuf_flush(self)
|
34
34
|
when :wait_writable, :wait_readable
|
35
35
|
return rv # tell epoll/kqueue to wait on this more
|
36
|
-
when :
|
37
|
-
@state = :
|
38
|
-
return :
|
36
|
+
when :ignore # :ignore on hijack
|
37
|
+
@state = :ignore
|
38
|
+
return :ignore
|
39
39
|
when Yahns::StreamFile
|
40
40
|
@state = rv # continue looping
|
41
41
|
when true, false # done
|
@@ -45,9 +45,17 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
45
45
|
end while true
|
46
46
|
end
|
47
47
|
|
48
|
+
# used only with "input_buffering true"
|
48
49
|
def mkinput_preread
|
50
|
+
k = self.class
|
51
|
+
len = @hs.content_length
|
52
|
+
mbs = k.client_max_body_size
|
53
|
+
if mbs && len && len > mbs
|
54
|
+
raise Unicorn::RequestEntityTooLargeError,
|
55
|
+
"Content-Length:#{len} too large (>#{mbs})", []
|
56
|
+
end
|
49
57
|
@state = :body
|
50
|
-
@input =
|
58
|
+
@input = k.tmpio_for(len)
|
51
59
|
rbuf = Thread.current[:yahns_rbuf]
|
52
60
|
@hs.filter_body(rbuf, @hs.buf)
|
53
61
|
@input.write(rbuf)
|
@@ -151,9 +159,9 @@ class Yahns::HttpClient < Kgio::Socket # :nodoc:
|
|
151
159
|
|
152
160
|
# run the rack app
|
153
161
|
response = k.app.call(env.merge!(k.app_defaults))
|
154
|
-
return :
|
162
|
+
return :ignore if env.include?(RACK_HIJACK_IO)
|
155
163
|
|
156
|
-
# this returns :wait_readable, :wait_writable, :
|
164
|
+
# this returns :wait_readable, :wait_writable, :ignore, or nil:
|
157
165
|
http_response_write(*response)
|
158
166
|
end
|
159
167
|
|
data/lib/yahns/http_context.rb
CHANGED
@@ -24,7 +24,7 @@ module Yahns::HttpContext # :nodoc:
|
|
24
24
|
@check_client_connection = false
|
25
25
|
@client_body_buffer_size = 112 * 1024
|
26
26
|
@client_header_buffer_size = 4000
|
27
|
-
@client_max_body_size = 1024 * 1024
|
27
|
+
@client_max_body_size = 1024 * 1024 # nil => infinity
|
28
28
|
@input_buffering = true
|
29
29
|
@output_buffering = true
|
30
30
|
@persistent_connections = true
|
@@ -34,7 +34,19 @@ module Yahns::HttpContext # :nodoc:
|
|
34
34
|
|
35
35
|
# call this after forking
|
36
36
|
def after_fork_init
|
37
|
-
@app = @yahns_rack.app_after_fork
|
37
|
+
@app = __wrap_app(@yahns_rack.app_after_fork)
|
38
|
+
end
|
39
|
+
|
40
|
+
def __wrap_app(app)
|
41
|
+
# input_buffering == false is handled in http_client
|
42
|
+
return app if @client_max_body_size == nil
|
43
|
+
|
44
|
+
require 'yahns/cap_input'
|
45
|
+
return app if @input_buffering == true
|
46
|
+
|
47
|
+
# @input_buffering == false/:lazy
|
48
|
+
require 'yahns/max_body'
|
49
|
+
Yahns::MaxBody.new(app, @client_max_body_size)
|
38
50
|
end
|
39
51
|
|
40
52
|
# call this immediately after successful accept()/accept4()
|
@@ -59,7 +71,11 @@ module Yahns::HttpContext # :nodoc:
|
|
59
71
|
end
|
60
72
|
|
61
73
|
def tmpio_for(len)
|
62
|
-
|
63
|
-
StringIO.new("") : Yahns::TmpIO.new
|
74
|
+
if len # Content-Length given
|
75
|
+
len <= @client_body_buffer_size ? StringIO.new("") : Yahns::TmpIO.new
|
76
|
+
else # chunked, unknown length
|
77
|
+
mbs = @client_max_body_size
|
78
|
+
mbs ? Yahns::CapInput.new(mbs) : Yahns::TmpIO.new
|
79
|
+
end
|
64
80
|
end
|
65
81
|
end
|
data/lib/yahns/http_response.rb
CHANGED
@@ -52,8 +52,8 @@ module Yahns::HttpResponse # :nodoc:
|
|
52
52
|
case rv # trysendfile return value
|
53
53
|
when nil
|
54
54
|
case alive
|
55
|
-
when :
|
56
|
-
@state = :
|
55
|
+
when :ignore
|
56
|
+
@state = :ignore
|
57
57
|
when true, false
|
58
58
|
http_response_done(alive)
|
59
59
|
end
|
@@ -140,7 +140,7 @@ module Yahns::HttpResponse # :nodoc:
|
|
140
140
|
|
141
141
|
if hijack
|
142
142
|
hijack.call(self)
|
143
|
-
return :
|
143
|
+
return :ignore
|
144
144
|
end
|
145
145
|
|
146
146
|
if body.respond_to?(:to_path)
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2009-2013, Eric Wong <normalperson@yhbt.net> et. al.
|
3
|
+
# License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
|
4
|
+
|
5
|
+
# Middleware used to enforce client_max_body_size for TeeInput users.
|
6
|
+
#
|
7
|
+
# There is no need to configure this middleware manually, it will
|
8
|
+
# automatically be configured for you based on the client_max_body_size
|
9
|
+
# setting.
|
10
|
+
#
|
11
|
+
# For more fine-grained control, you may also define it per-endpoint in
|
12
|
+
# your Rack config.ru like this:
|
13
|
+
#
|
14
|
+
# map "/limit_1M" do
|
15
|
+
# use Yahns::MaxBody, 1024*1024
|
16
|
+
# run MyApp
|
17
|
+
# end
|
18
|
+
# map "/limit_10M" do
|
19
|
+
# use Yahns::MaxBody, 1024*1024*10
|
20
|
+
# run MyApp
|
21
|
+
# end
|
22
|
+
class Yahns::MaxBody # :nodoc:
|
23
|
+
# This is automatically called when used with Rack::Builder#use
|
24
|
+
# See Yahns::MaxBody
|
25
|
+
def initialize(app, limit)
|
26
|
+
Integer === limit or raise ArgumentError, "limit not an Integer"
|
27
|
+
@app = app
|
28
|
+
@limit = limit
|
29
|
+
end
|
30
|
+
|
31
|
+
RACK_INPUT = "rack.input".freeze # :nodoc:
|
32
|
+
CONTENT_LENGTH = "CONTENT_LENGTH" # :nodoc:
|
33
|
+
HTTP_TRANSFER_ENCODING = "HTTP_TRANSFER_ENCODING" # :nodoc:
|
34
|
+
|
35
|
+
# our main Rack middleware endpoint
|
36
|
+
def call(env) # :nodoc:
|
37
|
+
catch(:yahns_EFBIG) do
|
38
|
+
len = env[CONTENT_LENGTH]
|
39
|
+
if len && len.to_i > @limit
|
40
|
+
return err
|
41
|
+
elsif /\Achunked\z/i =~ env[HTTP_TRANSFER_ENCODING]
|
42
|
+
limit_input!(env)
|
43
|
+
end
|
44
|
+
@app.call(env)
|
45
|
+
end || err
|
46
|
+
end
|
47
|
+
|
48
|
+
# Rack response returned when there's an error
|
49
|
+
def err # :nodoc:
|
50
|
+
[ 413, { 'Content-Length' => '0', 'Content-Type' => 'text/plain' }, [] ]
|
51
|
+
end
|
52
|
+
|
53
|
+
def limit_input!(env) # :nodoc:
|
54
|
+
input = env[RACK_INPUT]
|
55
|
+
klass = input.respond_to?(:rewind) ? RewindableWrapper : Wrapper
|
56
|
+
env[RACK_INPUT] = klass.new(input, @limit)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
require_relative 'max_body/wrapper'
|
60
|
+
require_relative 'max_body/rewindable_wrapper'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2009-2013, Eric Wong <normalperson@yhbt.net> et. al.
|
3
|
+
# License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
|
4
|
+
class Yahns::MaxBody::RewindableWrapper < Yahns::MaxBody::Wrapper # :nodoc:
|
5
|
+
def initialize(rack_input, limit)
|
6
|
+
@orig_limit = limit
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def rewind
|
11
|
+
@limit = @orig_limit
|
12
|
+
@rbuf = ''
|
13
|
+
@input.rewind
|
14
|
+
end
|
15
|
+
|
16
|
+
def size
|
17
|
+
@input.size
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
# Copyright (C) 2009-2013, Eric Wong <normalperson@yhbt.net> et. al.
|
3
|
+
# License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
|
4
|
+
#
|
5
|
+
# This is only used for chunked request bodies, which are rare
|
6
|
+
class Yahns::MaxBody::Wrapper # :nodoc:
|
7
|
+
def initialize(rack_input, limit)
|
8
|
+
@input, @limit, @rbuf = rack_input, limit, ''
|
9
|
+
end
|
10
|
+
|
11
|
+
def each
|
12
|
+
while line = gets
|
13
|
+
yield line
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# chunked encoding means this method behaves more like readpartial,
|
18
|
+
# since Rack does not support a method named "readpartial"
|
19
|
+
def read(length = nil, rv = '')
|
20
|
+
if length
|
21
|
+
if length <= @rbuf.size
|
22
|
+
length < 0 and raise ArgumentError, "negative length #{length} given"
|
23
|
+
rv.replace(@rbuf.slice!(0, length))
|
24
|
+
elsif @rbuf.empty?
|
25
|
+
checked_read(length, rv) or return
|
26
|
+
else
|
27
|
+
rv.replace(@rbuf.slice!(0, @rbuf.size))
|
28
|
+
end
|
29
|
+
rv.empty? && length != 0 ? nil : rv
|
30
|
+
else
|
31
|
+
rv.replace(read_all)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def gets
|
36
|
+
sep = $/
|
37
|
+
if sep.nil?
|
38
|
+
rv = read_all
|
39
|
+
return rv.empty? ? nil : rv
|
40
|
+
end
|
41
|
+
re = /\A(.*?#{Regexp.escape(sep)})/
|
42
|
+
|
43
|
+
begin
|
44
|
+
@rbuf.sub!(re, '') and return $1
|
45
|
+
|
46
|
+
if tmp = checked_read(16384)
|
47
|
+
@rbuf << tmp
|
48
|
+
elsif @rbuf.empty? # EOF
|
49
|
+
return nil
|
50
|
+
else # EOF, return whatever is left
|
51
|
+
return @rbuf.slice!(0, @rbuf.size)
|
52
|
+
end
|
53
|
+
end while true
|
54
|
+
end
|
55
|
+
|
56
|
+
def checked_read(length = 16384, buf = '')
|
57
|
+
if @input.read(length, buf)
|
58
|
+
throw :yahns_EFBIG if ((@limit -= buf.size) < 0)
|
59
|
+
return buf
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def read_all
|
64
|
+
rv = @rbuf.slice!(0, @rbuf.size)
|
65
|
+
tmp = ''
|
66
|
+
while checked_read(16384, tmp)
|
67
|
+
rv << tmp
|
68
|
+
end
|
69
|
+
rv
|
70
|
+
end
|
71
|
+
end
|
data/lib/yahns/queue_egg.rb
CHANGED
@@ -4,20 +4,17 @@
|
|
4
4
|
# this represents a Yahns::Queue before its vivified. This only
|
5
5
|
# lives in the parent process and should be clobbered after qc_vivify
|
6
6
|
class Yahns::QueueEgg # :nodoc:
|
7
|
-
|
8
|
-
attr_accessor :logger
|
7
|
+
attr_accessor :max_events, :worker_threads
|
9
8
|
|
10
9
|
def initialize
|
11
10
|
@max_events = 1 # 1 is good if worker_threads > 1
|
12
11
|
@worker_threads = 7 # any default is wrong for most apps...
|
13
|
-
@logger = nil
|
14
12
|
end
|
15
13
|
|
16
14
|
# only call after forking
|
17
|
-
def
|
15
|
+
def vivify(fdmap)
|
18
16
|
queue = Yahns::Queue.new
|
19
17
|
queue.fdmap = fdmap
|
20
|
-
queue.spawn_worker_threads(@logger, @worker_threads, @max_events)
|
21
18
|
queue
|
22
19
|
end
|
23
20
|
end
|
data/lib/yahns/queue_epoll.rb
CHANGED
@@ -22,36 +22,34 @@ class Yahns::Queue < SleepyPenguin::Epoll::IO # :nodoc:
|
|
22
22
|
end
|
23
23
|
|
24
24
|
# returns an array of infinitely running threads
|
25
|
-
def
|
26
|
-
|
27
|
-
Thread.
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
else
|
47
|
-
raise "BUG: #{io.inspect}#yahns_step returned: #{rv.inspect}"
|
48
|
-
end
|
25
|
+
def worker_thread(logger, max_events)
|
26
|
+
Thread.new do
|
27
|
+
Thread.current[:yahns_rbuf] = ""
|
28
|
+
begin
|
29
|
+
epoll_wait(max_events) do |_, io| # don't care for flags for now
|
30
|
+
case rv = io.yahns_step
|
31
|
+
when :wait_readable
|
32
|
+
epoll_ctl(Epoll::CTL_MOD, io, QEV_RD)
|
33
|
+
when :wait_writable
|
34
|
+
epoll_ctl(Epoll::CTL_MOD, io, QEV_WR)
|
35
|
+
when :wait_readwrite
|
36
|
+
epoll_ctl(Epoll::CTL_MOD, io, QEV_RDWR)
|
37
|
+
when :ignore # only used by rack.hijack
|
38
|
+
@fdmap.decr
|
39
|
+
when nil
|
40
|
+
# this is be the ONLY place where we call IO#close on
|
41
|
+
# things inside the queue
|
42
|
+
io.close
|
43
|
+
@fdmap.decr
|
44
|
+
else
|
45
|
+
raise "BUG: #{io.inspect}#yahns_step returned: #{rv.inspect}"
|
49
46
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
47
|
+
end
|
48
|
+
rescue => e
|
49
|
+
# sleep since this check is racy (and uncommon)
|
50
|
+
break if closed? || (sleep(0.01) && closed?)
|
51
|
+
Yahns::Log.exception(logger, 'queue loop', e)
|
52
|
+
end while true
|
55
53
|
end
|
56
54
|
end
|
57
55
|
end
|