yahns 1.15.0 → 1.16.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7e2b90d1e9db03563036b8fa9d20f734eadbc66b
4
- data.tar.gz: da4bfdde14dc21244e1ce59e5696e56bb23d7327
2
+ SHA256:
3
+ metadata.gz: f5dcbf514b3bf6c9e92c8f0ca7d038019148217ffe6f3a88ab6ea7810333487e
4
+ data.tar.gz: 4bfd3e6b7a2806daf1e53094050d928a9c28cd1b330ef594a118c2b6bd9d7c86
5
5
  SHA512:
6
- metadata.gz: b7f796332d347e087508c0ddd7fbba45567e2c51fdc1b9718b911ca03b058c111131e399f0779c8c28abb950197a3cc7e057e5fcee1577c9e32367173486188a
7
- data.tar.gz: b12c67cec9e1d9b6f752dc0282d0de9609e72074f2210bc54b68e4b6e47447ae238887aa6a87fcde6d95912ff86f54f9a9617c3d0596411fdba4101b408715cb
6
+ metadata.gz: 7f8123dcd0921c5c224bdb76009c7d7b1e751da62e2f30690ab438f959e234710afdb1f1b57ecfe64dc8b437ac782f95b69348c7177ecfe1e302173bc494574a
7
+ data.tar.gz: 7f0e74b4271f4ec44bd2b800b0eb6fa4a570ecd538cf42f79c31ddf92c2552d98bade3a9396d8fd02ba9e16a2f145eb2a028b9050b01e931f90f42adcbf1767a
@@ -40,6 +40,10 @@ and PATH is meant to be a path to a UNIX domain socket.
40
40
  Defaults to "0.0.0.0:9292" (all addresses on TCP port 9292).
41
41
  Multiple addresses may be separated with commas.
42
42
 
43
+ For systemd users, a special value of "inherit" may be specified
44
+ to inherit FDs using the LISTEN_FDS and LISTEN_PID environment
45
+ variables described in L<sd_listen_fds(3)>
46
+
43
47
  =item -O stderr_path=PATHNAME
44
48
 
45
49
  Allow redirecting $stderr to a given path. Unlike doing this from
@@ -656,7 +656,7 @@ compatibility.
656
656
 
657
657
  See the examples/ directory in the git source tree.
658
658
 
659
- git clone git://yhbt.net/yahns.git
659
+ git clone https://yhbt.net/yahns.git
660
660
 
661
661
  =head1 CONTACT
662
662
 
@@ -5,7 +5,7 @@
5
5
  CONSTANT = "Yahns::VERSION"
6
6
  RVF = "lib/yahns/version.rb"
7
7
  GVF = "GIT-VERSION-FILE"
8
- DEF_VER = "v1.15.0"
8
+ DEF_VER = "v1.16.0"
9
9
  vn = DEF_VER.dup
10
10
 
11
11
  # First see if there is a version file (included in release tarballs),
data/HACKING CHANGED
@@ -9,7 +9,7 @@ development dependencies
9
9
  * git - https://www.git-scm.com/
10
10
  * ruby - https://www.ruby-lang.org/en/
11
11
 
12
- git clone git://yhbt.net/yahns
12
+ git clone https://yhbt.net/yahns
13
13
 
14
14
  tests
15
15
  -----
data/README CHANGED
@@ -85,7 +85,7 @@ Hacking
85
85
  We use git and follow the same development model as git itself
86
86
  (mailing list-oriented, benevolent dictator).
87
87
 
88
- git clone git://yhbt.net/yahns
88
+ git clone https://yhbt.net/yahns
89
89
 
90
90
  Please use git-format-patch(1) and git-send-email(1) distributed with
91
91
  the git(7) suite for generating and sending patches. Please format
@@ -56,7 +56,7 @@ def call(env)
56
56
  case env["REQUEST_METHOD"]
57
57
  when "GET", "HEAD"
58
58
  # try to serve the static file, first
59
- status, headers, body = res = @app.call(env)
59
+ status, _, body = res = @app.call(env)
60
60
  return res if status.to_i != 404
61
61
 
