yahns 1.16.0 → 1.17.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
2
  SHA256:
3
- metadata.gz: f5dcbf514b3bf6c9e92c8f0ca7d038019148217ffe6f3a88ab6ea7810333487e
4
- data.tar.gz: 4bfd3e6b7a2806daf1e53094050d928a9c28cd1b330ef594a118c2b6bd9d7c86
3
+ metadata.gz: 728a55c5f8af10a1f3dff8d71444a89d5cb5990ecc00613266c5f62405b5eec0
4
+ data.tar.gz: a7eff02b1cb3fea5470ef6232ee624a50e57a91ffcea234e31a1fcd829920c52
5
5
  SHA512:
6
- metadata.gz: 7f8123dcd0921c5c224bdb76009c7d7b1e751da62e2f30690ab438f959e234710afdb1f1b57ecfe64dc8b437ac782f95b69348c7177ecfe1e302173bc494574a
7
- data.tar.gz: 7f0e74b4271f4ec44bd2b800b0eb6fa4a570ecd538cf42f79c31ddf92c2552d98bade3a9396d8fd02ba9e16a2f145eb2a028b9050b01e931f90f42adcbf1767a
6
+ metadata.gz: cfa4b7b2842701c03611c666d85be9cf22c975d10e69d8f9642181b4241cc80919ef6a9e012996e34012f49bc0dad024a766bff0718278aed8957d4421fd953d
7
+ data.tar.gz: 965ed79f974891574b2cba0c31f44bc61160f4cbfb59488d528f38eca0e5e8198259447b2ca45fc5e4386d6216ed09f27c8a30bbd0fae753ebfbabb9f443fb49
@@ -60,7 +60,7 @@ all :: txt
60
60
 
61
61
  %.txt : %.pod
62
62
  $(pod2text) $< $@+
63
- -touch -r $< $@+ 2>/dev/null # GNU-ism
63
+ touch -r $< $@+
64
64
  mv $@+ $@
65
65
 
66
66
  clean::
@@ -166,8 +166,8 @@ See rackup documentation for more details.
166
166
  =head1 CONTACT
167
167
 
168
168
  All feedback welcome via plain-text mail to L<mailto:yahns-public@yhbt.net>
169
- No subscription is necessary to post to the mailing list.
170
- List archives are available at L<https://yhbt.net/yahns-public/>
169
+ No subscription is necessary to email us.
170
+ Mail archives are available at L<https://yhbt.net/yahns-public/>
171
171
 
172
172
  =head1 COPYRIGHT
173
173
 
@@ -83,7 +83,7 @@ See L<yahns_config(5)> for documentation on the configuration file format.
83
83
  =head1 CONTACT
84
84
 
85
85
  All feedback welcome via plain-text mail to L<mailto:yahns-public@yhbt.net>
86
- No subscription is necessary to post to the mailing list.
86
+ No subscription is necessary to email us.
87
87
  Mail archives are available at L<https://yhbt.net/yahns-public/>
88
88
 
89
89
  =head1 COPYRIGHT
@@ -661,8 +661,8 @@ See the examples/ directory in the git source tree.
661
661
  =head1 CONTACT
662
662
 
663
663
  All feedback welcome via plain-text mail to L<mailto:yahns-public@yhbt.net>
664
- No subscription is necessary to post to the mailing list.
665
- List archives are available at L<https://yhbt.net/yahns-public/>
664
+ No subscription is necessary to email us.
665
+ Mail archives are available at L<https://yhbt.net/yahns-public/>
666
666
 
667
667
  =head1 COPYRIGHT
668
668
 
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env ruby
2
- # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
2
+ # Copyright (C) 2013-2019 all contributors <yahns-public@yhbt.net>
3
3
  # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
  # frozen_string_literal: true
5
5
  CONSTANT = "Yahns::VERSION"
6
6
  RVF = "lib/yahns/version.rb"
7
7
  GVF = "GIT-VERSION-FILE"
8
- DEF_VER = "v1.16.0"
8
+ DEF_VER = "v1.17.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 https://yhbt.net/yahns
12
+ git clone https://yhbt.net/yahns.git
13
13
 
14
14
  tests
15
15
  -----
@@ -42,15 +42,14 @@ installing from git
42
42
  contact
43
43
  -------
44
44
 
