yahns 1.1.0 → 1.2.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
  SHA1:
3
- metadata.gz: 0a9749686719fc4ebab52f18c57c3c3b9cf00ab8
4
- data.tar.gz: 82df4e6686e4ee97cad0462fb1d0c809d19f9a10
3
+ metadata.gz: 7b73379a257ad0e09cd00b6d1b1b316ca6913992
4
+ data.tar.gz: 1e5203cb1d70720bdab3f9624b9766e3d49a9d51
5
5
  SHA512:
6
- metadata.gz: f5f385fabee64576cadcea538d77065ec625e50ee6c9c40ebba09d662995f6ad9c1740a422ee219078c1f1a7c7198120927625d08632b5d16d6f96dfd8039fb3
7
- data.tar.gz: 12080fba5d268e326f648da62708ec2927063b2de470a3590be6d55d21968ea5150e94d8b18165dba22ca9388fbb535c9f5180039926a75df372d4f602d60c76
6
+ metadata.gz: 1c7a49cdbe694c1a27bd3f26d6db10b4b1f8ac11cf9307036957ba9df60aab266ef621cf515dcfd9eb592675648a0864b9949ba9b11ffccf083627a392353879
7
+ data.tar.gz: 2250ca7bf3fdfffbbb42cbb332cbe08bca48aa4c0e6066d3414e3248a2e666094ba2da07ff27312021558b67e45de59bdb10428fcd089c72bbfaeabd9f3488da
data/GIT-VERSION-GEN CHANGED
@@ -4,7 +4,7 @@
4
4
  CONSTANT = "Yahns::VERSION"
5
5
  RVF = "lib/yahns/version.rb"
6
6
  GVF = "GIT-VERSION-FILE"
7
- DEF_VER = "v1.1.0"
7
+ DEF_VER = "v1.2.0"
8
8
  vn = DEF_VER
9
9
 
10
10
  # First see if there is a version file (included in release tarballs),
data/INSTALL CHANGED
@@ -6,3 +6,10 @@ No tarballs are currently provided.
6
6
 
7
7
  You may also install yahns from the git source, see the HACKING document for
8
8
  more details.
9
+
10
+ Debian GNU/kFreeBSD:
11
+
12
+ For now, you will need to define SENDFILE_BROKEN=1 in the env before
13
+ running yahns, and also install the "io-extra" RubyGem (tested on 1.2.7)
14
+
15
+ gem install -v 1.2.7 io-extra
data/README CHANGED
@@ -44,11 +44,11 @@ Supported Platforms
44
44
 
45
45
  yahns is developed primarily for modern GNU/Linux systems.
46
46
 
47
- We may support kqueue for FreeBSD/OpenBSD/NetBSD if there is significant
48
- interest. Non-Free systems/dependencies will never be supported
47
+ We have experimental support kqueue on FreeBSD (and possibly OpenBSD and
48
+ NetBSD). Non-Free systems/dependencies will never be supported.
49
49
 
50
50
  Supported Ruby implementations:
51
- * (Matz) Ruby 1.9.3 and later (we develop against trunk)
51
+ * (Matz) Ruby 1.9.3 and later (we develop (and host our website) on trunk)
52
52
  * Rubinius 2.0 or later (best-effort)
53
53
 
54
54
  Contact
data/lib/yahns.rb CHANGED
@@ -30,7 +30,7 @@ module Yahns # :nodoc:
30
30
  #
31
31
  # Yahns::START[0] = "/home/bofh/2.0.0/bin/yahns"
32
32
  START = {
33
- :argv => ARGV.map { |arg| arg.dup },
33
+ :argv => ARGV.map(&:dup),
34
34
  0 => $0.dup,
35
35
  }
36
36
 
@@ -57,7 +57,7 @@ end
57
57
 
58
58
  # FIXME: require lazily
59
59
  require_relative 'yahns/log'
60
- require_relative 'yahns/queue_epoll'
60
+ require_relative 'yahns/queue'
61
61
  require_relative 'yahns/stream_input'
62
62
  require_relative 'yahns/tee_input'
63
63
  require_relative 'yahns/queue_egg'