62
62
  path_info = env["PATH_INFO"]
@@ -78,7 +78,7 @@ def call(env)
78
78
  tryenv = env.dup
79
79
  @index.each do |base|
80
80
  tryenv["PATH_INFO"] = "#{path_info}#{base}"
81
- status, headers, body = res = @app.call(tryenv)
81
+ status, _, body = res = @app.call(tryenv)
82
82
  return res if status.to_i != 404
83
83
  end
84
84
 
@@ -24,7 +24,7 @@ def ac_quit
24
24
  close
25
25
  return true
26
26
  end
27
- @thrs.each { |t| t[:yahns_quit] = true }
27
+ @quit = true
28
28
  return true if __ac_quit_done?
29
29
 
30
30
  @thrs.each do
@@ -42,10 +42,10 @@ def ac_quit
42
42
  end
43
43
 
44
44
  def spawn_acceptor(nr, logger, client_class)
45
+ @quit = false
45
46
  @thrs = nr.times.map do
46
47
  Thread.new do
47
48
  queue = client_class.queue
48
- t = Thread.current
49
49
  accept_flags = Kgio::SOCK_NONBLOCK | Kgio::SOCK_CLOEXEC
50
50
  qev_flags = client_class.superclass::QEV_FLAGS
51
51
  begin
@@ -64,7 +64,7 @@ def spawn_acceptor(nr, logger, client_class)
64
64
  sleep 1 # let other threads do some work
65
65
  rescue => e
66
66
  Yahns::Log.exception(logger, "accept loop", e)
67
- end until t[:yahns_quit]
67
+ end until @quit
68
68
  end
69
69
  end
70
70
  end
@@ -328,8 +328,8 @@ def app(type, *args, &block)
328
328
  "#{var}: #{file} did not register #{type} in #{self.class}::APP_CLASS"
329
329
 
330
330
  # apps may have multiple configurator contexts
331
- app = @app_instances[klass.instance_key(*args)] = klass.new(*args)
332
- ctx = app.config_context
331
+ app_cfg = @app_instances[klass.instance_key(*args)] = klass.new(*args)
332
+ ctx = app_cfg.config_context
333
333
  if block_given?
334
334
  @block = CfgBlock.new(:app, ctx)
335
335
  instance_eval(&block)
@@ -438,4 +438,9 @@ def commit!(server)
438
438
 
439
439
  @app_ctx.each { |app| app.logger ||= server.logger }
440
440
  end
441
+
442
+ def register_inherited(name)
443
+ return unless @config_listeners.empty? && @app_ctx.size == 1
444
+ @config_listeners[name] = { :yahns_app_ctx => @app_ctx[0] }
445
+ end
441
446
  end
@@ -298,10 +298,18 @@ def handle_error(e)
298
298
  when Unicorn::HttpParserError # try to tell the client they're bad
299
299
  400
300
300
  else
301
+ n = 500
302
+ case e.class.to_s
303
+ when 'OpenSSL::SSL::SSLError'
304
+ if e.message.include?('wrong version number')
305
+ n = nil
306
+ e.set_backtrace([])
307
+ end
308
+ end
301
309
  Yahns::Log.exception(@hs.env["rack.logger"], "app error", e)
302
- 500
310
+ n
303
311
  end
304
- kgio_trywrite(err_response(code))
312
+ kgio_trywrite(err_response(code)) if code
305
313
  rescue
306
314
  ensure
307
315
  shutdown rescue nil
@@ -314,12 +322,23 @@ def app_hijacked?(env, res)
314
322
  true
315
323
  end
316
324
 
317
- def trysendio(io, offset, count)
318
- return 0 if count == 0
325
+ def do_pread(io, count, offset)
319
326
  count = 0x4000 if count > 0x4000
320
327
  buf = Thread.current[:yahns_sfbuf] ||= ''.dup
