yahns 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Documentation/.gitignore +5 -0
  4. data/Documentation/GNUmakefile +50 -0
  5. data/Documentation/yahns-rackup.txt +152 -0
  6. data/Documentation/yahns.txt +68 -0
  7. data/Documentation/yahns_config.txt +563 -0
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +14 -7
  10. data/HACKING +56 -0
  11. data/INSTALL +8 -0
  12. data/README +15 -2
  13. data/Rakefile +2 -2
  14. data/bin/yahns +1 -2
  15. data/bin/yahns-rackup +9 -0
  16. data/examples/yahns_multi.conf.rb +14 -4
  17. data/examples/yahns_rack_basic.conf.rb +17 -1
  18. data/extras/README +16 -0
  19. data/extras/autoindex.rb +151 -0
  20. data/extras/exec_cgi.rb +108 -0
  21. data/extras/proxy_pass.rb +210 -0
  22. data/extras/try_gzip_static.rb +208 -0
  23. data/lib/yahns.rb +5 -2
  24. data/lib/yahns/acceptor.rb +64 -22
  25. data/lib/yahns/cap_input.rb +2 -2
  26. data/lib/yahns/{client_expire_portable.rb → client_expire_generic.rb} +12 -11
  27. data/lib/yahns/{client_expire.rb → client_expire_tcpi.rb} +7 -6
  28. data/lib/yahns/config.rb +107 -22
  29. data/lib/yahns/daemon.rb +2 -0
  30. data/lib/yahns/fdmap.rb +28 -9
  31. data/lib/yahns/http_client.rb +123 -37
  32. data/lib/yahns/http_context.rb +21 -3
  33. data/lib/yahns/http_response.rb +80 -19
  34. data/lib/yahns/log.rb +23 -4
  35. data/lib/yahns/queue_epoll.rb +20 -9
  36. data/lib/yahns/queue_quitter.rb +16 -0
  37. data/lib/yahns/queue_quitter_pipe.rb +24 -0
  38. data/lib/yahns/rack.rb +0 -1
  39. data/lib/yahns/rackup_handler.rb +57 -0
  40. data/lib/yahns/server.rb +189 -59
  41. data/lib/yahns/server_mp.rb +43 -35
  42. data/lib/yahns/sigevent_pipe.rb +1 -0
  43. data/lib/yahns/socket_helper.rb +37 -11
  44. data/lib/yahns/stream_file.rb +14 -4
  45. data/lib/yahns/stream_input.rb +13 -7
  46. data/lib/yahns/tcp_server.rb +7 -0
  47. data/lib/yahns/tmpio.rb +10 -3
  48. data/lib/yahns/unix_server.rb +7 -0
  49. data/lib/yahns/wbuf.rb +19 -2
  50. data/lib/yahns/wbuf_common.rb +10 -3
  51. data/lib/yahns/wbuf_str.rb +24 -0
  52. data/lib/yahns/worker.rb +5 -26
  53. data/test/helper.rb +15 -5
  54. data/test/server_helper.rb +37 -1
  55. data/test/test_bin.rb +17 -8
  56. data/test/test_buffer_tmpdir.rb +103 -0
  57. data/test/test_client_expire.rb +71 -35
  58. data/test/test_client_max_body_size.rb +5 -13
  59. data/test/test_config.rb +1 -1
  60. data/test/test_expect_100.rb +176 -0
  61. data/test/test_extras_autoindex.rb +53 -0
  62. data/test/test_extras_exec_cgi.rb +81 -0
  63. data/test/test_extras_exec_cgi.sh +35 -0
  64. data/test/test_extras_try_gzip_static.rb +177 -0
  65. data/test/test_input.rb +128 -0
  66. data/test/test_mt_accept.rb +48 -0
  67. data/test/test_output_buffering.rb +90 -63
  68. data/test/test_rack.rb +1 -1
  69. data/test/test_rack_hijack.rb +2 -6
  70. data/test/test_reopen_logs.rb +2 -8
  71. data/test/test_serve_static.rb +104 -8
  72. data/test/test_server.rb +448 -73
  73. data/test/test_stream_file.rb +1 -1
  74. data/test/test_unix_socket.rb +72 -0
  75. data/test/test_wbuf.rb +20 -17
  76. data/yahns.gemspec +3 -0
  77. metadata +57 -5