@@ -0,0 +1,82 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2014, Eric Wong <normalperson@yhbt.net> and all contributors
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ #
5
+ # This is the dangerous, low-level kqueue interface for sleepy_penguin
6
+ # It is safe as long as you're aware of all potential concurrency
7
+ # issues given multithreading, GC, and kqueue itself.
8
+ class Yahns::Queue < SleepyPenguin::Kqueue::IO # :nodoc:
9
+ include SleepyPenguin
10
+ attr_accessor :fdmap # Yahns::Fdmap
11
+
12
+ # public
13
+ QEV_QUIT = nil # Level Trigger for QueueQuitter
14
+ QEV_RD = EvFilt::READ
15
+ QEV_WR = EvFilt::WRITE
16
+
17
+ ADD_ONESHOT = Ev::ADD | Ev::ONESHOT # private
18
+
19
+ def self.new
20
+ rv = super
21
+ rv.close_on_exec = true
22
+ rv
23
+ end
24
+
25
+ # for HTTP and HTTPS servers, we rely on the io writing to us, first
26
+ # flags: QEV_RD/QEV_WR (usually QEV_RD)
27
+ def queue_add(io, flags)
28
+ # order is very important here, this thread cannot do anything with
29
+ # io once we've issued epoll_ctl() because another thread may use it
30
+ @fdmap.add(io)
31
+ fflags = ADD_ONESHOT
32
+ if flags == QEV_QUIT
33
+ fflags = Ev::ADD
34
+ flags = QEV_WR
35
+ end
36
+ kevent(Kevent[io.fileno, flags, fflags, 0, 0, io])
37
+ end
38
+
39
+ def thr_init
40
+ Thread.current[:yahns_rbuf] = ""
41
+ Thread.current[:yahns_fdmap] = @fdmap
42
+ end
43
+
44
+ def queue_del(io)
45
+ # do not bother with kevent EV_DELETE, it may be tricky to get right,
46
+ # we only did it in epoll since Eric knows the epoll internals well.
47
+ @fdmap.forget(io)
48
+ end
49
+
50
+ # returns an array of infinitely running threads
51
+ def worker_thread(logger, max_events)
52
+ Thread.new do
53
+ thr_init
54
+ begin
55
+ kevent(nil, max_events) do |_,_,_,_,_,io| # don't care for flags for now
56
+ # Note: we absolutely must not do anything with io after
57
+ # we've called epoll_ctl on it, io is exclusive to this
58
+ # thread only until epoll_ctl is called on it.
59
+ case rv = io.yahns_step
60
+ when :wait_readable
61
+ kevent(Kevent[io.fileno, QEV_RD, ADD_ONESHOT, 0, 0, io])
62
+ when :wait_writable
63
+ kevent(Kevent[io.fileno, QEV_WR, ADD_ONESHOT, 0, 0, io])
64
+ when :ignore # only used by rack.hijack
65
+ # we cannot EV_DELETE after hijacking, the hijacker
66
+ # may have already closed it Likewise, io.fileno is not
67
+ # expected to work, so we had to erase it from fdmap before hijack
68
+ when nil, :close
69
+ # this must be the ONLY place where we call IO#close on
70
+ # things that got inside the queue
71
+ @fdmap.sync_close(io)
72
+ else
73
+ raise "BUG: #{io.inspect}#yahns_step returned: #{rv.inspect}"
74
+ end
75
+ end
76
+ rescue => e
77
+ break if closed? # can still happen due to shutdown_timeout
78
+ Yahns::Log.exception(logger, 'queue loop', e)
79
+ end while true
80
+ end
81
+ end
82
+ end
@@ -5,9 +5,8 @@
5
5
  class Yahns::QueueQuitter # :nodoc:
6
6
  attr_reader :to_io
7
7
  def initialize
8
- reader, @to_io = IO.pipe
8
+ @reader, @to_io = IO.pipe
9
9
  @to_io.close_on_exec = true
10
- reader.close
11
10
  end
12
11
 
13
12
  def yahns_step
@@ -19,6 +18,7 @@ class Yahns::QueueQuitter # :nodoc:
19
18
  end
20
19
 
21
20
  def close
21
+ @reader.close
22
22
  @to_io.close
23
23
  end
24
24
  end
