yahns 0.0.0 → 0.0.1
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.
- 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
|