yahns 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +50 -0
- data/Documentation/yahns-rackup.txt +152 -0
- data/Documentation/yahns.txt +68 -0
- data/Documentation/yahns_config.txt +563 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +14 -7
- data/HACKING +56 -0
- data/INSTALL +8 -0
- data/README +15 -2
- data/Rakefile +2 -2
- data/bin/yahns +1 -2
- data/bin/yahns-rackup +9 -0
- data/examples/yahns_multi.conf.rb +14 -4
- data/examples/yahns_rack_basic.conf.rb +17 -1
- data/extras/README +16 -0
- data/extras/autoindex.rb +151 -0
- data/extras/exec_cgi.rb +108 -0
- data/extras/proxy_pass.rb +210 -0
- data/extras/try_gzip_static.rb +208 -0
- data/lib/yahns.rb +5 -2
- data/lib/yahns/acceptor.rb +64 -22
- data/lib/yahns/cap_input.rb +2 -2
- data/lib/yahns/{client_expire_portable.rb → client_expire_generic.rb} +12 -11
- data/lib/yahns/{client_expire.rb → client_expire_tcpi.rb} +7 -6
- data/lib/yahns/config.rb +107 -22
- data/lib/yahns/daemon.rb +2 -0
- data/lib/yahns/fdmap.rb +28 -9
- data/lib/yahns/http_client.rb +123 -37
- data/lib/yahns/http_context.rb +21 -3
- data/lib/yahns/http_response.rb +80 -19
- data/lib/yahns/log.rb +23 -4
- data/lib/yahns/queue_epoll.rb +20 -9
- data/lib/yahns/queue_quitter.rb +16 -0
- data/lib/yahns/queue_quitter_pipe.rb +24 -0
- data/lib/yahns/rack.rb +0 -1
- data/lib/yahns/rackup_handler.rb +57 -0
- data/lib/yahns/server.rb +189 -59
- data/lib/yahns/server_mp.rb +43 -35
- data/lib/yahns/sigevent_pipe.rb +1 -0
- data/lib/yahns/socket_helper.rb +37 -11
- data/lib/yahns/stream_file.rb +14 -4
- data/lib/yahns/stream_input.rb +13 -7
- data/lib/yahns/tcp_server.rb +7 -0
- data/lib/yahns/tmpio.rb +10 -3
- data/lib/yahns/unix_server.rb +7 -0
- data/lib/yahns/wbuf.rb +19 -2
- data/lib/yahns/wbuf_common.rb +10 -3
- data/lib/yahns/wbuf_str.rb +24 -0
- data/lib/yahns/worker.rb +5 -26
- data/test/helper.rb +15 -5
- data/test/server_helper.rb +37 -1
- data/test/test_bin.rb +17 -8
- data/test/test_buffer_tmpdir.rb +103 -0
- data/test/test_client_expire.rb +71 -35
- data/test/test_client_max_body_size.rb +5 -13
- data/test/test_config.rb +1 -1
- data/test/test_expect_100.rb +176 -0
- data/test/test_extras_autoindex.rb +53 -0
- data/test/test_extras_exec_cgi.rb +81 -0
- data/test/test_extras_exec_cgi.sh +35 -0
- data/test/test_extras_try_gzip_static.rb +177 -0
- data/test/test_input.rb +128 -0
- data/test/test_mt_accept.rb +48 -0
- data/test/test_output_buffering.rb +90 -63
- data/test/test_rack.rb +1 -1
- data/test/test_rack_hijack.rb +2 -6
- data/test/test_reopen_logs.rb +2 -8
- data/test/test_serve_static.rb +104 -8
- data/test/test_server.rb +448 -73
- data/test/test_stream_file.rb +1 -1
- data/test/test_unix_socket.rb +72 -0
- data/test/test_wbuf.rb +20 -17
- data/yahns.gemspec +3 -0
- metadata +57 -5
data/lib/yahns/server_mp.rb
CHANGED
|
@@ -4,35 +4,10 @@
|
|
|
4
4
|
module Yahns::ServerMP # :nodoc:
|
|
5
5
|
EXIT_SIGS = [ :QUIT, :TERM, :INT ]
|
|
6
6
|
|
|
7
|
-
def mp_init
|
|
8
|
-
trap(:CHLD) { @sev.sev_signal }
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
# reaps all unreaped workers
|
|
12
|
-
def reap_all_workers
|
|
13
|
-
begin
|
|
14
|
-
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
|
15
|
-
wpid or return
|
|
16
|
-
if @reexec_pid == wpid
|
|
17
|
-
@logger.error "reaped #{status.inspect} exec()-ed"
|
|
18
|
-
@reexec_pid = 0
|
|
19
|
-
self.pid = @pid.chomp('.oldbin') if @pid
|
|
20
|
-
proc_name 'master'
|
|
21
|
-
else
|
|
22
|
-
worker = @workers.delete(wpid)
|
|
23
|
-
worker_id = worker ? worker.nr : "(unknown)"
|
|
24
|
-
m = "reaped #{status.inspect} worker=#{worker_id}"
|
|
25
|
-
status.success? ? @logger.info(m) : @logger.error(m)
|
|
26
|
-
end
|
|
27
|
-
rescue Errno::ECHILD
|
|
28
|
-
return
|
|
29
|
-
end while true
|
|
30
|
-
end
|
|
31
|
-
|
|
32
7
|
def maintain_worker_count
|
|
33
8
|
(off = @workers.size - @worker_processes) == 0 and return
|
|
34
9
|
off < 0 and return spawn_missing_workers
|
|
35
|
-
@workers.
|
|
10
|
+
@workers.each do |wpid, worker|
|
|
36
11
|
worker.nr >= @worker_processes and Process.kill(:QUIT, wpid)
|
|
37
12
|
end
|
|
38
13
|
end
|
|
@@ -47,7 +22,7 @@ module Yahns::ServerMP # :nodoc:
|
|
|
47
22
|
# to free some resources and drops all sig handlers.
|
|
48
23
|
# traps for USR1, USR2, and HUP may be set in the after_fork Proc
|
|
49
24
|
# by the user.
|
|
50
|
-
def
|
|
25
|
+
def worker_atfork_internal(worker)
|
|
51
26
|
worker.atfork_child
|
|
52
27
|
|
|
53
28
|
# daemon_pipe may be true for non-initial workers
|
|
@@ -71,8 +46,14 @@ module Yahns::ServerMP # :nodoc:
|
|
|
71
46
|
Yahns::START.clear
|
|
72
47
|
@sev.close
|
|
73
48
|
@sev = Yahns::Sigevent.new
|
|
74
|
-
|
|
49
|
+
switch_user(*@user) if @user
|
|
75
50
|
@user = @workers = nil
|
|
51
|
+
__call_hooks(@atfork_child, worker.nr)
|
|
52
|
+
@atfork_child = @atfork_parent = @atfork_prepare = nil
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def __call_hooks(ary, worker_nr)
|
|
56
|
+
ary.each { |x| x.call(worker_nr) } if ary
|
|
76
57
|
end
|
|
77
58
|
|
|
78
59
|
def spawn_missing_workers
|
|
@@ -81,10 +62,13 @@ module Yahns::ServerMP # :nodoc:
|
|
|
81
62
|
@workers.value?(worker_nr) and next
|
|
82
63
|
worker = Yahns::Worker.new(worker_nr)
|
|
83
64
|
@logger.info("worker=#{worker_nr} spawning...")
|
|
65
|
+
__call_hooks(@atfork_prepare, worker_nr)
|
|
84
66
|
if pid = fork
|
|
85
67
|
@workers[pid] = worker.atfork_parent
|
|
68
|
+
# XXX is this useful?
|
|
69
|
+
__call_hooks(@atfork_parent, worker_nr)
|
|
86
70
|
else
|
|
87
|
-
|
|
71
|
+
worker_atfork_internal(worker)
|
|
88
72
|
run_mp_worker(worker)
|
|
89
73
|
end
|
|
90
74
|
end
|
|
@@ -106,10 +90,10 @@ module Yahns::ServerMP # :nodoc:
|
|
|
106
90
|
begin
|
|
107
91
|
@sev.kgio_wait_readable
|
|
108
92
|
@sev.yahns_step
|
|
109
|
-
|
|
93
|
+
reap_all
|
|
110
94
|
case @sig_queue.shift
|
|
111
95
|
when *EXIT_SIGS # graceful shutdown (twice for non graceful)
|
|
112
|
-
|
|
96
|
+
@listeners.each(&:close).clear
|
|
113
97
|
kill_each_worker(:QUIT)
|
|
114
98
|
state = :QUIT
|
|
115
99
|
when :USR1 # rotate logs
|
|
@@ -158,20 +142,23 @@ module Yahns::ServerMP # :nodoc:
|
|
|
158
142
|
def run_mp_worker(worker)
|
|
159
143
|
fdmap = fdmap_init_mp
|
|
160
144
|
alive = true
|
|
145
|
+
watch = [ worker, @sev ]
|
|
161
146
|
begin
|
|
162
|
-
alive = mp_sig_handle(
|
|
147
|
+
alive = mp_sig_handle(watch, alive)
|
|
163
148
|
rescue => e
|
|
164
149
|
Yahns::Log.exception(@logger, "main worker loop", e)
|
|
165
|
-
end while alive || fdmap
|
|
150
|
+
end while alive || dropping(fdmap)
|
|
166
151
|
exit
|
|
167
152
|
ensure
|
|
168
153
|
quit_finish
|
|
169
154
|
end
|
|
170
155
|
|
|
171
|
-
def mp_sig_handle(
|
|
156
|
+
def mp_sig_handle(watch, alive)
|
|
172
157
|
# not performance critical
|
|
173
|
-
|
|
158
|
+
watch.delete_if { |io| io.to_io.closed? }
|
|
159
|
+
if r = IO.select(watch, nil, nil, alive ? nil : 0.01)
|
|
174
160
|
r[0].each { |io| io.yahns_step }
|
|
161
|
+
end
|
|
175
162
|
case @sig_queue.shift
|
|
176
163
|
when *EXIT_SIGS
|
|
177
164
|
return quit_enter(alive)
|
|
@@ -180,4 +167,25 @@ module Yahns::ServerMP # :nodoc:
|
|
|
180
167
|
end
|
|
181
168
|
alive
|
|
182
169
|
end
|
|
170
|
+
|
|
171
|
+
# reaps all unreaped workers/reexec processes
|
|
172
|
+
def reap_all
|
|
173
|
+
begin
|
|
174
|
+
wpid, status = Process.waitpid2(-1, Process::WNOHANG)
|
|
175
|
+
wpid or return
|
|
176
|
+
if @reexec_pid == wpid
|
|
177
|
+
@logger.error "reaped #{status.inspect} exec()-ed"
|
|
178
|
+
@reexec_pid = 0
|
|
179
|
+
self.pid = @pid.chomp('.oldbin') if @pid
|
|
180
|
+
proc_name('master')
|
|
181
|
+
else
|
|
182
|
+
worker = @workers.delete(wpid)
|
|
183
|
+
desc = worker ? "worker=#{worker.nr}" : "(unknown)"
|
|
184
|
+
m = "reaped #{status.inspect} #{desc}"
|
|
185
|
+
status.success? ? @logger.info(m) : @logger.error(m)
|
|
186
|
+
end
|
|
187
|
+
rescue Errno::ECHILD
|
|
188
|
+
return
|
|
189
|
+
end while true
|
|
190
|
+
end
|
|
183
191
|
end
|
data/lib/yahns/sigevent_pipe.rb
CHANGED
data/lib/yahns/socket_helper.rb
CHANGED
|
@@ -3,8 +3,24 @@
|
|
|
3
3
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
4
4
|
# this is only meant for Yahns::Server
|
|
5
5
|
module Yahns::SocketHelper # :nodoc:
|
|
6
|
+
|
|
7
|
+
# Linux got SO_REUSEPORT in 3.9, BSDs have had it for ages
|
|
8
|
+
def so_reuseport
|
|
9
|
+
if defined?(Socket::SO_REUSEPORT)
|
|
10
|
+
Socket::SO_REUSEPORT
|
|
11
|
+
elsif RUBY_PLATFORM =~ /linux/
|
|
12
|
+
if RUBY_PLATFORM =~ /(?:alpha|mips|parisc|sparc)/
|
|
13
|
+
0x0200 # untested
|
|
14
|
+
else
|
|
15
|
+
15 # only tested on x86_64 and i686
|
|
16
|
+
end
|
|
17
|
+
else
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
6
22
|
def set_server_sockopt(sock, opt)
|
|
7
|
-
opt = {backlog: 1024}.merge!(opt
|
|
23
|
+
opt = {backlog: 1024}.merge!(opt || {})
|
|
8
24
|
sock.close_on_exec = true
|
|
9
25
|
|
|
10
26
|
TCPSocket === sock and sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1)
|
|
@@ -26,6 +42,7 @@ module Yahns::SocketHelper # :nodoc:
|
|
|
26
42
|
rcvbuf = sock.getsockopt(:SOL_SOCKET, :SO_RCVBUF).int
|
|
27
43
|
sndbuf = sock.getsockopt(:SOL_SOCKET, :SO_SNDBUF).int
|
|
28
44
|
@logger.info("#{pfx}#{sock_name(sock)} rcvbuf=#{rcvbuf} sndbuf=#{sndbuf}")
|
|
45
|
+
rescue # TODO: get this fixed in rbx
|
|
29
46
|
end
|
|
30
47
|
|
|
31
48
|
# creates a new server, socket. address may be a HOST:PORT or
|
|
@@ -53,14 +70,14 @@ module Yahns::SocketHelper # :nodoc:
|
|
|
53
70
|
end
|
|
54
71
|
old_umask = File.umask(opt[:umask] || 0)
|
|
55
72
|
begin
|
|
56
|
-
|
|
73
|
+
Yahns::UNIXServer.new(address)
|
|
57
74
|
ensure
|
|
58
75
|
File.umask(old_umask)
|
|
59
76
|
end
|
|
60
77
|
elsif /\A\[([a-fA-F0-9:]+)\]:(\d+)\z/ =~ address
|
|
61
|
-
|
|
78
|
+
new_tcp_server($1, $2.to_i, opt.merge(ipv6: true))
|
|
62
79
|
elsif /\A(\d+\.\d+\.\d+\.\d+):(\d+)\z/ =~ address
|
|
63
|
-
|
|
80
|
+
new_tcp_server($1, $2.to_i, opt)
|
|
64
81
|
else
|
|
65
82
|
raise ArgumentError, "Don't know how to bind: #{address}"
|
|
66
83
|
end
|
|
@@ -68,14 +85,23 @@ module Yahns::SocketHelper # :nodoc:
|
|
|
68
85
|
sock
|
|
69
86
|
end
|
|
70
87
|
|
|
71
|
-
def
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
def new_tcp_server(addr, port, opt)
|
|
89
|
+
sock = Socket.new(opt[:ipv6] ? :INET6 : :INET, :STREAM, 0)
|
|
90
|
+
if opt.key?(:ipv6only)
|
|
91
|
+
sock.setsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY, opt[:ipv6only] ? 1 : 0)
|
|
92
|
+
end
|
|
75
93
|
sock.setsockopt(:SOL_SOCKET, :SO_REUSEADDR, 1)
|
|
94
|
+
|
|
95
|
+
begin
|
|
96
|
+
sock.setsockopt(:SOL_SOCKET, so_reuseport, 1)
|
|
97
|
+
rescue => e
|
|
98
|
+
name = sock_name(sock)
|
|
99
|
+
@logger.warn("failed to set SO_REUSEPORT on #{name}: #{e.message}")
|
|
100
|
+
end if opt[:reuseport]
|
|
101
|
+
|
|
76
102
|
sock.bind(Socket.pack_sockaddr_in(port, addr))
|
|
77
103
|
sock.autoclose = false
|
|
78
|
-
|
|
104
|
+
Yahns::TCPServer.for_fd(sock.fileno)
|
|
79
105
|
end
|
|
80
106
|
|
|
81
107
|
# returns rfc2732-style (e.g. "[::1]:666") addresses for IPv6
|
|
@@ -110,9 +136,9 @@ module Yahns::SocketHelper # :nodoc:
|
|
|
110
136
|
sock.autoclose = false
|
|
111
137
|
begin
|
|
112
138
|
Socket.unpack_sockaddr_in(sock.getsockname)
|
|
113
|
-
|
|
139
|
+
Yahns::TCPServer.for_fd(sock.fileno)
|
|
114
140
|
rescue ArgumentError
|
|
115
|
-
|
|
141
|
+
Yahns::UNIXServer.for_fd(sock.fileno)
|
|
116
142
|
end
|
|
117
143
|
end
|
|
118
144
|
end
|
data/lib/yahns/stream_file.rb
CHANGED
|
@@ -6,6 +6,9 @@ require_relative 'wbuf_common'
|
|
|
6
6
|
class Yahns::StreamFile # :nodoc:
|
|
7
7
|
include Yahns::WbufCommon
|
|
8
8
|
|
|
9
|
+
# do not use this in your app (or any of our API)
|
|
10
|
+
NeedClose = Class.new(File) # :nodoc:
|
|
11
|
+
|
|
9
12
|
def initialize(body, persist, offset, count)
|
|
10
13
|
if body.respond_to?(:to_io)
|
|
11
14
|
@tmpio = body.to_io
|
|
@@ -15,7 +18,16 @@ class Yahns::StreamFile # :nodoc:
|
|
|
15
18
|
@tmpio = IO.for_fd($1.to_i)
|
|
16
19
|
@tmpio.autoclose = false
|
|
17
20
|
else
|
|
18
|
-
|
|
21
|
+
retried = false
|
|
22
|
+
begin
|
|
23
|
+
@tmpio = NeedClose.open(path)
|
|
24
|
+
rescue Errno::EMFILE, Errno::ENFILE
|
|
25
|
+
raise if retried
|
|
26
|
+
retried = true
|
|
27
|
+
Thread.current[:yahns_fdmap].desperate_expire_for(nil, 5)
|
|
28
|
+
sleep(1)
|
|
29
|
+
retry
|
|
30
|
+
end
|
|
19
31
|
end
|
|
20
32
|
end
|
|
21
33
|
@sf_offset = offset
|
|
@@ -26,9 +38,7 @@ class Yahns::StreamFile # :nodoc:
|
|
|
26
38
|
|
|
27
39
|
# called by last wbuf_flush
|
|
28
40
|
def wbuf_close(client)
|
|
29
|
-
if
|
|
30
|
-
@tmpio.close
|
|
31
|
-
end
|
|
41
|
+
@tmpio.close if NeedClose === @tmpio
|
|
32
42
|
wbuf_close_common(client)
|
|
33
43
|
end
|
|
34
44
|
end
|
data/lib/yahns/stream_input.rb
CHANGED
|
@@ -45,7 +45,7 @@ class Yahns::StreamInput # :nodoc:
|
|
|
45
45
|
to_read = length - @rbuf.size
|
|
46
46
|
rv.replace(@rbuf.slice!(0, @rbuf.size))
|
|
47
47
|
until to_read == 0 || eof? || (rv.size > 0 && @chunked)
|
|
48
|
-
@client.
|
|
48
|
+
@client.yahns_read(to_read, @buf) or eof!
|
|
49
49
|
filter_body(@rbuf, @buf)
|
|
50
50
|
rv << @rbuf
|
|
51
51
|
to_read -= @rbuf.size
|
|
@@ -63,6 +63,10 @@ class Yahns::StreamInput # :nodoc:
|
|
|
63
63
|
@client ? @client.class.client_body_buffer_size : nil
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
def __tlsbuf
|
|
67
|
+
Thread.current[:yahns_rbuf]
|
|
68
|
+
end
|
|
69
|
+
|
|
66
70
|
# :call-seq:
|
|
67
71
|
# ios.gets => string or nil
|
|
68
72
|
#
|
|
@@ -80,12 +84,13 @@ class Yahns::StreamInput # :nodoc:
|
|
|
80
84
|
end
|
|
81
85
|
re = /\A(.*?#{Regexp.escape(sep)})/
|
|
82
86
|
rsize = __rsize or return
|
|
87
|
+
tlsbuf = __tlsbuf
|
|
83
88
|
begin
|
|
84
89
|
@rbuf.sub!(re, '') and return $1
|
|
85
90
|
return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size) if eof?
|
|
86
|
-
@client.
|
|
87
|
-
filter_body(
|
|
88
|
-
@rbuf <<
|
|
91
|
+
@client.yahns_read(rsize, @buf) or eof!
|
|
92
|
+
filter_body(tlsbuf, @buf)
|
|
93
|
+
@rbuf << tlsbuf
|
|
89
94
|
end while true
|
|
90
95
|
end
|
|
91
96
|
|
|
@@ -105,9 +110,10 @@ class Yahns::StreamInput # :nodoc:
|
|
|
105
110
|
def eof?
|
|
106
111
|
if @parser.body_eof?
|
|
107
112
|
rsize = __rsize
|
|
113
|
+
tlsbuf = __tlsbuf
|
|
108
114
|
while @chunked && ! @parser.parse
|
|
109
|
-
|
|
110
|
-
@buf <<
|
|
115
|
+
@client.yahns_read(rsize, tlsbuf) or eof!
|
|
116
|
+
@buf << tlsbuf
|
|
111
117
|
end
|
|
112
118
|
@client = nil
|
|
113
119
|
true
|
|
@@ -126,7 +132,7 @@ class Yahns::StreamInput # :nodoc:
|
|
|
126
132
|
dst.replace(@rbuf)
|
|
127
133
|
rsize = __rsize or return
|
|
128
134
|
until eof?
|
|
129
|
-
@client.
|
|
135
|
+
@client.yahns_read(rsize, @buf) or eof!
|
|
130
136
|
filter_body(@rbuf, @buf)
|
|
131
137
|
dst << @rbuf
|
|
132
138
|
end
|
data/lib/yahns/tmpio.rb
CHANGED
|
@@ -11,11 +11,18 @@ class Yahns::TmpIO < File # :nodoc:
|
|
|
11
11
|
# creates and returns a new File object. The File is unlinked
|
|
12
12
|
# immediately, switched to binary mode, and userspace output
|
|
13
13
|
# buffering is disabled
|
|
14
|
-
def self.new
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
def self.new(tmpdir = Dir.tmpdir)
|
|
15
|
+
retried = false
|
|
16
|
+
begin
|
|
17
|
+
fp = super("#{tmpdir}/#{rand}", RDWR|CREAT|EXCL, 0600)
|
|
17
18
|
rescue Errno::EEXIST
|
|
18
19
|
retry
|
|
20
|
+
rescue Errno::EMFILE, Errno::ENFILE
|
|
21
|
+
raise if retried
|
|
22
|
+
retried = true
|
|
23
|
+
Thread.current[:yahns_fdmap].desperate_expire_for(nil, 5)
|
|
24
|
+
sleep(1)
|
|
25
|
+
retry
|
|
19
26
|
end
|
|
20
27
|
unlink(fp.path)
|
|
21
28
|
fp.binmode
|
data/lib/yahns/wbuf.rb
CHANGED
|
@@ -6,14 +6,25 @@ require_relative 'wbuf_common'
|
|
|
6
6
|
class Yahns::Wbuf # :nodoc:
|
|
7
7
|
include Yahns::WbufCommon
|
|
8
8
|
|
|
9
|
-
def initialize(body, persist)
|
|
10
|
-
@tmpio = Yahns::TmpIO.new
|
|
9
|
+
def initialize(body, persist, tmpdir)
|
|
10
|
+
@tmpio = Yahns::TmpIO.new(tmpdir)
|
|
11
11
|
@sf_offset = @sf_count = 0
|
|
12
12
|
@wbuf_persist = persist # whether or not we keep the connection alive
|
|
13
13
|
@body = body
|
|
14
|
+
@bypass = false
|
|
14
15
|
end
|
|
15
16
|
|
|
16
17
|
def wbuf_write(client, buf)
|
|
18
|
+
# try to bypass the VFS layer if we're all caught up
|
|
19
|
+
case rv = client.kgio_trywrite(buf)
|
|
20
|
+
when String
|
|
21
|
+
buf = rv # retry in loop
|
|
22
|
+
when nil
|
|
23
|
+
return # yay! hopefully we don't have to buffer again
|
|
24
|
+
when :wait_writable, :wait_readable
|
|
25
|
+
@bypass = false # ugh, continue to buffering to file
|
|
26
|
+
end while @bypass
|
|
27
|
+
|
|
17
28
|
@sf_count += @tmpio.write(buf)
|
|
18
29
|
case rv = client.trysendfile(@tmpio, @sf_offset, @sf_count)
|
|
19
30
|
when Integer
|
|
@@ -25,6 +36,12 @@ class Yahns::Wbuf # :nodoc:
|
|
|
25
36
|
raise "BUG: #{rv.nil ? "EOF" : rv.inspect} on tmpio " \
|
|
26
37
|
"sf_offset=#@sf_offset sf_count=#@sf_count"
|
|
27
38
|
end while @sf_count > 0
|
|
39
|
+
|
|
40
|
+
# we're all caught up, try to prevent dirty data from getting flushed
|
|
41
|
+
# to disk if we can help it.
|
|
42
|
+
@tmpio.truncate(@sf_offset = 0)
|
|
43
|
+
@tmpio.rewind
|
|
44
|
+
@bypass = true
|
|
28
45
|
nil
|
|
29
46
|
end
|
|
30
47
|
|
data/lib/yahns/wbuf_common.rb
CHANGED
|
@@ -14,8 +14,16 @@ module Yahns::WbufCommon # :nodoc:
|
|
|
14
14
|
@sf_offset += rv # keep going otherwise
|
|
15
15
|
when :wait_writable, :wait_readable
|
|
16
16
|
return rv
|
|
17
|
+
when nil
|
|
18
|
+
# response got truncated, drop the connection
|
|
19
|
+
# this may happens when using Rack::File or similar, we can't
|
|
20
|
+
# keep the connection alive because we already sent our Content-Length
|
|
21
|
+
# header the client would be confused.
|
|
22
|
+
@wbuf_persist = false
|
|
23
|
+
return wbuf_close(client)
|
|
17
24
|
else
|
|
18
|
-
raise "BUG:
|
|
25
|
+
raise "BUG: rv=#{rv.inspect} " \
|
|
26
|
+
"on tmpio=#{@tmpio.inspect} " \
|
|
19
27
|
"sf_offset=#@sf_offset sf_count=#@sf_count"
|
|
20
28
|
end while true
|
|
21
29
|
end
|
|
@@ -23,8 +31,7 @@ module Yahns::WbufCommon # :nodoc:
|
|
|
23
31
|
def wbuf_close_common(client)
|
|
24
32
|
@body.close if @body.respond_to?(:close)
|
|
25
33
|
if @wbuf_persist.respond_to?(:call) # hijack
|
|
26
|
-
@wbuf_persist
|
|
27
|
-
:ignore
|
|
34
|
+
client.response_hijacked(@wbuf_persist) # :ignore
|
|
28
35
|
else
|
|
29
36
|
@wbuf_persist # true or false or Yahns::StreamFile
|
|
30
37
|
end
|