@@ -0,0 +1,29 @@
1
+ # -*- encoding: binary -*-
2
+ # Copyright (C) 2009-2014, Eric Wong <normalperson@yhbt.net> et. al.
3
+ # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
+ require 'io/extra' # gem install io-extra
5
+
6
+ module Yahns::SendfileCompat
7
+ def trysendfile(io, offset, count)
8
+ return 0 if count == 0
9
+ count = 0x4000 if count > 0x4000
10
+ str = IO.pread(io.fileno, count, offset)
11
+ if count > str.bytesize
12
+ raise EOFError, "end of file reached"
13
+ end
14
+ n = 0
15
+ case rv = kgio_trywrite(str)
16
+ when String # partial write, keep trying
17
+ n += (str.bytesize - rv.bytesize)
18
+ str = rv
19
+ when :wait_writable, :wait_readable
20
+ return n > 0 ? n : rv
21
+ when nil
22
+ return n + str.bytesize # yay!
23
+ end while true
24
+ end
25
+ end
26
+
27
+ class IO
28
+ include Yahns::SendfileCompat
29
+ end
@@ -8,8 +8,8 @@ class Yahns::Sigevent # :nodoc:
8
8
  @to_io.close_on_exec = @wr.close_on_exec = true
9
9
  end
10
10
 
11
- def kgio_wait_readable
12
- @to_io.kgio_wait_readable
11
+ def kgio_wait_readable(*args)
12
+ @to_io.kgio_wait_readable(*args)
13
13
  end
14
14
 
15
15
  def sev_signal
data/lib/yahns/wbuf.rb CHANGED
@@ -30,6 +30,14 @@ require_relative 'wbuf_common'
30
30
  class Yahns::Wbuf # :nodoc:
31
31
  include Yahns::WbufCommon
32
32
 
33
+ # TODO: Figure out why this hack is needed to pass output buffering tests.
34
+ # It could be a bug in our code, Ruby, the sendfile gem, or FreeBSD itself.
35
+ # Tested on FreeBSD fbsd 9.2-RELEASE FreeBSD 9.2-RELEASE #0 r255898
36
+ # We are able to use bypass mode on Linux to reduce buffering in some
37
+ # cases. Without bypass mode, we must always finish writing the entire
38
+ # response completely before sending more data to the client.
39
+ bypass_ok = RUBY_PLATFORM =~ /linux/
40
+
33
41
  def initialize(body, persist, tmpdir)
34
42
  @tmpio = Yahns::TmpIO.new(tmpdir)
35
43
  @sf_offset = @sf_count = 0
@@ -67,7 +75,12 @@ class Yahns::Wbuf # :nodoc:
67
75
  @tmpio.rewind
68
76
  @bypass = true
69
77
  nil
70
- end
78
+ end if bypass_ok
79
+
80
+ def wbuf_write(client, buf)
81
+ @sf_count += @tmpio.write(buf)
82
+ :wait_writable
83
+ end unless bypass_ok
71
84
 
72
85
  # called by last wbuf_flush
73
86
  def wbuf_close(client)
@@ -1,7 +1,12 @@
1
1
  # -*- encoding: binary -*-
2
2
  # Copyright (C) 2009-2013, Eric Wong <normalperson@yhbt.net> et. al.
3
3
  # License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
4
- require 'sendfile'
4
+ if ENV["SENDFILE_BROKEN"]
5
+ require_relative 'sendfile_compat'
6
+ else
7
+ require 'sendfile'
8
+ end
9
+
5
10
  module Yahns::WbufCommon # :nodoc:
6
11
  # returns nil on success, :wait_*able when blocked
7
12
  # currently, we rely on each thread having exclusive access to the
@@ -25,7 +30,8 @@ module Yahns::WbufCommon # :nodoc:
25
30
  raise "BUG: rv=#{rv.inspect} " \
26
31
  "on tmpio=#{@tmpio.inspect} " \
27
32
  "sf_offset=#@sf_offset sf_count=#@sf_count"
28
- end while true
33
+ end while @sf_count > 0
34
+ wbuf_close(client)
29
35
  end
30
36
 
31
37
  def wbuf_close_common(client)
data/test/helper.rb CHANGED
@@ -36,6 +36,12 @@ if ENV["COVERAGE"]
36
36
  # filter out stuff that's not in our project