45
- We use git(7) and develop yahns on a public mailing list like git
46
- developers do. Please send patches via git-send-email(1) to the public
47
- mailing list at <yahns-public@yhbt.net>. Pull requests should be
48
- formatted using git-request-pull(1).
45
+ We use git(7) and develop yahns using email like git.git hackers do.
46
+ Please send patches via git-send-email(1) to us at <yahns-public@yhbt.net>.
47
+ Pull requests should be formatted using git-request-pull(1).
49
48
 
50
49
  All mail is archived publically at: https://yhbt.net/yahns-public/
51
50
  Anonymous contributions will always be welcome.
52
- No subscription is necessary to post to the mailing list.
53
- Please remember to Cc: all recipients as subscription is optional.
51
+ No subscription is necessary to email us.
52
+ Please remember to reply-to-all as we do not encourage subscription.
54
53
 
55
54
  Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
56
55
  License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
data/README CHANGED
@@ -16,6 +16,7 @@ Features
16
16
  * suitable for slow clients, fast clients, or a mixture of both
17
17
  * HTTP/0.9 support
18
18
  * HTTP/1.1 persistent connections and pipelining
19
+ * HTTPS for HTTP/1.1 support
19
20
  * decodes HTTP chunked encoding for requests
20
21
  * parses HTTP/1.1 trailers in requests
21
22
  * supports streaming responses with lazy buffering for slow clients
@@ -55,37 +56,31 @@ Contact
55
56
 
56
57
  We are happy to see feedback of all types via plain-text email.
57
58
  Please send comments, user/dev discussion, patches, bug reports,
58
- and pull requests to the open-to-all mailing list at:
59
+ and pull requests to our public inbox at:
59
60
 
60
61
  yahns-public@yhbt.net
61
62
 
62
- No subscription is necessary to post. Please Cc: all recipients as
63
- subscription is not necessary.
63
+ Please use reply-to-all as we do not require any sort of subscription.
64
+ We archive all of our mail publically at:
64
65
 
65
- You may optionally subscribe by sending an email to:
66
+ https://yhbt.net/yahns-public/
67
+ nntp://news.public-inbox.org/inbox.comp.lang.ruby.yahns
66
68
 
67
- yahns-public+subscribe@yhbt.net
68
-
69
- We suck at delivering email, so relying on the archives might be
70
- a better bet:
71
-
72
- Mailing list archives browsable via HTTPS: https://yhbt.net/yahns-public/
73
- Or NNTP: nntp://news.public-inbox.org/inbox.comp.lang.ruby.yahns
74
69
  Atom feed: https://yhbt.net/yahns-public/new.atom
75
70
 
76
71
  This README is our homepage, we would rather be working on HTTP servers
77
72
  all day than worrying about the next browser vulnerability because
78
73
  HTML/CSS/JS is too complicated for us.
79
74
 
80
- * https://yhbt.net/yahns/README
75
+ * https://yhbt.net/yahns.git/about/
81
76
 
82
77
  Hacking
83
78
  -------
84
79
 
85
80
  We use git and follow the same development model as git itself
86
- (mailing list-oriented, benevolent dictator).
81
+ (email-oriented, benevolent dictator).
87
82
 
88
- git clone https://yhbt.net/yahns
83
+ git clone https://yhbt.net/yahns.git
89
84
 
90
85
  Please use git-format-patch(1) and git-send-email(1) distributed with
91
86
  the git(7) suite for generating and sending patches. Please format
@@ -14,6 +14,21 @@ class Autoindex
14
14
  FN = %{<a href="%s">%s</a>}
15
15
  TFMT = "%Y-%m-%d %H:%M"
16
16
 