@@ -0,0 +1,24 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> et. al.
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ require_relative 'wbuf_common'
5
+
6
+ class Yahns::WbufStr # :nodoc:
7
+ include Yahns::WbufCommon
8
+
9
+ def initialize(str, next_state)
10
+ @str = str
11
+ @next = next_state # :check_client_connection, :http_100_response
12
+ end
13
+
14
+ def wbuf_flush(client)
15
+ case rv = client.kgio_trywrite(@str)
16
+ when String
17
+ @str = rv
18
+ when :wait_writable, :wait_readable
19
+ return rv
20
+ when nil
21
+ return @next
22
+ end while true
23
+ end
24
+ end
data/lib/yahns/worker.rb CHANGED
@@ -23,36 +23,15 @@ class Yahns::Worker # :nodoc:
23
23
  # This causes the worker to gracefully exit if the master
24
24
  # dies unexpectedly.
25
25
  def yahns_step
26
- @to_io.kgio_tryread(11) == nil and Process.kill(:QUIT, $$)
27
- :wait_readable
26
+ if @to_io.kgio_tryread(11) == nil
27
+ Process.kill(:QUIT, $$)
28
+ @to_io.close
29
+ end
30
+ :ignore
28
31
  end
29
32
 
30
33
  # worker objects may be compared to just plain Integers
31
34
  def ==(other_nr) # :nodoc:
32
35
  @nr == other_nr
33
36
  end
34
-
35
- # Changes the worker process to the specified +user+ and +group+
36
- # This is only intended to be called from within the worker
37
- # process from the +after_fork+ hook. This should be called in
38
- # the +after_fork+ hook after any privileged functions need to be
39
- # run (e.g. to set per-worker CPU affinity, niceness, etc)
40
- #
41
- # Any and all errors raised within this method will be propagated
42
- # directly back to the caller (usually the +after_fork+ hook.
43
- # These errors commonly include ArgumentError for specifying an
44
- # invalid user/group and Errno::EPERM for insufficient privileges
45
- def user(user, group = nil)
46
- # we do not protect the caller, checking Process.euid == 0 is
47
- # insufficient because modern systems have fine-grained
48
- # capabilities. Let the caller handle any and all errors.
49
- uid = Etc.getpwnam(user).uid
50
- gid = Etc.getgrnam(group).gid if group
51
- Yahns::Log.chown_all(uid, gid)
52
- if gid && Process.egid != gid
53
- Process.initgroups(user, gid)
54
- Process::GID.change_privilege(gid)
55
- end
56
- Process.euid != uid and Process::UID.change_privilege(uid)
57
- end
58
37
  end
data/test/helper.rb CHANGED
@@ -3,6 +3,11 @@
3
3
  $stdout.sync = $stderr.sync = Thread.abort_on_exception = true
4
4
  $-w = true if RUBY_VERSION.to_f >= 2.0
5
5
  require 'thread'
6
+ require 'fileutils'
7
+
8
+ def rubyv
9
+ puts RUBY_DESCRIPTION
10
+ end
6
11
 
7
12
  # Global Test Lock, to protect:
8
13
  # Process.wait*, Dir.chdir, ENV, trap, require, etc...
@@ -11,13 +16,14 @@ GTL = Mutex.new
11
16
  # fork-aware coverage data gatherer, see also test/covshow.rb
12
17
  if ENV["COVERAGE"]
13
18
  require "coverage"
14
- COVMATCH = %r{/lib/yahns\b.*rb\z}
19
+ COVMATCH = %r{(/lib/yahns\b|extras/).*rb\z}
20
+ COVDUMPFILE = File.expand_path("coverage.dump")
15
21
 
16
22
  def __covmerge
17
23
  res = Coverage.result
18
24
 
19
- # do not create the file, Makefile does htis before any tests run
20
- File.open("coverage.dump", IO::RDWR) do |covtmp|
25
+ # do not create the file, Makefile does this before any tests run
26
+ File.open(COVDUMPFILE, IO::RDWR) do |covtmp|
21
27
  covtmp.binmode