37
37
  COVMATCH =~ filename or next
38
38
 
39
+ # For compatibility with https://bugs.ruby-lang.org/issues/9508
40
+ # TODO: support those features if that gets merged into mainline
41
+ unless Array === counts
42
+ counts = counts[:lines]
43
+ end
44
+
39
45
  merge = prev[filename] || []
40
46
  merge = merge
41
47
  counts.each_with_index do |count, i|
@@ -9,12 +9,18 @@ class TestBufferTmpdir < Testcase
9
9
  attr_reader :ino, :tmpdir
10
10
 
11
11
  def setup
12
- @ino = SleepyPenguin::Inotify.new(:CLOEXEC)
12
+ @ino = nil
13
+ begin
14
+ @ino = SleepyPenguin::Inotify.new(:CLOEXEC)
15
+ rescue
16
+ skip "test needs inotify"
17
+ end
13
18
  @tmpdir = Dir.mktmpdir
14
19
  server_helper_setup
15
20
  end
16
21
 
17
22
  def teardown
23
+ return unless @ino
18
24
  server_helper_teardown
19
25
  @ino.close
20
26
  FileUtils.rm_rf @tmpdir
@@ -100,4 +106,4 @@ class TestBufferTmpdir < Testcase
100
106
  c.close if c
101
107
  quit_wait(pid)
102
108
  end
103
- end if SleepyPenguin.const_defined?(:Inotify)
109
+ end
@@ -149,9 +149,17 @@ class TestClientExpire < Testcase
149
149
  Process.waitpid2(_pid)
150
150
  end
151
151
  end
152
+
153
+ # this seems to be needed in Debian GNU/kFreeBSD
154
+ linux = !!(RUBY_PLATFORM =~ /linux/)
155
+ sleep(1) unless linux
156
+
152
157
  [ f, s ].each do |io|
153
158
  assert_raises(Errno::EPIPE,Errno::ECONNRESET) do
154
- req.each_byte { |b| io.write(b.chr) }
159
+ req.each_byte do |b|
160
+ io.write(b.chr)
161
+ sleep(0.01) unless linux
162
+ end
155
163
  end
156
164
  io.close
157
165
  end
@@ -10,6 +10,8 @@ class TestMtAccept < Testcase
10
10
  alias teardown server_helper_teardown
11
11
 
12
12
  def test_mt_accept
13
+ skip "Linux kernel required" unless RUBY_PLATFORM =~ /linux/
14
+ skip "/proc not mounted" unless File.directory?("/proc")
13
15
  err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
14
16
  opts = { threads: 1 }
15
17
  cfg.instance_eval do
@@ -45,4 +47,4 @@ class TestMtAccept < Testcase
45
47
  ensure
46
48
  quit_wait(pid)
47
49
  end
48
- end if RUBY_PLATFORM =~ /linux/ && File.directory?("/proc")
50
+ end
data/test/test_server.rb CHANGED
@@ -178,11 +178,14 @@ class TestServer < Testcase
178
178
  end
179
179
 
180
180
  def test_check_client_connection
181
+ tmpdir = Dir.mktmpdir
182
+ sock = "#{tmpdir}/sock"
183
+ unix_srv = UNIXServer.new(sock)
184
+ unix_srv.close_on_exec = true
181
185
  msgs = %w(ZZ zz)
182
186
  err = @err
183
187
  cfg = Yahns::Config.new
184
188
  bpipe = cloexec_pipe
185
- host, port = @srv.addr[3], @srv.addr[1]
186
189
  cfg.instance_eval do
187
190
  ru = lambda { |e|
188
191
  case e['PATH_INFO']
@@ -205,7 +208,7 @@ class TestServer < Testcase
205
208
  }
