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
data/lib/yahns/server.rb
CHANGED
@@ -20,6 +20,8 @@ class Yahns::Server # :nodoc:
|
|
20
20
|
@pid = nil
|
21
21
|
@worker_processes = nil
|
22
22
|
@user = nil
|
23
|
+
@queues = []
|
24
|
+
@thr = []
|
23
25
|
end
|
24
26
|
|
25
27
|
def sqwakeup(sig)
|
@@ -173,19 +175,14 @@ class Yahns::Server # :nodoc:
|
|
173
175
|
end
|
174
176
|
end
|
175
177
|
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
cmd = [ Yahns::START[0] ].concat(Yahns::START[:argv])
|
185
|
-
@logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
|
186
|
-
cmd << redirects
|
187
|
-
exec(*cmd)
|
188
|
-
end
|
178
|
+
opts = {}
|
179
|
+
@listeners.each { |sock| opts[sock.fileno] = sock }
|
180
|
+
env = { "YAHNS_FD" => opts.keys.map(&:to_s).join(',') }
|
181
|
+
opts[:chdir] = @config.value(:working_directory) || Yahns::START[:cwd]
|
182
|
+
cmd = [ Yahns::START[0] ].concat(Yahns::START[:argv])
|
183
|
+
@logger.info "spawning #{cmd.inspect} (in #{opts[:chdir]})"
|
184
|
+
cmd << opts
|
185
|
+
@reexec_pid = Process.spawn(env, *cmd)
|
189
186
|
proc_name 'master (old)'
|
190
187
|
end
|
191
188
|
|
@@ -267,21 +264,25 @@ class Yahns::Server # :nodoc:
|
|
267
264
|
|
268
265
|
# initialize queues (epoll/kqueue) and associated worker threads
|
269
266
|
queues = {}
|
270
|
-
@config.qeggs.each do |name,
|
271
|
-
queue =
|
272
|
-
|
267
|
+
@config.qeggs.each do |name, qe|
|
268
|
+
queue = qe.vivify(fdmap)
|
269
|
+
qe.worker_threads.times do
|
270
|
+
@thr << queue.worker_thread(@logger, qe.max_events)
|
271
|
+
end
|
272
|
+
@queues << queue
|
273
|
+
queues[qe] = queue
|
273
274
|
end
|
274
275
|
|
275
276
|
# spin up applications (which are preload: false)
|
276
277
|
@config.app_ctx.each { |ctx| ctx.after_fork_init }
|
277
278
|
|
278
|
-
# spin up
|
279
|
+
# spin up acceptor threads, clients flow into worker queues after this
|
279
280
|
@listeners.each do |l|
|
280
281
|
ctx = sock_opts(l)[:yahns_app_ctx]
|
281
282
|
qegg = ctx.qegg || @config.qeggs[:default]
|
282
283
|
|
283
284
|
# acceptors feed the the queues
|
284
|
-
l.spawn_acceptor(@logger, ctx, queues[qegg])
|
285
|
+
@thr << l.spawn_acceptor(@logger, ctx, queues[qegg])
|
285
286
|
end
|
286
287
|
fdmap
|
287
288
|
end
|
@@ -293,7 +294,7 @@ class Yahns::Server # :nodoc:
|
|
293
294
|
end
|
294
295
|
|
295
296
|
def quit_enter(alive)
|
296
|
-
self.listeners = []
|
297
|
+
self.listeners = [] # close acceptors, we close epolls in quit_done
|
297
298
|
exit(0) unless alive # drop connections immediately if signaled twice
|
298
299
|
@config.config_listeners.each_value do |opts|
|
299
300
|
ctx = opts[:yahns_app_ctx] or next
|
@@ -302,6 +303,13 @@ class Yahns::Server # :nodoc:
|
|
302
303
|
false
|
303
304
|
end
|
304
305
|
|
306
|
+
# drops all the the IO objects we have threads waiting on before exiting
|
307
|
+
def quit_finish
|
308
|
+
@queues.each(&:close)
|
309
|
+
self.listeners = [] # just in case, this is used in ensure
|
310
|
+
@thr.each(&:join)
|
311
|
+
end
|
312
|
+
|
305
313
|
def sp_sig_handle(alive)
|
306
314
|
@sev.kgio_wait_readable(alive ? nil : 0.01)
|
307
315
|
@sev.yahns_step
|
@@ -332,5 +340,7 @@ class Yahns::Server # :nodoc:
|
|
332
340
|
Yahns::Log.exception(@logger, "main loop", e)
|
333
341
|
end while alive || fdmap.size > 0
|
334
342
|
unlink_pid_safe(@pid) if @pid
|
343
|
+
ensure
|
344
|
+
quit_finish
|
335
345
|
end
|
336
346
|
end
|
data/lib/yahns/server_mp.rb
CHANGED
@@ -150,7 +150,7 @@ module Yahns::ServerMP # :nodoc:
|
|
150
150
|
|
151
151
|
def fdmap_init_mp
|
152
152
|
fdmap = fdmap_init # builds apps (if not preloading)
|
153
|
-
EXIT_SIGS.each { |sig| trap(sig) { sqwakeup(sig) } }
|
153
|
+
[:USR1, *EXIT_SIGS].each { |sig| trap(sig) { sqwakeup(sig) } }
|
154
154
|
@config.postfork_cleanup # reduce live objects
|
155
155
|
fdmap
|
156
156
|
end
|
@@ -164,6 +164,8 @@ module Yahns::ServerMP # :nodoc:
|
|
164
164
|
Yahns::Log.exception(@logger, "main worker loop", e)
|
165
165
|
end while alive || fdmap.size > 0
|
166
166
|
exit
|
167
|
+
ensure
|
168
|
+
quit_finish
|
167
169
|
end
|
168
170
|
|
169
171
|
def mp_sig_handle(worker, alive)
|
data/lib/yahns/socket_helper.rb
CHANGED
@@ -5,6 +5,7 @@
|
|
5
5
|
module Yahns::SocketHelper # :nodoc:
|
6
6
|
def set_server_sockopt(sock, opt)
|
7
7
|
opt = {backlog: 1024}.merge!(opt) if opt
|
8
|
+
sock.close_on_exec = true
|
8
9
|
|
9
10
|
TCPSocket === sock and sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1)
|
10
11
|
sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
|
data/lib/yahns/stream_input.rb
CHANGED
@@ -60,7 +60,7 @@ class Yahns::StreamInput # :nodoc:
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def __rsize
|
63
|
-
@client.class.client_body_buffer_size
|
63
|
+
@client ? @client.class.client_body_buffer_size : nil
|
64
64
|
end
|
65
65
|
|
66
66
|
# :call-seq:
|
@@ -79,7 +79,7 @@ class Yahns::StreamInput # :nodoc:
|
|
79
79
|
return rv.empty? ? nil : rv
|
80
80
|
end
|
81
81
|
re = /\A(.*?#{Regexp.escape(sep)})/
|
82
|
-
rsize = __rsize
|
82
|
+
rsize = __rsize or return
|
83
83
|
begin
|
84
84
|
@rbuf.sub!(re, '') and return $1
|
85
85
|
return @rbuf.empty? ? nil : @rbuf.slice!(0, @rbuf.size) if eof?
|
@@ -124,8 +124,7 @@ class Yahns::StreamInput # :nodoc:
|
|
124
124
|
|
125
125
|
def read_all(dst)
|
126
126
|
dst.replace(@rbuf)
|
127
|
-
|
128
|
-
rsize = @client.class.client_body_buffer_size
|
127
|
+
rsize = __rsize or return
|
129
128
|
until eof?
|
130
129
|
@client.kgio_read(rsize, @buf) or eof!
|
131
130
|
filter_body(@rbuf, @buf)
|
data/lib/yahns/wbuf_common.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
2
2
|
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
$stdout.sync = $stderr.sync = Thread.abort_on_exception = true
|
4
|
+
$-w = true if RUBY_VERSION.to_f >= 2.0
|
4
5
|
require 'thread'
|
5
6
|
|
6
7
|
# Global Test Lock, to protect:
|
@@ -11,69 +12,68 @@ GTL = Mutex.new
|
|
11
12
|
if ENV["COVERAGE"]
|
12
13
|
require "coverage"
|
13
14
|
COVMATCH = %r{/lib/yahns\b.*rb\z}
|
14
|
-
COVTMP = File.open("coverage.dump", IO::CREAT|IO::RDWR)
|
15
|
-
COVTMP.binmode
|
16
|
-
COVTMP.sync = true
|
17
15
|
|
18
16
|
def __covmerge
|
19
17
|
res = Coverage.result
|
20
18
|
|
21
|
-
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
merge
|
19
|
+
# do not create the file, Makefile does htis before any tests run
|
20
|
+
File.open("coverage.dump", IO::RDWR) do |covtmp|
|
21
|
+
covtmp.binmode
|
22
|
+
covtmp.sync = true
|
23
|
+
|
24
|
+
# we own this file (at least until somebody tries to use NFS :x)
|
25
|
+
covtmp.flock(File::LOCK_EX)
|
26
|
+
|
27
|
+
prev = covtmp.read
|
28
|
+
prev = prev.empty? ? {} : Marshal.load(prev)
|
29
|
+
res.each do |filename, counts|
|
30
|
+
# filter out stuff that's not in our project
|
31
|
+
COVMATCH =~ filename or next
|
32
|
+
|
33
|
+
merge = prev[filename] || []
|
34
|
+
merge = merge
|
35
|
+
counts.each_with_index do |count, i|
|
36
|
+
count or next
|
37
|
+
merge[i] = (merge[i] || 0) + count
|
38
|
+
end
|
39
|
+
prev[filename] = merge
|
36
40
|
end
|
37
|
-
|
41
|
+
covtmp.rewind
|
42
|
+
covtmp.truncate(0)
|
43
|
+
covtmp.write(Marshal.dump(prev))
|
44
|
+
covtmp.flock(File::LOCK_UN)
|
38
45
|
end
|
39
|
-
COVTMP.rewind
|
40
|
-
COVTMP.truncate(0)
|
41
|
-
COVTMP.write(Marshal.dump(prev))
|
42
|
-
COVTMP.flock(File::LOCK_UN)
|
43
46
|
end
|
44
47
|
|
45
48
|
Coverage.start
|
49
|
+
# we need to nest at_exit to fire after minitest runs
|
46
50
|
at_exit { at_exit { __covmerge } }
|
47
51
|
end
|
48
52
|
|
49
53
|
gem 'minitest'
|
50
|
-
|
51
|
-
require
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
Minitest::Unit::TestCase
|
54
|
+
begin # favor minitest 5
|
55
|
+
require 'minitest'
|
56
|
+
Testcase = Minitest::Test
|
57
|
+
mtobj = Minitest
|
58
|
+
rescue NameError, LoadError # but support minitest 4
|
59
|
+
require 'minitest/unit'
|
60
|
+
Testcase = Minitest::Unit::TestCase
|
61
|
+
mtobj = MiniTest::Unit.new
|
57
62
|
end
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
if
|
68
|
-
at_exit do
|
69
|
-
FIFOS.each { |(pid,_path)| File.unlink(_path) if $$ == pid }
|
70
|
-
end
|
71
|
-
end
|
72
|
-
FIFOS << [ $$, path ]
|
64
|
+
# Not using minitest/autorun because that doesn't guard against redundant
|
65
|
+
# extra runs with fork. We cannot use exit! in the tests either
|
66
|
+
# (since users/apps hosted on yahns _should_ expect exit, not exit!).
|
67
|
+
TSTART_PID = $$
|
68
|
+
at_exit do
|
69
|
+
# skipping @@after_run stuff in minitest since we don't need it
|
70
|
+
case $!
|
71
|
+
when nil, SystemExit
|
72
|
+
mtobj.run(ARGV) if $$ == TSTART_PID
|
73
73
|
end
|
74
|
-
path
|
75
74
|
end
|
76
75
|
|
76
|
+
require "tempfile"
|
77
77
|
require 'tmpdir'
|
78
78
|
class Dir
|
79
79
|
require 'fileutils'
|
data/test/server_helper.rb
CHANGED
@@ -9,8 +9,9 @@ module ServerHelper
|
|
9
9
|
def check_err(err = @err)
|
10
10
|
err = File.open(err.path, "r") if err.respond_to?(:path)
|
11
11
|
err.rewind
|
12
|
-
lines = err.readlines
|
13
|
-
|
12
|
+
lines = err.readlines
|
13
|
+
bad_lines = lines.dup.delete_if { |l| l =~ /INFO/ }
|
14
|
+
assert bad_lines.empty?, lines.join("\n")
|
14
15
|
err.close! if err == @err
|
15
16
|
end
|
16
17
|
|
data/test/test_bin.rb
CHANGED
@@ -47,6 +47,7 @@ class TestBin < Testcase
|
|
47
47
|
ENV["YAHNS_FD"] = @srv.fileno.to_s
|
48
48
|
else
|
49
49
|
@srv = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
|
50
|
+
@srv.close_on_exec = true # needed for 1.9.3
|
50
51
|
end
|
51
52
|
host, port = @srv.addr[3], @srv.addr[1]
|
52
53
|
listen = ENV["YAHNS_TEST_LISTEN"] = "#{host}:#{port}"
|
@@ -94,4 +95,99 @@ class TestBin < Testcase
|
|
94
95
|
end
|
95
96
|
@pid.close! if @pid
|
96
97
|
end
|
98
|
+
|
99
|
+
def test_usr2_preload_noworker; usr2(true, false); end
|
100
|
+
def test_usr2_preload_worker; usr2(true, true); end
|
101
|
+
def test_usr2_nopreload_worker; usr2(false, true); end
|
102
|
+
def test_usr2_nopreload_noworker; usr2(false, false); end
|
103
|
+
|
104
|
+
def usr2(preload, worker)
|
105
|
+
Dir.mktmpdir { |tmpdir| usr2_dir(tmpdir, preload, worker) }
|
106
|
+
end
|
107
|
+
|
108
|
+
def usr2_dir(tmpdir, preload, worker)
|
109
|
+
exe = "#{tmpdir}/yahns"
|
110
|
+
|
111
|
+
# need to fork here since tests are MT and the FD can leak out and go to
|
112
|
+
# other processes which fork (but do not exec), causing ETXTBUSY on
|
113
|
+
# Process.spawn
|
114
|
+
pid = fork do
|
115
|
+
ruby = "#!#{`which ruby`}"
|
116
|
+
File.open(exe, "w") { |y|
|
117
|
+
lines = File.readlines("bin/yahns")
|
118
|
+
lines[0] = ruby
|
119
|
+
y.chmod(0755)
|
120
|
+
y.syswrite(lines.join)
|
121
|
+
}
|
122
|
+
end
|
123
|
+
_, status = Process.waitpid2(pid)
|
124
|
+
assert status.success?, status.inspect
|
125
|
+
|
126
|
+
@pid = tmpfile(%w(test_bin_daemon .pid))
|
127
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
128
|
+
@ru = tmpfile(%w(test_bin_daemon .ru))
|
129
|
+
@ru.puts("use Rack::ContentLength")
|
130
|
+
@ru.puts("use Rack::ContentType, 'text/plain'")
|
131
|
+
@ru.puts("run lambda { |_| [ 200, {}, [ Process.pid.to_s ] ] }")
|
132
|
+
cfg = tmpfile(%w(test_bin_daemon_conf .rb))
|
133
|
+
cfg.puts "pid '#{@pid.path}'"
|
134
|
+
cfg.puts "stderr_path '#{@err.path}'"
|
135
|
+
cfg.puts "worker_processes 1" if worker
|
136
|
+
cfg.puts "app(:rack, '#{@ru.path}', preload: #{preload}) do"
|
137
|
+
cfg.puts " listen '#{host}:#{port}'"
|
138
|
+
cfg.puts "end"
|
139
|
+
env = {
|
140
|
+
"YAHNS_FD" => @srv.fileno.to_s,
|
141
|
+
"PATH" => "#{tmpdir}:#{ENV['PATH']}",
|
142
|
+
"RUBYLIB" => "#{Dir.pwd}/lib",
|
143
|
+
}
|
144
|
+
cmd = %W(#{exe} -D -c #{cfg.path})
|
145
|
+
cmd << { @srv => @srv, close_others: true }
|
146
|
+
pid = GTL.synchronize { Process.spawn(env, *cmd) }
|
147
|
+
res = Net::HTTP.start(host, port) { |h| h.get("/") }
|
148
|
+
assert_equal 200, res.code.to_i
|
149
|
+
orig = res.body
|
150
|
+
Process.kill(:USR2, pid)
|
151
|
+
newpid = pid
|
152
|
+
Timeout.timeout(10) do
|
153
|
+
begin
|
154
|
+
newpid = File.read(@pid.path)
|
155
|
+
rescue Errno::ENOENT
|
156
|
+
end while newpid.to_i == pid && sleep(0.01)
|
157
|
+
end
|
158
|
+
Process.kill(:QUIT, pid)
|
159
|
+
_, status = Timeout.timeout(10) { Process.waitpid2(pid) }
|
160
|
+
assert status.success?, status
|
161
|
+
res = Net::HTTP.start(host, port) { |h| h.get("/") }
|
162
|
+
assert_equal 200, res.code.to_i
|
163
|
+
second = res.body
|
164
|
+
refute_equal orig, second
|
165
|
+
|
166
|
+
newpid = newpid.to_i
|
167
|
+
assert_operator newpid, :>, 0
|
168
|
+
Process.kill(:HUP, newpid)
|
169
|
+
third = second
|
170
|
+
Timeout.timeout(10) do
|
171
|
+
begin
|
172
|
+
third = Net::HTTP.start(host, port) { |h| h.get("/") }.body
|
173
|
+
end while third == second && sleep(0.01)
|
174
|
+
end
|
175
|
+
if worker
|
176
|
+
Process.kill(0, newpid) # nothing should raise
|
177
|
+
else
|
178
|
+
poke_until_dead newpid
|
179
|
+
end
|
180
|
+
rescue => e
|
181
|
+
Yahns::Log.exception(Logger.new($stderr), "test", e)
|
182
|
+
raise
|
183
|
+
ensure
|
184
|
+
File.unlink(exe) if exe
|
185
|
+
cfg.close! if cfg
|
186
|
+
pid = File.read(@pid.path)
|
187
|
+
pid = pid.to_i
|
188
|
+
assert_operator pid, :>, 0
|
189
|
+
Process.kill(:QUIT, pid)
|
190
|
+
poke_until_dead pid
|
191
|
+
@pid.close!
|
192
|
+
end
|
97
193
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
require_relative 'server_helper'
|
4
|
+
|
5
|
+
class TestClientMaxBodySize < Testcase
|
6
|
+
parallelize_me!
|
7
|
+
include ServerHelper
|
8
|
+
alias setup server_helper_setup
|
9
|
+
alias teardown server_helper_teardown
|
10
|
+
DEFMBS = 1024 * 1024
|
11
|
+
|
12
|
+
DRAINER = lambda do |e|
|
13
|
+
input = e["rack.input"]
|
14
|
+
buf = ""
|
15
|
+
nr = 0
|
16
|
+
while rv = input.read(16384, buf)
|
17
|
+
nr += rv.size
|
18
|
+
end
|
19
|
+
body = nr.to_s
|
20
|
+
h = { "Content-Length" => body.size.to_s, "Content-Type" => 'text/plain' }
|
21
|
+
[ 200, h, [body] ]
|
22
|
+
end
|
23
|
+
|
24
|
+
def identity_req(bytes, body = true)
|
25
|
+
body_bytes = body ? bytes : 0
|
26
|
+
"PUT / HTTP/1.1\r\nConnection: close\r\nHost: example.com\r\n" \
|
27
|
+
"Content-Length: #{bytes}\r\n\r\n#{'*' * body_bytes}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def mkserver(cfg)
|
31
|
+
fork do
|
32
|
+
srv = Yahns::Server.new(cfg)
|
33
|
+
ENV["YAHNS_FD"] = @srv.fileno.to_s
|
34
|
+
srv.start.join
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_0_lazy; cmbs_test_0(:lazy); end
|
39
|
+
def test_0_true; cmbs_test_0(true); end
|
40
|
+
def test_0_false; cmbs_test_0(false); end
|
41
|
+
|
42
|
+
def cmbs_test_0(btype)
|
43
|
+
err = @err
|
44
|
+
cfg = Yahns::Config.new
|
45
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
46
|
+
cfg.instance_eval do
|
47
|
+
GTL.synchronize {
|
48
|
+
app(:rack, DRAINER) {
|
49
|
+
listen "#{host}:#{port}"
|
50
|
+
input_buffering btype
|
51
|
+
client_max_body_size 0
|
52
|
+
}
|
53
|
+
}
|
54
|
+
logger(Logger.new(err.path))
|
55
|
+
end
|
56
|
+
pid = mkserver(cfg)
|
57
|
+
default_identity_checks(host, port, 0)
|
58
|
+
default_chunked_checks(host, port, 0)
|
59
|
+
ensure
|
60
|
+
quit_wait(pid)
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_cmbs_lazy; cmbs_test(:lazy); end
|
64
|
+
def test_cmbs_true; cmbs_test(true); end
|
65
|
+
def test_cmbs_false; cmbs_test(false); end
|
66
|
+
|
67
|
+
def cmbs_test(btype)
|
68
|
+
err = @err
|
69
|
+
cfg = Yahns::Config.new
|
70
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
71
|
+
cfg.instance_eval do
|
72
|
+
GTL.synchronize {
|
73
|
+
app(:rack, DRAINER) {
|
74
|
+
listen "#{host}:#{port}"
|
75
|
+
input_buffering btype
|
76
|
+
}
|
77
|
+
}
|
78
|
+
logger(Logger.new(err.path))
|
79
|
+
end
|
80
|
+
pid = mkserver(cfg)
|
81
|
+
default_identity_checks(host, port)
|
82
|
+
default_chunked_checks(host, port)
|
83
|
+
ensure
|
84
|
+
quit_wait(pid)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_inf_false; big_test(false); end
|
88
|
+
def test_inf_true; big_test(true); end
|
89
|
+
def test_inf_lazy; big_test(:lazy); end
|
90
|
+
|
91
|
+
def big_test(btype)
|
92
|
+
err = @err
|
93
|
+
cfg = Yahns::Config.new
|
94
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
95
|
+
cfg.instance_eval do
|
96
|
+
GTL.synchronize {
|
97
|
+
app(:rack, DRAINER) {
|
98
|
+
listen "#{host}:#{port}"
|
99
|
+
input_buffering btype
|
100
|
+
client_max_body_size nil
|
101
|
+
}
|
102
|
+
}
|
103
|
+
logger(Logger.new(err.path))
|
104
|
+
end
|
105
|
+
pid = mkserver(cfg)
|
106
|
+
|
107
|
+
bytes = 10 * 1024 * 1024
|
108
|
+
r = `dd if=/dev/zero bs=#{bytes} count=1 2>/dev/null | \
|
109
|
+
curl -sSf -HExpect: -T- http://#{host}:#{port}/`
|
110
|
+
assert $?.success?, $?.inspect
|
111
|
+
assert_equal bytes.to_s, r
|
112
|
+
|
113
|
+
r = `dd if=/dev/zero bs=#{bytes} count=1 2>/dev/null | \
|
114
|
+
curl -sSf -HExpect: -HContent-Length:#{bytes} -HTransfer-Encoding: \
|
115
|
+
-T- http://#{host}:#{port}/`
|
116
|
+
assert $?.success?, $?.inspect
|
117
|
+
assert_equal bytes.to_s, r
|
118
|
+
ensure
|
119
|
+
quit_wait(pid)
|
120
|
+
end
|
121
|
+
|
122
|
+
def default_chunked_checks(host, port, defmax = DEFMBS)
|
123
|
+
r = `curl -sSf -HExpect: -T- </dev/null http://#{host}:#{port}/`
|
124
|
+
assert $?.success?, $?.inspect
|
125
|
+
assert_equal "0", r
|
126
|
+
|
127
|
+
r = `dd if=/dev/zero bs=#{defmax} count=1 2>/dev/null | \
|
128
|
+
curl -sSf -HExpect: -T- http://#{host}:#{port}/`
|
129
|
+
assert $?.success?, $?.inspect
|
130
|
+
assert_equal "#{defmax}", r
|
131
|
+
|
132
|
+
r = `dd if=/dev/zero bs=#{defmax + 1} count=1 2>/dev/null | \
|
133
|
+
curl -sf -HExpect: -T- --write-out %{http_code} \
|
134
|
+
http://#{host}:#{port}/ 2>&1`
|
135
|
+
refute $?.success?, $?.inspect
|
136
|
+
assert_equal "413", r
|
137
|
+
end
|
138
|
+
|
139
|
+
def default_identity_checks(host, port, defmax = DEFMBS)
|
140
|
+
if defmax >= 666
|
141
|
+
c = TCPSocket.new(host, port)
|
142
|
+
c.write(identity_req(666))
|
143
|
+
assert_equal "666", c.read.split(/\r\n\r\n/)[1]
|
144
|
+
c.close
|
145
|
+
end
|
146
|
+
|
147
|
+
c = TCPSocket.new(host, port)
|
148
|
+
c.write(identity_req(0))
|
149
|
+
assert_equal "0", c.read.split(/\r\n\r\n/)[1]
|
150
|
+
c.close
|
151
|
+
|
152
|
+
c = TCPSocket.new(host, port)
|
153
|
+
c.write(identity_req(defmax))
|
154
|
+
assert_equal "#{defmax}", c.read.split(/\r\n\r\n/)[1]
|
155
|
+
c.close
|
156
|
+
|
157
|
+
toobig = defmax + 1
|
158
|
+
c = TCPSocket.new(host, port)
|
159
|
+
c.write(identity_req(toobig, false))
|
160
|
+
assert_match(%r{\AHTTP/1\.[01] 413 }, Timeout.timeout(10) { c.read })
|
161
|
+
c.close
|
162
|
+
end
|
163
|
+
end
|