yahns 1.15.0 → 1.16.0

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