206
209
  GTL.synchronize {
207
210
  app(:rack, ru) {
208
- listen "#{host}:#{port}"
211
+ listen sock
209
212
  check_client_connection true
210
213
  # needed to avoid concurrency with check_client_connection
211
214
  queue { worker_threads 1 }
@@ -223,12 +226,13 @@ class TestServer < Testcase
223
226
 
224
227
  pid = fork do
225
228
  bpipe[1].close
226
- ENV["YAHNS_FD"] = @srv.fileno.to_s
229
+ ENV["YAHNS_FD"] = unix_srv.fileno.to_s
227
230
  srv.start.join
228
231
  end
229
232
  bpipe[0].close
230
- a = get_tcp_client(host, port)
231
- b = get_tcp_client(host, port)
233
+ a = UNIXSocket.new(sock)
234
+ b = UNIXSocket.new(sock)
235
+ b.close_on_exec = a.close_on_exec = true
232
236
  a.write("GET /sleep HTTP/1.0\r\n\r\n")
233
237
  r = IO.select([a], nil, nil, 4)
234
238
  assert r, "nothing ready"
@@ -250,10 +254,19 @@ class TestServer < Testcase
250
254
  assert_equal msgs.join, buf.split(/\r\n\r\n/)[1]
251
255
 
252
256
  # do things still work?
253
- run_client(host, port) { |res| assert_equal "HI", res.body }
257
+ c = UNIXSocket.new(sock)
258
+ c.write "GET /\r\n\r\n"
259
+ assert_equal "HI", c.read
260
+ c.close
254
261
  a.close
262
+ rescue => e
263
+ warn e.class
264
+ warn e.message
265
+ warn e.backtrace.join("\n")
255
266
  ensure
267
+ unix_srv.close
256
268
  quit_wait(pid)
269
+ FileUtils.rm_rf(tmpdir)
257
270
  end
258
271
 
259
272
  def test_mp
data/test/test_wbuf.rb CHANGED
@@ -81,8 +81,8 @@ class TestWbuf < Testcase
81
81
  assert_equal b, IO.select([b], nil, nil, 5)[0][0]
82
82
  b.read(nr - 2) if nr > 2
83
83
  assert_equal b, IO.select([b], nil, nil, 5)[0][0]
84
- assert_equal "HI", b.read(2)
85
- assert_equal false, wbuf.wbuf_flush(a)
84
+ assert_equal "HI", b.read(2), "read the end of the response"
85
+ assert_equal true, wbuf.wbuf_flush(a)
86
86
  ensure
87
87
  a.close
88
88
  b.close
data/yahns.gemspec CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |s|
12
12
  s.files = manifest
13
13
  s.add_dependency(%q<kgio>, '~> 2.9')
14
14
  s.add_dependency(%q<sleepy_penguin>, '~> 3.2')
15
- s.add_dependency(%q<sendfile>, '~> 1.2.1')
15
+ s.add_dependency(%q<kgio-sendfile>, '~> 1.2')
16
16
  s.add_dependency(%q<unicorn>, '~> 4.6', '>= 4.6.3')
17
17
 
18
18
  # minitest is standard in Ruby 2.0, 4.3 is packaged with Ruby 2.0.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.1.0
4
+ version: 1.2.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: 2014-02-04 00:00:00.000000000 Z
11
+ date: 2014-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio
@@ -39,19 +39,19 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.2'
41
41
  - !ruby/object:Gem::Dependency
42
- name: sendfile
42
+ name: kgio-sendfile
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 1.2.1
47
+ version: '1.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 1.2.1
54
+ version: '1.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: unicorn
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -152,10 +152,12 @@ files:
152
152
  - lib/yahns/queue.rb
153
153
  - lib/yahns/queue_egg.rb
154
154
  - lib/yahns/queue_epoll.rb
155
+ - lib/yahns/queue_kqueue.rb
155
156
  - lib/yahns/queue_quitter.rb
156
157
  - lib/yahns/queue_quitter_pipe.rb
157
158
  - lib/yahns/rack.rb
158
159
  - lib/yahns/rackup_handler.rb
160
+ - lib/yahns/sendfile_compat.rb
159
161
  - lib/yahns/server.rb
160
162
  - lib/yahns/server_mp.rb
161
163
  - lib/yahns/sigevent.rb
@@ -223,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
223
225
  version: '0'
224
226
  requirements: []
225
227
  rubyforge_project:
226
- rubygems_version: 2.2.0
228
+ rubygems_version: 2.2.2
227
229
  signing_key:
228
230
  specification_version: 4
229
231
  summary: sleepy, multi-threaded, non-blocking application server