321
- io.pos = offset
322
- str = io.read(count, buf) or return # nil for EOF
328
+ if io.respond_to?(:pread)
329
+ io.pread(count, offset, buf)
330
+ else
331
+ io.pos = offset
332
+ io.read(count, buf)
333
+ end
334
+ rescue EOFError
335
+ warn "BUG: do_pread overreach:\n #{caller.join("\n ")}\n"
336
+ nil
337
+ end
338
+
339
+ def trysendio(io, offset, count)
340
+ return 0 if count == 0
341
+ str = do_pread(io, count, offset) or return # nil for EOF
323
342
  n = 0
324
343
  case rv = kgio_trywrite(str)
325
344
  when String # partial write, keep trying
@@ -40,15 +40,31 @@ def yahns_init_ssl(ssl_ctx)
40
40
  def kgio_trywrite(buf)
41
41
  len = buf.bytesize
42
42
  return if len == 0
43
- buf = @ssl_blocked = buf.dup
43
+
44
+ case @ssl_blocked
45
+ when nil # likely
46
+ buf = @ssl_blocked = buf.dup
47
+ when Exception
48
+ raise @ssl_blocked
49
+ when String
50
+ if @ssl_blocked != buf
51
+ pfx = object_id
52
+ warn("#{pfx} BUG: ssl_blocked != buf\n" \
53
+ "#{pfx} ssl_blocked=#{@ssl_blocked.inspect}\n" \
54
+ "#{pfx} buf=#{buf.inspect}\n")
55
+ raise 'BUG: ssl_blocked} != buf'
56
+ end
57
+ end
58
+
44
59
  case rv = @ssl.write_nonblock(buf, exception: false)
45
60
  when :wait_readable, :wait_writable
46
- return rv # do not clear ssl_blocked
61
+ rv # do not clear ssl_blocked
47
62
  when Integer
48
- rv = len == rv ? nil : buf.byteslice(rv, len - rv)
63
+ @ssl_blocked = len == rv ? nil : buf.byteslice(rv, len - rv)
49
64
  end
50
- @ssl_blocked = nil
51
- rv
65
+ rescue SystemCallError => e # ECONNRESET/EPIPE
66
+ e.set_backtrace([])
67
+ raise(@ssl_blocked = e)
52
68
  end
53
69
 
54
70
  def kgio_trywritev(buf)
@@ -75,22 +91,28 @@ def kgio_tryread(len, buf)
75
91
  def trysendio(io, offset, count)
76
92
  return 0 if count == 0
77
93
 
78
- unless buf = @ssl_blocked
79
- count = 0x4000 if count > 0x4000
80
- buf = Thread.current[:yahns_sfbuf] ||= ''.dup
81
- io.pos = offset
82
- buf = io.read(count, buf) or return # nil for EOF
94
+ case buf = @ssl_blocked
95
+ when nil
96
+ buf = do_pread(io, count, offset) or return # nil for EOF
83
97
  buf = @ssl_blocked = buf.dup
98
+ when Exception
99
+ raise buf
100
+ # when String # just use it as-is
84
101
  end
85
102
 
86
103
  # call write_nonblock directly since kgio_trywrite allocates
87
104
  # an unnecessary string
105
+ len = buf.size
88
106
  case rv = @ssl.write_nonblock(buf, exception: false)
89
107
  when :wait_readable, :wait_writable
90
108
  return rv # do not clear ssl_blocked
109
+ when Integer
110
+ @ssl_blocked = len == rv ? nil : buf.byteslice(rv, len - rv)
91
111
  end
92
- @ssl_blocked = nil
93
112
  rv
113
+ rescue SystemCallError => e # ECONNRESET/EPIPE
114
+ e.set_backtrace([])
115
+ raise(@ssl_blocked = e)
94
116
  end
95
117
 
96
118
  def shutdown # we never call this with a how=SHUT_* arg
@@ -57,7 +57,7 @@ def init_path_vars(path)
57
57
  def call(env)
58
58
  # 3-way handshake for TCP backends while we generate the request header
59
59
  rr = Yahns::ReqRes.start(@sockaddr)
60
- c = env['rack.hijack'].call
60
+ c = env['rack.hijack'].call # Yahns::HttpClient#call
61
61
 
62
62
  req = Rack::Request.new(env)
63
63
  req = @path.gsub(/\$(\w+)/) { req.__send__($1) }