22
28
  covtmp.sync = true
23
29
 
@@ -69,20 +75,20 @@ at_exit do
69
75
  # skipping @@after_run stuff in minitest since we don't need it
70
76
  case $!
71
77
  when nil, SystemExit
72
- mtobj.run(ARGV) if $$ == TSTART_PID
78
+ exit(mtobj.run(ARGV)) if $$ == TSTART_PID
73
79
  end
74
80
  end
75
81
 
76
82
  require "tempfile"
77
83
  require 'tmpdir'
78
84
  class Dir
79
- require 'fileutils'
80
85
  def Dir.mktmpdir
81
86
  begin
82
87
  d = "#{Dir.tmpdir}/#$$.#{rand}"
83
88
  Dir.mkdir(d)
84
89
  rescue Errno::EEXIST
85
90
  end while true
91
+ return d unless block_given?
86
92
  begin
87
93
  yield d
88
94
  ensure
@@ -109,6 +115,10 @@ class IO
109
115
  end
110
116
  end if ! IO.method_defined?(:nread) && RUBY_PLATFORM =~ /linux/
111
117
 
118
+ def cloexec_pipe
119
+ IO.pipe.each { |io| io.close_on_exec = true }
120
+ end
121
+
112
122
  require 'yahns'
113
123
 
114
124
  # needed for parallel (MT) tests)
@@ -29,10 +29,16 @@ module ServerHelper
29
29
 
30
30
  def quit_wait(pid)
31
31
  pid or return
32
+ err = $!
32
33
  Process.kill(:QUIT, pid)
33
34
  _, status = Timeout.timeout(10) { Process.waitpid2(pid) }
34
35
  assert status.success?, status.inspect
35
- rescue Timeout::Error
36
+ rescue Timeout::Error => tout
37
+ err ||= tout
38
+ begin
39
+ warn "#{err.message} (#{err.class})"
40
+ err.backtrace.each { |l| warn l }
41
+ end
36
42
  if RUBY_PLATFORM =~ /linux/
37
43
  system("lsof -p #{pid}")
38
44
  warn "#{pid} failed to die, waiting for user to inspect"
@@ -45,6 +51,7 @@ module ServerHelper
45
51
  def get_tcp_client(host, port, tries = 500)
46
52
  begin
47
53
  c = TCPSocket.new(host, port)
54
+ c.close_on_exec = true
48
55
  return c
49
56
  rescue Errno::ECONNREFUSED
50
57
  raise if tries < 0
@@ -63,4 +70,33 @@ module ServerHelper
63
70
  @err = tmpfile(%w(srv .err))
64
71
  @ru = nil
65
72
  end
73
+
74
+ def mkserver(cfg)
75
+ fork do
76
+ ENV["YAHNS_FD"] = @srv.fileno.to_s
77
+ yield if block_given?
78
+ Yahns::Server.new(cfg).start.join
79
+ end
80
+ end
81
+
82
+ def wait_for_full(c)
83
+ prev = 0
84
+ prev_time = Time.now
85
+ begin
86
+ nr = c.nread
87
+ break if nr > 0 && nr == prev && (Time.now - prev_time) > 0.5
88
+ if nr != prev
89
+ prev = nr
90
+ prev_time = Time.now
91
+ end
92
+ Thread.pass
93
+ end while sleep(0.1)
94
+ end
95
+ end
96
+
97
+ module TrywriteBlocked
98
+ def kgio_trywrite(*args)
99
+ return :wait_writable if $_tw_block_on.include?($_tw_blocked += 1)
100
+ super
101
+ end
66
102
  end
data/test/test_bin.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
3
3
  require_relative 'server_helper'
4
4
  class TestBin < Testcase
5
- parallelize_me!
5
+ ENV["N"].to_i > 1 and parallelize_me!
6
6
  include ServerHelper
7
7
  alias teardown server_helper_teardown
8
8
 
@@ -40,20 +40,32 @@ class TestBin < Testcase
40
40
  cfg.puts " listen ENV['YAHNS_TEST_LISTEN']"
41
41
  cfg.puts "end"
