yahns 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|