@@ -16,14 +16,10 @@ def self.run(app, o)
16
16
  # fine for most apps, but we have SIGUSR2 restarts to support
17
17
  working_directory(Yahns::START[:cwd])
18
18
 
19
- app(:rack, app) do
19
+ app(:rack, app) do # Yahns::Config#app
20
20
  addr = o[:listen] || "#{o[:Host]||default_host}:#{o[:Port]||8080}"
21
- # allow listening to multiple addresses
22
- if addr.include?(',')
23
- addr.split(',').each { |l| listen(l) }
24
- else
25
- listen addr
26
- end
21
+ # allow listening to multiple addresses (Yahns::Config#listen)
22
+ addr.split(',').each { |l| listen(l) } unless addr == 'inherit'
27
23
 
28
24
  val = o[:client_timeout] and client_timeout(val)
29
25
  end
@@ -237,24 +237,23 @@ def reexec
237
237
  end
238
238
  end
239
239
 
240
- # We cannot use Process.spawn here because of redirects + close-on-exec
241
- # We must keep close_on_exec=true in the parent process and only set
242
- # close_on_exec=false in the child. There must be no opportunity
243
- # for the user app to ever get a listen socket with close_on_exec=false
244
- @reexec_pid = fork do
245
- redirects = {}
246
- @listeners.each do |sock|
247
- sock.close_on_exec = false
248
- redirects[sock.fileno] = sock
240
+ opt = {}
241
+ @listeners.each { |sock| opt[sock.fileno] = sock }
242
+ env = ENV.to_hash
243
+ env['YAHNS_FD'] = opt.keys.join(',')
244
+ opt[:close_others] = true
245
+ cmd = [ Yahns::START[0] ].concat(Yahns::START[:argv])
246
+ dir = @config.value(:working_directory) || Yahns::START[:cwd]
247
+ @logger.info "spawning #{cmd.inspect} (in #{dir})"
248
+ @reexec_pid = if @before_exec
249
+ fork do
250
+ Dir.chdir(dir)
251
+ @before_exec.call(cmd)
252
+ exec(env, *cmd, opt)
249
253
  end
250
- ENV['YAHNS_FD'] = redirects.keys.join(',')
251
- redirects[:close_others] = true
252
- Dir.chdir(@config.value(:working_directory) || Yahns::START[:cwd])
253
- cmd = [ Yahns::START[0] ].concat(Yahns::START[:argv])
254
- @logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
255
- @before_exec.call(cmd) if @before_exec
256
- cmd << redirects
257
- exec(*cmd)
254
+ else
255
+ opt[:chdir] = dir
256
+ spawn(env, *cmd, opt)
258
257
  end
259
258
  end
260
259
 
@@ -328,7 +327,9 @@ def inherit_listeners!
328
327
  opts = sock_opts(io)
329
328
  io = server_cast(io, opts)
330
329
  set_server_sockopt(io, opts)
331
- @logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
330
+ name = sock_name(io)
331
+ @logger.info "inherited addr=#{name} fd=#{io.fileno}"
332
+ @config.register_inherited(name)
332
333
  io
333
334
  end
334
335
 
@@ -395,7 +396,9 @@ def fdmap_init
395
396
 
396
397
  # call OpenSSL::SSL::SSLContext#setup explicitly here to detect
397
398
  # errors and avoid race conditions. We avoid calling this in the
398
- # parent process since
399
+ # parent process (if we have multiple workers) in case the
400
+ # setup code starts TCP connections to memcached or similar
401
+ # for session caching.
399
402
  ssl_ctx.setup
400
403
  end
401
404
  ctx_list << ctx
@@ -451,7 +454,7 @@ def quit_finish
451
454
  @queues.each(&:close).clear
452
455
 
453
456
  # we must not let quitter get GC-ed if we have any worker threads leftover
454
- @wthr.each { |t| t[:yahns_quitter] = quitter }
457
+ @quitter = quitter
455
458
 
456
459
  quitter.close
457
460
  rescue => e
@@ -136,6 +136,10 @@ def require_exec(cmd)
136
136
  false
