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
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
|