17
+ # default to a dark, web-safe (216 color) palette for power-savings.
18
+ # Color-capable browsers can respect the prefers-color-scheme:light
19
+ # @media query (browser support a work-in-progress)
20
+ STYLE = <<''.gsub(/^\s*/m, '').delete!("\n")
21
+ @media screen {
22
+ *{background:#000;color:#ccc}
23
+ a{color:#69f;text-decoration:none}
24
+ a:visited{color:#96f}
25
+ }
26
+ @media screen AND (prefers-color-scheme:light) {
27
+ *{background:#fff;color:#333}
28
+ a{color:#00f;text-decoration:none}
29
+ a:visited{color:#808}
30
+ }
31
+
17
32
  def initialize(app, *args)
18
33
  app.respond_to?(:root) or raise ArgumentError,
19
34
  "wrapped app #{app.inspect} does not respond to #root"
@@ -139,8 +154,9 @@ def call(env)
139
154
  path_info_html = path_info_ue.split(%r{/}, -1).map! do |part|
140
155
  Rack::Utils.escape_html(part)
141
156
  end.join("/")
142
- body = "<html><head><title>Index of #{path_info_html}</title></head>" \
143
- "<body><h1>Index of #{path_info_html}</h1><hr><pre>\n" \
157
+ body = "<html><head><title>Index of #{path_info_html}</title>" \
158
+ "<style>#{STYLE}</style>" \
159
+ "</head><body><h1>Index of #{path_info_html}</h1><hr><pre>\n" \
144
160
  "#{dirs.concat(files).join("\n")}" \
145
161
  "</pre><hr></body></html>\n"
146
162
  h = { "Content-Type" => "text/html", "Content-Length" => body.size.to_s }
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
- # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
3
- # License: GPLv2 or later (https://www.gnu.org/licenses/gpl-2.0.txt)
2
+ # Copyright (C) 2013-2018 all contributors <yahns-public@yhbt.net>
3
+ # License: GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
4
4
  # frozen_string_literal: true
5
5
  #
6
6
  # if running under yahns, worker_processes is recommended to avoid conflicting
@@ -18,21 +18,31 @@
18
18
  # use Rack::Chunked
19
19
  # # other Rack middlewares can go here...
20
20
  #
21
- # run ExecCgi.new('/path/to/cgit.cgi') # cgit: https://git.zx2c4.com/cgit/
21
+ # # cgit: https://git.zx2c4.com/cgit/
22
+ # run ExecCgi.new('/path/to/cgit.cgi', opts)
22
23
  #
23
24
  class ExecCgi
24
- class MyIO < Kgio::Pipe
25
+ class MyIO
25
26
  attr_writer :my_pid
26
27
  attr_writer :body_tip
28
+ attr_reader :rd
29
+
30
+ def initialize(rd)
31
+ @rd = rd
32
+ end
27
33
 
28
34
  def each
29
- buf = @body_tip || ''.dup
30
- if buf.size > 0
31
- yield buf
32
- end
33
- while tmp = kgio_read(8192, buf)
35
+ buf = @body_tip
36
+ yield buf unless buf.empty?
37
+
38
+ case tmp = @rd.read_nonblock(8192, buf, exception: false)
39
+ when :wait_readable
40
+ @rd.wait_readable
41
+ when nil
42
+ break
43
+ else # String
34
44
  yield tmp
35
- end
45
+ end while true
36
46
  self
37
47
  ensure
38
48
  # do this sooner, since the response body may be buffered, we want
@@ -46,8 +56,8 @@ def close
46
56
  # Note: this object (and any client-specific objects) will never
47
57
  # be shared across different threads, so we do not need extra
48
58
  # mutual exclusion here.
49
- return if closed?
50
- super
59
+ return if @rd.closed?
60
+ @rd.close
51
61
  begin
52
62
  Process.waitpid(@my_pid)
53
63
  rescue Errno::ECHILD
@@ -72,7 +82,7 @@ def close
72
82
  SERVER_PROTOCOL
73
83
  SERVER_SOFTWARE
74
84
  SCRIPT_NAME
75
- ).map(&:freeze) # frozen strings are faster for Hash assignments
85
+ )
76
86
 
77
87
  def initialize(*args)
78
88
  @env = Hash === args[0] ? args.shift : {}
@@ -82,6 +92,7 @@ def initialize(*args)
82
92
  first[0] == ?/ or args[0] = ::File.expand_path(first)
83
93
  File.executable?(args[0]) or
84
94
  raise ArgumentError, "#{args[0]} is not executable"
95
+ @opts = Hash === args[-1] ? args.pop : {}
85
96
  end
86
97
 
87
98
  # Calls the app
@@ -90,20 +101,23 @@ def call(env)
90
101
  cgi_env = { "GATEWAY_INTERFACE" => "CGI/1.1" }
91
102
  PASS_VARS.each { |key| val = env[key] and cgi_env[key] = val }
92
103
  env.each { |key,val| cgi_env[key] = val if key =~ /\AHTTP_/ }
93
- pipe = MyIO.pipe
94
- errbody = pipe[0]
95
- errbody.my_pid = Process.spawn(cgi_env.merge!(@env), *@args,
96
- out: pipe[1], close_others: true)
97
- pipe[1].close
98
- pipe = pipe[0]
99
104
 
100
- if head = pipe.kgio_read(8192)
105
+ rd, wr = IO.pipe
106
+ io = MyIO.new(rd)
107
+ errbody = io
108
+ errbody.my_pid = spawn(cgi_env.merge!(@env), *@args,
109
+ @opts.merge(out: wr, close_others: true))
110
+ wr.close
111
+
112
+ begin
113
+ head = rd.readpartial(8192)
101
114
  until head =~ /\r?\n\r?\n/
102
- tmp = pipe.kgio_read(8192) or break
115
+ tmp = rd.readpartial(8192)
103
116
  head << tmp
117
+ tmp.clear
104
118
  end
105
119
  head, body = head.split(/\r?\n\r?\n/, 2)
106
- pipe.body_tip = body
120
+ io.body_tip = body
107
121
 
108
122
  env["HTTP_VERSION"] ||= "HTTP/1.0" # stop Rack::Chunked for HTTP/0.9
109
123
 
@@ -117,8 +131,8 @@ def call(env)
117
131
  end
118
132
  status = headers.delete("Status") || 200
119
133
  errbody = nil
120
- [ status, headers, pipe ]
121
- else
134
+ [ status, headers, io ]
135
+ rescue EOFError
122
136
  [ 500, { "Content-Length" => "0", "Content-Type" => "text/plain" }, [] ]
123
137
  end
124
138
  ensure
@@ -36,7 +36,7 @@ class UpstreamSocket < Kgio::Socket # :nodoc:
36
36
  attr_writer :expiry
37
37
 
38
38
  # called automatically by kgio_read!
39
- def kgio_wait_readable(timeout = nil)
39
+ def wait_readable(timeout = nil)
40
40
  super(timeout || wait_time)
41
41
  end
42
42
 
@@ -59,7 +59,7 @@ def req_write(buf, timeout)
59
59
  @expiry = Time.now + timeout
60
60
  case rv = kgio_trywrite(buf)
61
61
  when :wait_writable
62
- kgio_wait_writable(wait_time)
62
+ wait_writable(wait_time)
63
63
  when nil
64
64
  return
65
65
  when String
@@ -5,6 +5,7 @@
5
5
 
6
6
  require 'unicorn' # pulls in raindrops, kgio, fcntl, etc, stringio, and logger
7
7
  require 'sleepy_penguin'
8
+ require 'io/wait'
8
9
 
9
10
  # kill off some unicorn internals we don't need
10
11
  # we'll probably just make kcar into a server parser so we don't depend
@@ -16,8 +17,9 @@
16
17
  end
17
18
 
18
19
  # yahns exposes no user-visible API outside of the config file.
19
- # See https://yhbt.net/yahns/yahns_config.txt for the config documentation
20
- # and https://yhbt.net/yahns/ for the homepage.
20
+ # See https://yhbt.net/yahns.git/tree/examples/yahns_config.txt
21
+ # for the config documentation
22
+ # and https://yhbt.net/yahns.git/about/ for the homepage.
21
23
  # Internals are subject to change.
22
24
 
23
25
  module Yahns
@@ -4,8 +4,8 @@
4
4
  # frozen_string_literal: true
5
5
  #
6
6
  # Implements a DSL for configuring a yahns server.
7
- # See https://yhbt.net/yahns/examples/yahns_multi.conf.rb for a full
8
- # example configuration file.
7
+ # See https://yhbt.net/yahns.git/tree/examples/yahns_multi.conf.rb
8
+ # for a full example configuration file.
9
9
  class Yahns::Config # :nodoc:
10
10
  # public within yahns itself, NOT a public interface for users outside
11
11
  # of yahns. See yahns/rack for usage example
@@ -409,7 +409,7 @@ def errors(val)
409
409
  if String === val
410
410
  # we've already bound working_directory by the time we get here
411
411
  val = File.open(File.expand_path(val), "ab")
412
- val.close_on_exec = val.sync = true
412
+ val.sync = true
413
413
  else
414
414
  rt = [ :puts, :write, :flush ] # match Rack::Lint
415
415
  rt.all? { |m| val.respond_to?(m) } or raise ArgumentError,
@@ -32,7 +32,6 @@ def self.daemon(yahns_server)
32
32
  # We cannot use Yahns::Sigevent (eventfd) here because we need
33
33
  # to detect EOF on unexpected death, not just read/write
34
34
  rd, wr = IO.pipe
35
- rd.close_on_exec = wr.close_on_exec = true
36
35
  grandparent = $$
37
36
  if fork
38
37
  wr.close # grandparent does not write
@@ -235,25 +235,17 @@ def app_call(input)
235
235
  http_response_write(res, opt)
236
236
  end
237
237
 
238
- # called automatically by kgio_write
239
- def kgio_wait_writable(timeout = self.class.client_timeout)
240
- super timeout
241
- end
242
-
243
- # called automatically by kgio_read
244
- def kgio_wait_readable(timeout = self.class.client_timeout)
245
- super timeout
246
- end
247
-
248
238
  # used by StreamInput (and thus TeeInput) for input_buffering {false|:lazy}
249
239
  def yahns_read(bytes, buf)
250
240
  case rv = kgio_tryread(bytes, buf)
251
241
  when String, nil
252
242
  return rv
253
243
  when :wait_readable
254
- kgio_wait_readable or raise Yahns::ClientTimeout, "waiting for read", []
244
+ wait_readable(self.class.client_timeout) or
245
+ raise Yahns::ClientTimeout, "waiting for read", []
255
246
  when :wait_writable
256
- kgio_wait_writable or raise Yahns::ClientTimeout, "waiting for write", []
247
+ wait_writable(self.class.client_timeout) or
248
+ raise Yahns::ClientTimeout, "waiting for write", []
257
249
  end while true
258
250
  end
259
251
 
@@ -332,7 +324,6 @@ def do_pread(io, count, offset)
332
324
  io.read(count, buf)
333
325
  end
334
326
  rescue EOFError
335
- warn "BUG: do_pread overreach:\n #{caller.join("\n ")}\n"
336
327
  nil
337
328
  end
338
329
 
@@ -46,10 +46,9 @@ def response_start
46
46
  @hs.response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
47
47
  end
48
48
 
49
- def response_wait_write(rv)
50
- # call the kgio_wait_readable or kgio_wait_writable method
51
- ok = __send__("kgio_#{rv}") and return ok
49
+ def response_wait_write(rv) # rv = [:wait_writable | :wait_readable ]
52
50
  k = self.class
51
+ ok = __send__(rv, k.client_timeout) and return ok
53
52
  k.logger.info("fd=#{fileno} ip=#@kgio_addr timeout on :#{rv} after "\
54
53
  "#{k.client_timeout}s")
55
54
  false
@@ -17,12 +17,6 @@ class Yahns::Queue < SleepyPenguin::Kqueue::IO # :nodoc:
17
17
 
18
18
  ADD_ONESHOT = Ev::ADD | Ev::ONESHOT # private
19
19
 
20
- def self.new
21
- rv = super
22
- rv.close_on_exec = true
23
- rv
24
- end
25
-
26
20
  # for HTTP and HTTPS servers, we rely on the io writing to us, first
27
21
  # flags: QEV_RD/QEV_WR (usually QEV_RD)
28
22
  def queue_add(io, flags)
@@ -7,7 +7,6 @@ class Yahns::QueueQuitter # :nodoc:
7
7
  attr_reader :to_io
8
8
  def initialize
9
9
  @reader, @to_io = IO.pipe
10
- @to_io.close_on_exec = true
11
10
  end
12
11
 
13
12
  def yahns_step
@@ -22,4 +21,8 @@ def close
22
21
  @reader.close
23
22
  @to_io.close
24
23
  end
24
+
25
+ def closed?
26
+ @to_io.closed?
27
+ end
25
28
  end
@@ -476,7 +476,7 @@ def reap_reexec
476
476
  end
477
477
 
478
478
  def sp_sig_handle(alive)
479
- @sev.kgio_wait_readable(alive ? nil : 0.01)
479
+ @sev.wait_readable(alive ? nil : 0.01)
480
480
  @sev.yahns_step
481
481
  case sig = @sig_queue.shift
482
482
  when :QUIT, :TERM, :INT
@@ -500,6 +500,19 @@ def dropping(fdmap)
500
500
  if drop_acceptors[0] || fdmap.size > 0
501
501
  timeout = @shutdown_expire < Yahns.now ? -1 : @shutdown_timeout
502
502
  n = fdmap.desperate_expire(timeout)
503
+ return false if n == 0 && @listeners.empty? # all done!
504
+
505
+ # FIXME: sometimes shutdowns take a long time when using proxy_pass
506
+ # Still not sure what's going on and it takes a while to reproduce..
507
+ if timeout == -1
508
+ @logger.error(
509
+ "exiting on shutdown_timeout=#@shutdown_timeout #{fdmap.size} FD(s) remain"
510
+ )
511
+
512
+ system('lsof', '-n', '-p', "#$$") if RUBY_PLATFORM =~ /linux/
513
+ return false
514
+ end
515
+
503
516
  $0 = "yahns quitting, #{n} FD(s) remain"
504
517
  true
505
518
  else
@@ -31,8 +31,6 @@ def worker_atfork_internal(worker)
31
31
  # daemon_pipe may be true for non-initial workers
32
32
  @daemon_pipe = @daemon_pipe.close if @daemon_pipe.respond_to?(:close)
33
33
 
34
- srand # in case this pops up again: https://bugs.ruby-lang.org/issues/4338
35
-
36
34
  # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
37
35
  # dying workers can recycle pids
38
36
  OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
@@ -91,7 +89,7 @@ def join
91
89
  @logger.info "master process ready"
92
90
  daemon_ready
93
91
  begin
94
- @sev.kgio_wait_readable
92
+ @sev.wait_readable
95
93
  @sev.yahns_step
96
94
  reap_all
97
95
  case @sig_queue.shift
@@ -159,7 +157,7 @@ def run_mp_worker(worker)
159
157
  def mp_sig_handle(watch, alive)
160
158
  # not performance critical
161
159
  watch.delete_if { |io| io.to_io.closed? }
162
- if r = IO.select(watch, nil, nil, alive ? nil : 0.1)
160
+ if r = select(watch, nil, nil, alive ? nil : 0.1)
163
161
  r[0].each(&:yahns_step)
164
162
  end
165
163
  case @sig_queue.shift
@@ -3,7 +3,6 @@
3
3
  # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
4
4
  # frozen_string_literal: true
5
5
  class Yahns::Sigevent < SleepyPenguin::EventFD # :nodoc:
6
- include Kgio::DefaultWaiters
7
6
  def self.new
8
7
  super(0, :CLOEXEC)
9
8
  end
@@ -5,21 +5,24 @@
5
5
  class Yahns::Sigevent # :nodoc:
6
6
  attr_reader :to_io
7
7
  def initialize
8
- @to_io, @wr = Kgio::Pipe.new
9
- @to_io.close_on_exec = @wr.close_on_exec = true
8
+ @to_io, @wr = IO.pipe
10
9
  end
11
10
 
12
- def kgio_wait_readable(*args)
13
- @to_io.kgio_wait_readable(*args)
11
+ def wait_readable(*args)
12
+ @to_io.wait_readable(*args)
13
+ end
14
+
15
+ def fileno
16
+ @to_io.fileno
14
17
  end
15
18
 
16
19
  def sev_signal
17
- @wr.kgio_trywrite(".")
20
+ @wr.write_nonblock(".", exception: false)
18
21
  end
19
22
 
20
23
  def yahns_step
21
24
  # 11 byte strings -> no malloc on YARV
22
- while String === @to_io.kgio_tryread(11)
25
+ while String === @to_io.read_nonblock(11, exception: false)
23
26
  end
24
27
  :wait_readable
25
28
  end
@@ -28,4 +31,8 @@ def close
28
31
  @to_io.close
29
32
  @wr.close
30
33
  end
34
+
35
+ def closed?
36
+ @to_io.closed?
37
+ end
31
38
  end
@@ -19,7 +19,7 @@ def so_reuseport
19
19
 
20
20
  def set_server_sockopt(sock, opt)
21
21
  opt = {backlog: 1024}.merge!(opt)
22
- sock.close_on_exec = true
22
+ sock.close_on_exec = true # needed for inherited sockets
23
23
 
24
24
  TCPSocket === sock and sock.setsockopt(:IPPROTO_TCP, :TCP_NODELAY, 1)
25
25
  sock.setsockopt(:SOL_SOCKET, :SO_KEEPALIVE, 1)
@@ -31,6 +31,7 @@
31
31
  class Yahns::Wbuf # :nodoc:
32
32
  include Yahns::WbufCommon
33
33
  attr_reader :busy
34
+ IO_WRITEV = RUBY_VERSION.to_r >= 2.5 # IO#write uses writev
34
35
 
35
36
  def initialize(body, persist)
36
37
  @tmpio = nil
@@ -40,9 +41,15 @@ def initialize(body, persist)
40
41
  @busy = false
41
42
  end
42
43
 
43
- def wbuf_writev(buf)
44
- @tmpio.kgio_writev(buf)
45
- buf.inject(0) { |n, s| n += s.size }
44
+ if IO_WRITEV
45
+ def wbuf_writev(buf)
46
+ @tmpio.write(*buf)
47
+ end
48
+ else
49
+ def wbuf_writev(buf)
50
+ @tmpio.kgio_writev(buf)
51
+ buf.inject(0) { |n, s| n += s.size }
52
+ end
46
53
  end
47
54
 
48
55
  def wbuf_write(c, buf)
@@ -9,6 +9,14 @@ class Yahns::Worker # :nodoc:
9
9
  def initialize(nr)
10
10
  @nr = nr
11
11
  @to_io, @wr = Kgio::Pipe.new
12
+
13
+ begin
14
+ # F_SETPIPE_SZ = 1031, PAGE_SIZE = 4096
15
+ # (fcntl will handle minimum size on platforms where PAGE_SIZE > 4096)
16
+ @to_io.fcntl(1031, 4096)
17
+ rescue Errno::EINVAL
18
+ # old kernel
19
+ end if RUBY_PLATFORM =~ /\blinux\b/
12
20
  end
13
21
 
14
22
  def atfork_child
@@ -125,7 +125,7 @@ def nread
125
125
  end if ! IO.method_defined?(:nread) && RUBY_PLATFORM =~ /linux/
126
126
 
127
127
  def cloexec_pipe
128
- IO.pipe.each { |io| io.close_on_exec = true }
128
+ IO.pipe
129
129
  end
130
130
 
131
131
  def require_exec(cmd)
@@ -52,9 +52,7 @@ def quit_wait(pid)
52
52
  # only use for newly bound sockets
53
53
  def get_tcp_client(host, port, tries = 500)
54
54
  begin
55
- c = TCPSocket.new(host, port)
56
- c.close_on_exec = true
57
- return c
55
+ return TCPSocket.new(host, port)
58
56
  rescue Errno::ECONNREFUSED
59
57
  raise if tries < 0
60
58
  tries -= 1
@@ -99,7 +99,10 @@ def bin_daemon(worker, inherit)
99
99
  # Even with a synchronous FD_CLOEXEC, there's a chance of a race
100
100
  # because the server does not bind right away.
101
101
  unless inherit
102
- @srv.shutdown
102
+ begin
103
+ @srv.shutdown
104
+ rescue Errno::ENOTCONN
105
+ end
103
106
  @srv.close
104
107
  end
105
108
  exec(*@cmd)
@@ -170,7 +170,7 @@ def _blocked_zombie(block_on, rtype)
170
170
  assert_match %r{\A\d+\n\z}, body
171
171
  exec_pid = body.to_i
172
172
  poke_until_dead exec_pid
173
- assert_raises(EOFError) { c.read_nonblock(666) }
173
+ assert_raises(EOFError) { c.readpartial(666) }
174
174
  else
175
175
  raise "BUG in test, bad rtype"
176
176
  end
@@ -179,4 +179,27 @@ def _blocked_zombie(block_on, rtype)
179
179
  c.close if c
180
180
  quit_wait(pid)
181
181
  end
182
+
183
+ def test_rlimit_options
184
+ err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
185
+ tout = 1
186
+ opts = { rlimit_cpu: tout, rlimit_core: 0 }
187
+ cmd = [ '/bin/sh', '-c', 'while :; do :;done', opts ]
188
+ pid = mkserver(cfg) do
189
+ require './extras/exec_cgi'
190
+ cfg.instance_eval do
191
+ stack = Rack::ContentLength.new(Rack::Chunked.new(ExecCgi.new(*cmd)))
192
+ app(:rack, stack) { listen "#{host}:#{port}" }
193
+ stderr_path err.path
194
+ worker_processes 1
195
+ end
196
+ end
197
+ c = get_tcp_client(host, port)
198
+ c.write "GET / HTTP/1.0\r\n\r\n"
199
+ assert_same c, c.wait(tout + 5)
200
+ assert_match %r{ 500 Internal Server Error\b}, c.readpartial(4096)
201
+ c.close
202
+ ensure
203
+ quit_wait(pid)
204
+ end
182
205
  end
@@ -38,7 +38,6 @@ def test_serve_static
38
38
 
39
39
  # ensure sendfile works on Unix sockets
40
40
  s = UNIXSocket.new(sock)
41
- s.close_on_exec = true
42
41
  s.write "GET /COPYING\r\n\r\n"
43
42
  assert_equal gplv3, Timeout.timeout(30) { s.read }
44
43
  s.close
@@ -182,7 +182,6 @@ def test_check_client_connection
182
182
  tmpdir = yahns_mktmpdir
183
183
  sock = "#{tmpdir}/sock"
184
184
  unix_srv = UNIXServer.new(sock)
185
- unix_srv.close_on_exec = true
186
185
  msgs = %w(ZZ zz)
187
186
  err = @err
188
187
  cfg = Yahns::Config.new
@@ -234,7 +233,6 @@ def a.each
234
233
  bpipe[0].close
235
234
  a = UNIXSocket.new(sock)
236
235
  b = UNIXSocket.new(sock)
237
- b.close_on_exec = a.close_on_exec = true
238
236
  a.write("GET /sleep HTTP/1.0\r\n\r\n")
239
237
  r = IO.select([a], nil, nil, 4)
240
238
  assert r, "nothing ready"
@@ -681,7 +679,6 @@ def test_errors
681
679
  assert_equal "INFO HIHI\n", re.read
682
680
 
683
681
  c = UNIXSocket.new(sock)
684
- c.close_on_exec = true
685
682
  c.write "GET /\r\n\r\n"
686
683
  assert_equal c, c.wait(30)
687
684
  assert_equal "OK", c.read
@@ -12,9 +12,7 @@ class TestUnixSocket < Testcase
12
12
  def unix_socket(path)
13
13
  Timeout.timeout(30) do
14
14
  begin
15
- c = UNIXSocket.new(path)
16
- c.close_on_exec = true
17
- return c
15
+ return UNIXSocket.new(path)
18
16
  rescue Errno::ENOENT
19
17
  sleep 0.01
20
18
  retry
@@ -19,7 +19,7 @@ def self.output_buffer_tmpdir
19
19
  end
20
20
 
21
21
  def socketpair
22
- KgioUS.pair.each { |io| io.close_on_exec = true }
22
+ KgioUS.pair
23
23
  end
24
24
 
25
25
  def test_wbuf
@@ -28,6 +28,6 @@
28
28
  # for Rack::Utils::HeaderHash#each
29
29
  s.add_development_dependency(%q<rack>, '>= 1.1')
30
30
 
31
- s.homepage = 'https://yhbt.net/yahns/README'
31
+ s.homepage = 'https://yhbt.net/yahns.git/about/'
32
32
  s.licenses = "GPL-3.0+"
33
33
  end
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.16.0
4
+ version: 1.17.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: 2018-08-06 00:00:00.000000000 Z
11
+ date: 2019-04-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio
@@ -222,7 +222,7 @@ files:
222
222
  - test/test_unix_socket.rb
223
223
  - test/test_wbuf.rb
224
224
  - yahns.gemspec
225
- homepage: https://yhbt.net/yahns/README
225
+ homepage: https://yhbt.net/yahns.git/about/
226
226
  licenses:
227
227
  - GPL-3.0+
228
228
  metadata: {}
@@ -241,8 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
241
241
  - !ruby/object:Gem::Version
242
242
  version: '0'
243
243
  requirements: []
244
- rubyforge_project:
245
- rubygems_version: 2.7.7
244
+ rubygems_version: 3.0.2
246
245
  signing_key:
247
246
  specification_version: 4
248
247
  summary: sleepy, multi-threaded, non-blocking application server