137
137
  end
138
138
 
139
+ def xfork
140
+ GTL.synchronize { fork { yield } }
141
+ end
142
+
139
143
  class DieIfUsed
140
144
  @@n = 0
141
145
  def each
@@ -31,7 +31,7 @@ def poke_until_dead(pid)
31
31
 
32
32
  def quit_wait(pid)
33
33
  pid or return
34
- err = $!
34
+ err = $! and warn "Terminating on #{err.inspect} (#{err.class})"
35
35
  Process.kill(:QUIT, pid)
36
36
  _, status = Timeout.timeout(10) { Process.waitpid2(pid) }
37
37
  assert status.success?, status.inspect
@@ -65,16 +65,33 @@ def server_helper_teardown
65
65
  @srv.close if defined?(@srv) && !@srv.closed?
66
66
  @ru.close! if defined?(@ru) && @ru
67
67
  check_err if defined?(@err)
68
+ Timeout.timeout(30) do
69
+ Process.kill(:TERM, @tail_pid)
70
+ Process.waitpid(@tail_pid)
71
+ end if @tail_pid
68
72
  end
69
73
 
70
74
  def server_helper_setup
71
75
  @srv = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
72
76
  @err = tmpfile(%w(srv .err))
73
77
  @ru = nil
78
+ @tail_pid = nil
79
+ case tail = ENV['TAIL']
80
+ when '1'
81
+ tail = 'tail -f' # POSIX
82
+ when nil, '0'
83
+ tail = nil
84
+ # else : allow users to specify 'tail -F' or 'gtail -F' for GNU
85
+ end
86
+ if tail
87
+ cmd = tail.split(/\s+/)
88
+ cmd << @err.path
89
+ @tail_pid = spawn(*cmd)
90
+ end
74
91
  end
75
92
 
76
93
  def mkserver(cfg, srv = @srv)
77
- fork do
94
+ xfork do
78
95
  ENV["YAHNS_FD"] = srv.fileno.to_s
79
96
  srv.autoclose = false
80
97
  yield if block_given?
@@ -9,40 +9,41 @@ class TestBin < Testcase
9
9
 
10
10
  def setup
11
11
  server_helper_setup
12
- @cmd = %W(ruby -I lib bin/yahns)
12
+ @cmd = %W(#{RbConfig.ruby} -I lib bin/yahns)
13
13
  end
14
14
 
15
15
  def test_listen_fd3
16
16
  return unless RUBY_VERSION.to_f > 2.3 # Fixed in ruby/trunk r51209, actually
17
- @srv.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0)
18
17
  host, port = @srv.addr[3], @srv.addr[1]
19
18
 
20
19
  ru = tmpfile(%w(test_bin_daemon .ru))
21
20
  ru.write("require 'rack/lobster'; run Rack::Lobster.new\n")