42
42
  @cmd.concat(%W(-D -c #{cfg.path}))
43
- addr = IO.pipe
43
+ addr = cloexec_pipe
44
44
  pid = fork do
45
+ opts = { close_others: true }
46
+ addr[0].close
45
47
  if inherit
46
- @cmd << { @srv.fileno => @srv }
48
+ opts[@srv.fileno] = @srv
47
49
  ENV["YAHNS_FD"] = @srv.fileno.to_s
48
50
  else
51
+ # we must create the socket inside the child and tell the parent
52
+ # about it to avoid sharing
49
53
  @srv = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
50
- @srv.close_on_exec = true # needed for 1.9.3
51
54
  end
55
+ @cmd << opts
52
56
  host, port = @srv.addr[3], @srv.addr[1]
53
57
  listen = ENV["YAHNS_TEST_LISTEN"] = "#{host}:#{port}"
54
58
  addr[1].write(listen)
55
59
  addr[1].close
56
- addr[0].close
60
+
61
+ # close/FD_CLOEXEC may be insufficient since the socket could be
62
+ # released asynchronously, leading to occasional test failures.
63
+ # Even with a synchronous FD_CLOEXEC, there's a chance of a race
64
+ # because the server does not bind right away.
65
+ unless inherit
66
+ @srv.shutdown
67
+ @srv.close
68
+ end
57
69
  exec(*@cmd)
58
70
  end
59
71
  addr[1].close
@@ -177,9 +189,6 @@ class TestBin < Testcase
177
189
  else
178
190
  poke_until_dead newpid
179
191
  end
180
- rescue => e
181
- Yahns::Log.exception(Logger.new($stderr), "test", e)
182
- raise
183
192
  ensure
184
193
  File.unlink(exe) if exe
185
194
  cfg.close! if cfg
@@ -0,0 +1,103 @@
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
+ require 'sleepy_penguin'
5
+
6
+ class TestBufferTmpdir < Testcase
7
+ ENV["N"].to_i > 1 and parallelize_me!
8
+ include ServerHelper
9
+ attr_reader :ino, :tmpdir
10
+
11
+ def setup
12
+ @ino = SleepyPenguin::Inotify.new(:CLOEXEC)
13
+ @tmpdir = Dir.mktmpdir
14
+ server_helper_setup
15
+ end
16
+
17
+ def teardown
18
+ server_helper_teardown
19
+ @ino.close
20
+ FileUtils.rm_rf @tmpdir
21
+ end
22
+
23
+ class GiantBody
24
+ # just spew until the client gives up
25
+ def each
26
+ nr = 16384
27
+ buf = "#{nr.to_s(16)}\r\n#{("!" * nr)}\r\n"
28
+ loop do
29
+ yield buf
30
+ end
31
+ end
32
+ end
33
+
34
+ def test_output_buffer_tmpdir
35
+ opts = { tmpdir: @tmpdir }
36
+ err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
37
+ pid = mkserver(cfg) do
38
+ cfg.instance_eval do
39
+ ru = lambda { |e|
40
+ h = {
41
+ "Transfer-Encoding" => "chunked",
42
+ "Content-Type" => "text/plain"
43
+ }
44
+ [ 200, h, GiantBody.new ]
45
+ }
46
+ app(:rack, ru) do
47
+ listen "#{host}:#{port}"
48
+ output_buffering true, opts
49
+ end
50
+ stderr_path err.path
51
+ end
52
+ end
53
+ @ino.add_watch @tmpdir, [:CREATE, :DELETE]
54
+ c = get_tcp_client(host, port)
55
+ c.write "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
56
+ Timeout.timeout(30) do
57
+ event = @ino.take
58
+ assert_equal [:CREATE], event.events
59
+ name = event.name
60
+ event = @ino.take
61
+ assert_equal [:DELETE], event.events
62
+ assert_equal name, event.name
63
+ refute File.exist?("#@tmpdir/#{name}")
64
+ end
65
+ ensure
66
+ c.close if c
67
+ quit_wait(pid)
68
+ end
69
+
70
+ def test_input_buffer_lazy; input_buffer(:lazy); end
71
+ def test_input_buffer_true; input_buffer(true); end
72
+
73
+ def input_buffer(btype)
74
+ opts = { tmpdir: @tmpdir }
75
+ err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
76
+ pid = mkserver(cfg) do
77
+ cfg.instance_eval do
78
+ require 'rack/lobster'
79
+ app(:rack, Rack::Lobster.new) do
80
+ listen "#{host}:#{port}"
81
+ input_buffering btype, opts
82
+ end
83
+ stderr_path err.path
84
+ end
85
+ end
86
+ @ino.add_watch tmpdir, [:CREATE, :DELETE]
87
+ c = get_tcp_client(host, port)
88
+ nr = 16384 # must be > client_body_buffer_size
89
+ c.write "POST / HTTP/1.0\r\nContent-Length: #{nr}\r\n\r\n"
90
+ Timeout.timeout(30) do
91
+ event = ino.take
92
+ assert_equal [:CREATE], event.events
93
+ name = event.name
94
+ event = ino.take
95
+ assert_equal [:DELETE], event.events
96
+ assert_equal name, event.name
97
+ refute File.exist?("#{tmpdir}/#{name}")
98
+ end
99
+ ensure
100
+ c.close if c
101
+ quit_wait(pid)
102
+ end
103
+ end if SleepyPenguin.const_defined?(:Inotify)
@@ -3,7 +3,7 @@
3
3
  require_relative 'server_helper'
4
4
 
5
5
  class TestClientExpire < Testcase
6
- parallelize_me!
6
+ ENV["N"].to_i > 1 and parallelize_me!
7
7
  include ServerHelper
8
8
  alias setup server_helper_setup
9
9
  alias teardown server_helper_teardown
@@ -22,11 +22,7 @@ class TestClientExpire < Testcase
22
22
  end
23
23
  logger(Logger.new(err.path))
24
24
  end
25
- srv = Yahns::Server.new(cfg)
26
- pid = fork do
27
- ENV["YAHNS_FD"] = @srv.fileno.to_s
28
- srv.start.join
29
- end
25
+ pid = mkserver(cfg)
30
26
  Net::HTTP.start(host, port) { |h|
31
27
  res = h.get("/")
32
28
  assert_empty res.body
@@ -51,16 +47,13 @@ class TestClientExpire < Testcase
51
47
  end
52
48
  logger(Logger.new(err.path))
53
49
  end
54
- srv = Yahns::Server.new(cfg)
55
- pid = fork do
56
- ENV["YAHNS_FD"] = @srv.fileno.to_s
57
- srv.start.join
58
- end
59
- f = TCPSocket.new(host, port)
60
- s = TCPSocket.new(host, port)
50
+ pid = mkserver(cfg)
51
+ f = get_tcp_client(host, port)
52
+ s = get_tcp_client(host, port)
61
53
  req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
62
54
  s.write(req)
63
- str = Timeout.timeout(20) { s.readpartial(666) }
55
+ str = ""
56
+ Timeout.timeout(20) { str << s.readpartial(666) until str =~ /\r\n\r\n/ }
64
57
  assert_match(%r{keep-alive}, str)
65
58
  sleep 2
66
59
  abe = tmpfile(%w(abe .err))
@@ -75,57 +68,100 @@ class TestClientExpire < Testcase
75
68
  end
76
69
  io.close
77
70
  end
78
- rescue => e
79
- Yahns::Log.exception(Logger.new($stderr), "test", e)
80
- raise
81
71
  ensure
82
72
  quit_wait(pid)
83
73
  end
84
74
 
75
+ # test EMFILE handling
85
76
  def test_client_expire_desperate
86
- skip "disabled since FD counts vary heavily in an MT process"
87
77
  err = @err
88
78
  cfg = Yahns::Config.new
89
79
  host, port = @srv.addr[3], @srv.addr[1]
90
80
  cfg.instance_eval do
91
81
  GTL.synchronize do
92
82
  h = { "Content-Length" => "0" }
93
- app(:rack, lambda { |e| [ 200, h, [] ]}) do
83
+ queue { worker_threads 1 }
84
+ ru = lambda { |e|
85
+ sleep(0.01) unless e["PATH_INFO"] == "/_"
86
+ [ 200, h, [] ]
87
+ }
88
+ app(:rack, ru) do
94
89
  listen "#{host}:#{port}", sndbuf: 2048, rcvbuf: 2048
95
- client_timeout 1
90
+ client_timeout 1.0
91
+ # FIXME: wbuf creation does not recover from EMFILE/ENFILE
92
+ output_buffering false
93
+ check_client_connection true
96
94
  end
97
95
  client_expire_threshold 1.0
98
96
  end
99
- logger(Logger.new(err.path))
97
+ stderr_path err.path
100
98
  end
101
- srv = Yahns::Server.new(cfg)
102
- pid = fork do
103
- Process.setrlimit :NOFILE, 512, 1024
104
- ENV["YAHNS_FD"] = @srv.fileno.to_s
105
- srv.start.join
99
+ pid = mkserver(cfg) do
100
+ keep = { $stderr => true, $stdout => true, $stdin => true, @srv => true }
101
+ ObjectSpace.each_object(IO) do |obj|
102
+ next if keep[obj]
103
+ begin
104
+ obj.close unless obj.closed?
105
+ rescue IOError # could be uninitialized
106
+ end
107
+ end
106
108
  end
107
- f = TCPSocket.new(host, port)
108
- s = TCPSocket.new(host, port)
109
+ f = get_tcp_client(host, port)
110
+ f.write "G"
111
+ s = get_tcp_client(host, port)
109
112
  req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
110
113
  s.write(req)
111
- str = Timeout.timeout(20) { s.readpartial(666) }
114
+ str = ""
115
+ Timeout.timeout(20) { str << s.readpartial(666) until str =~ /\r\n\r\n/ }
112
116
  assert_match(%r{keep-alive}, str)
113
- sleep 3
114
- system "ab -c 1000 -n 10000 -k http://#{host}:#{port}/ 2>&1"
117
+ sleep 1
115
118
 
119
+ # ignore errors, just beat the crap out of the process
120
+ nr = Process.getrlimit(:NOFILE)[0] # 1024 is common
121
+ assert_operator nr, :>, 666, "increase RLIM_NOFILE (ulimit -n)"
122
+ nr -= 50
123
+ opts = { out: "/dev/null", err: "/dev/null", close_others: true }
124
+ begin
125
+ pids = 2.times.map do
126
+ fork do
127
+ exec(*%W(ab -c #{nr} -n 9999999 -v1 -k http://#{host}:#{port}/), opts)
128
+ end
129
+ end
130
+
131
+ re1 = %r{consider raising open file limits}
132
+ re2 = %r{dropping (\d+) of \d+ clients for timeout=\d+}
133
+ Timeout.timeout(30) do
134
+ n = 0
135
+ begin
136
+ buf = File.read(err.path)
137
+ if buf =~ re1 && buf =~ re2
138
+ n += $1.to_i
139
+ break if n >= 2
140
+ end
141
+ end while sleep(0.01)
142
+ end
143
+ ensure
144
+ # don't care for ab errors, they're likely
145
+ pids.each do |_pid|
146
+ Process.kill(:KILL, _pid)
147
+ Process.waitpid2(_pid)
148
+ end
149
+ end
116
150
  [ f, s ].each do |io|
117
151
  assert_raises(Errno::EPIPE,Errno::ECONNRESET) do
118
152
  req.each_byte { |b| io.write(b.chr) }
119
153
  end
120
154
  io.close
121
155
  end
156
+
157
+ # make sure the server still works
158
+ res = Net::HTTP.start(host, port) { |h| h.get("/_") }
159
+ assert_equal 200, res.code.to_i
160
+
122
161
  errs = File.readlines(err.path).grep(/ERROR/)
123
162
  File.truncate(err.path, 0) # avoid error on teardown
124
- re = %r{consider raising open file limits} # - accept, consider raising open file limits}
163
+ re = %r{consider raising open file limits}
125
164
  assert_equal errs.grep(re), errs
126
- rescue => e
127
- Yahns::Log.exception(Logger.new($stderr), "test", e)
128
- raise
129
165
  ensure
130
166
  quit_wait(pid)
131
167
  end