22
- cmd = %W(ruby -I lib bin/yahns-rackup
23
- -E none -p #{port} -o #{host} #{ru.path})
24
- pid = fork do # emulate a systemd environment
25
- env = {
26
- 'LISTEN_PID' => $$.to_s,
27
- 'LISTEN_FDS' => '1',
28
- }
29
- exec env, *cmd, 3 => @srv, err: @err.path
30
- end
31
- Net::HTTP.start(host, port) do |http|
32
- req = Net::HTTP::Get.new("/")
33
- res = http.request(req)
34
- assert_equal 200, res.code.to_i
35
- assert_equal "keep-alive", res["Connection"]
21
+ cmd = %W(#{RbConfig.ruby} -I lib bin/yahns-rackup -E none #{ru.path})
22
+ [ %w(-O listen=inherit), %W(-p #{port} -o #{host}) ].each do |opt|
23
+ @srv.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 0)
24
+ begin
25
+ pid = xfork do # emulate a systemd environment
26
+ env = { 'LISTEN_PID' => $$.to_s, 'LISTEN_FDS' => '1' }
27
+ cmd.concat(opt)
28
+ exec env, *cmd, 3 => @srv, err: @err.path
29
+ end
30
+ Net::HTTP.start(host, port) do |http|
31
+ req = Net::HTTP::Get.new("/")
32
+ res = http.request(req)
33
+ assert_equal 200, res.code.to_i
34
+ assert_equal "keep-alive", res["Connection"]
35
+ end
36
+ assert @srv.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).bool,
37
+ 'ensure the inheriting process applies TCP socket options'
38
+ ensure
39
+ if pid
40
+ Process.kill(:QUIT, pid)
41
+ _, status = Process.waitpid2(pid)
42
+ assert status.success?, status.inspect
43
+ end
44
+ end
36
45
  end
37
-
38
- assert_equal 1, @srv.getsockopt(:SOL_SOCKET, :SO_KEEPALIVE).int,
39
- 'ensure the inheriting process applies TCP socket options'
40
46
  ensure
41
- if pid
42
- Process.kill(:QUIT, pid)
43
- _, status = Process.waitpid2(pid)
44
- assert status.success?, status.inspect
45
- end
46
47
  ru.close! if ru
47
48
  end
48
49
 
@@ -76,7 +77,7 @@ def bin_daemon(worker, inherit)
76
77
  cfg.puts "end"
77
78
  @cmd.concat(%W(-D -c #{cfg.path}))
78
79
  addr = cloexec_pipe
79
- pid = fork do
80
+ pid = xfork do
80
81
  opts = { close_others: true }
81
82
  addr[0].close
82
83
  if inherit
@@ -158,11 +159,10 @@ def usr2_dir(tmpdir, preload, worker)
158
159
  # need to fork here since tests are MT and the FD can leak out and go to
159
160
  # other processes which fork (but do not exec), causing ETXTBUSY on
160
161
  # Process.spawn
161
- pid = fork do
162
- ruby = "#!#{`which ruby`}"
162
+ pid = xfork do
163
163
  File.open(exe, "w") { |y|
164
164
  lines = File.readlines("bin/yahns")
165
- lines[0] = ruby
165
+ lines[0] = "#!#{RbConfig.ruby}\n"
166
166
  y.chmod(0755)
167
167
  y.syswrite(lines.join)
168
168
  }
@@ -190,7 +190,7 @@ def usr2_dir(tmpdir, preload, worker)
190
190
  }
191
191
  cmd = %W(#{exe} -D -c #{cfg.path})
192
192
  cmd << { @srv => @srv, close_others: true }
193
- pid = GTL.synchronize { Process.spawn(env, *cmd) }
193
+ pid = Process.spawn(env, *cmd)
194
194
  res = Net::HTTP.start(host, port) { |h| h.get("/") }
195
195
  assert_equal 200, res.code.to_i
196
196
  orig = res.body
@@ -13,7 +13,7 @@ def test_initialize
13
13
  end
14
14
 
15
15
  def test_multi_conf_example
16
- pid = fork do
16
+ pid = xfork do
17
17
  tmpdir = yahns_mktmpdir
18
18
 
19
19
  # modify the example config file for testing
@@ -37,7 +37,7 @@ def test_multi_conf_example
37
37
  end
38
38
 
39
39
  def test_rack_basic_conf_example
40
- pid = fork do
40
+ pid = xfork do
41
41
  tmpdir = yahns_mktmpdir
42
42
 
43
43
  # modify the example config file for testing
@@ -52,7 +52,7 @@ def test_gzip_static
52
52
  File.symlink "COPYING", "#{tmpdir}/COPYING.relsymlink"
53
53
  gplgz = "#{tmpdir}/COPYING.gz"
54
54
  FileUtils.cp("COPYING", gpl)
55
- _, status = Process.waitpid2(fork do
55
+ _, status = Process.waitpid2(xfork do
56
56
  File.open(gplgz, "w") do |fp|
57
57
  Zlib::GzipWriter.wrap(fp.dup) { |io| io.write(GPL_TEXT) }
58
58
  end
@@ -14,7 +14,6 @@ def test_mt_accept
14
14
  skip "Linux kernel required" unless RUBY_PLATFORM =~ /linux/
15
15
  skip "/proc not mounted" unless File.directory?("/proc")
16
16
  err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
17
- opts = { threads: 1 }
18
17
  cfg.instance_eval do
19
18
  GTL.synchronize do
20
19
  app(:rack, Rack::Lobster.new) { listen "#{host}:#{port}", threads: 1 }
@@ -29,7 +28,6 @@ def test_mt_accept
29
28
  quit_wait(pid)
30
29
 
31
30
  cfg = Yahns::Config.new
32
- opts = { threads: 1 }
33
31
  cfg.instance_eval do
34
32
  GTL.synchronize do
35
33
  app(:rack, Rack::Lobster.new) { listen "#{host}:#{port}", threads: 2 }
@@ -6,6 +6,7 @@
6
6
  require 'digest'
7
7
  begin
8
8
  require 'kcar'
9
+ require 'yahns/proxy_pass'
9
10
  rescue LoadError
10
11
  end
11
12
 
@@ -188,7 +189,6 @@ def test_unix_socket_no_path
188
189
  pid = mkserver(cfg) do
189
190
  @srv.autoclose = @srv2.autoclose = false
190
191
  ENV["YAHNS_FD"] = "#{@srv.fileno},#{@srv2.fileno}"
191
- require 'yahns/proxy_pass'
192
192
  cfg.instance_eval do
193
193
  app(:rack, Yahns::ProxyPass.new("unix:#{unix_path}:/$fullpath")) do
194
194
  listen "#{host}:#{port}"
@@ -251,7 +251,6 @@ def test_proxy_pass
251
251
  err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
252
252
  host2, port2 = @srv2.addr[3], @srv2.addr[1]
253
253
  pid = mkserver(cfg) do
254
- require 'yahns/proxy_pass'
255
254
  @srv2.close
256
255
  cfg.instance_eval do
257
256
  app(:rack, Yahns::ProxyPass.new("http://#{host2}:#{port2}")) do
@@ -4,6 +4,7 @@
4
4
  require_relative 'server_helper'
5
5
  begin
6
6
  require 'kcar'
7
+ require 'yahns/proxy_pass'
7
8
  rescue LoadError
8
9
  end
9
10
  require 'digest/md5'
@@ -39,7 +40,6 @@ def setup
39
40
  @srv2 = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
40
41
  server_helper_setup
41
42
  skip "kcar missing yahns/proxy_pass" unless defined?(Kcar)
42
- require 'yahns/proxy_pass'
43
43
  @tmpdir = yahns_mktmpdir
44
44
  end
45
45
 
@@ -0,0 +1,58 @@
1
+ # Copyright (C) 2017 all contributors <yahns-public@yhbt.net>
2
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
3
+ # frozen_string_literal: true
4
+ require_relative 'server_helper'
5
+ require 'rack'
6
+
7
+ class TestRackEnv < Testcase
8
+ ENV["N"].to_i > 1 and parallelize_me!
9
+ include ServerHelper
10
+ alias setup server_helper_setup
11
+ alias teardown server_helper_teardown
12
+
13
+ def test_rack_env_logger
14
+ err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
15
+ cfg.instance_eval do
16
+ stderr_path err.path
17
+ GTL.synchronize do
18
+ app = Rack::Builder.new do
19
+ use Rack::Lint # ensure Lint passes
20
+ run(lambda do |env|
21
+ logger = env['rack.logger']
22
+ %w(SERVER_NAME SERVER_PORT rack.url_scheme).each do |k|
23
+ logger.info("#{k}=#{env[k].inspect}")
24
+ end
25
+ [ 200, [ %w(Content-Length 3), %w(Content Type text/plain)],
26
+ [ "OK\n" ] ]
27
+ end)
28
+ end
29
+ app(:rack, app.to_app) { listen "#{host}:#{port}" }
30
+ end
31
+ end
32
+ pid = mkserver(cfg)
33
+ Net::HTTP.start(host, port) do |http|
34
+ res = http.request(Net::HTTP::Get.new("/"))
35
+ assert_equal 200, res.code.to_i
36
+ assert_equal "OK\n", res.body
37
+ txt = File.read(err.path)
38
+ assert_match %r{\srack\.url_scheme=#{Regexp.escape('http'.inspect)}\s}s,
39
+ txt
40
+ assert_match %r{\sSERVER_NAME=#{Regexp.escape(host.inspect)}\s}s, txt
41
+ assert_match %r{\sSERVER_PORT=#{Regexp.escape(port.to_s.inspect)}\s}s, txt
42
+ end
43
+ err.truncate 0
44
+ err.rewind
45
+ c = TCPSocket.new(host, port)
46
+ c.write("GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
47
+ assert_match %r{\r\nOK\n\z}s, c.read
48
+ txt = File.read(err.path)
49
+ assert_match %r{\srack\.url_scheme=#{Regexp.escape('http'.inspect)}\s}s,
50
+ txt
51
+ assert_match %r{\sSERVER_NAME=#{Regexp.escape('example.com'.inspect)}\s}s,
52
+ txt
53
+ assert_match %r{\sSERVER_PORT=#{Regexp.escape('80'.inspect)}\s}s, txt
54
+ ensure
55
+ c.close if c
56
+ quit_wait(pid)
57
+ end
58
+ end
@@ -225,7 +225,7 @@ def a.each
225
225
  assert_equal 1, eggs.size
226
226
  assert_equal 1, eggs.first[1].instance_variable_get(:@worker_threads)
227
227
 
228
- pid = fork do
228
+ pid = xfork do
229
229
  bpipe[1].close
230
230
  ENV["YAHNS_FD"] = unix_srv.fileno.to_s
231
231
  unix_srv.autoclose = false
@@ -41,6 +41,7 @@ def teardown
41
41
  def ssl_client(host, port)
42
42
  ctx = OpenSSL::SSL::SSLContext.new
43
43
  ctx.ciphers = "ADH"
44
+ ctx.security_level = 0
44
45
  s = TCPSocket.new(host, port)
45
46
  ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
46
47
  ssl.connect
@@ -51,6 +52,7 @@ def ssl_client(host, port)
51
52
  def srv_ctx
52
53
  ctx = OpenSSL::SSL::SSLContext.new
53
54
  ctx.ciphers = "ADH"
55
+ ctx.security_level = 0
54
56
  ctx.tmp_dh_callback = proc { TEST_KEY_DH1024 }
55
57
  ctx
56
58
  end
@@ -10,6 +10,9 @@
10
10
  s.email = %q{yahns-public@yhbt.net}
11
11
  s.executables = manifest.grep(%r{\Abin/}).map { |s| s.sub(%r{\Abin/}, "") }
12
12
  s.files = manifest
13
+
14
+ s.required_ruby_version = '>= 2.0'
15
+
13
16
  s.add_dependency(%q<kgio>, '~> 2.9')
14
17
  s.add_dependency(%q<sleepy_penguin>, '~> 3.2')
15
18
  s.add_dependency(%q<unicorn>, '>= 4.6.3', '< 6.0')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yahns
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.15.0
4
+ version: 1.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - yahns hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-03-23 00:00:00.000000000 Z
11
+ date: 2018-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio
@@ -210,6 +210,7 @@ files:
210
210
  - test/test_proxy_pass.rb
211
211
  - test/test_proxy_pass_no_buffering.rb
212
212
  - test/test_rack.rb
213
+ - test/test_rack_env.rb
213
214
  - test/test_rack_hijack.rb
214
215
  - test/test_reopen_logs.rb
215
216
  - test/test_response.rb
@@ -233,7 +234,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
233
234
  requirements:
234
235
  - - ">="
235
236
  - !ruby/object:Gem::Version
236
- version: '0'
237
+ version: '2.0'
237
238
  required_rubygems_version: !ruby/object:Gem::Requirement
238
239
  requirements:
239
240
  - - ">="
@@ -241,7 +242,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
242
  version: '0'
242
243
  requirements: []
243
244
  rubyforge_project:
244
- rubygems_version: 2.6.10
245
+ rubygems_version: 2.7.7
245
246
  signing_key:
246
247
  specification_version: 4
247
248
  summary: sleepy, multi-threaded, non-blocking application server