yahns 1.12.0 → 1.12.1

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: 0200645323cf0450da2a840cea7660fa17470606
4
- data.tar.gz: 85b6a4bb94c485538c4f9ed715985072a7409321
3
+ metadata.gz: 9fd3617f72db8e2c7a45a5b0273571b721c9cfad
4
+ data.tar.gz: 144a50bd647079d46b5e78a80b659eed0b107a8d
5
5
  SHA512:
6
- metadata.gz: 5bdda8b713ed199f2ffce4bcc455f5805eb9f31143ac6bdefc87794426904e577bd7da0867097ac5e4c32434ce0c43250cadf7b0583d6c5913e907ae457e9511
7
- data.tar.gz: 3bd31164da8bbb367c8db649b470c7b64edb71dfde446901923d3f63e6eef0e4d555f27b28459db9ca12a1a916dfbd7e2de3070cab335c5262f93ac32c5a19f9
6
+ metadata.gz: 606ab401acd65b522b5f340103f958d68ff4ff4ca3d2f7b7d563115e0b53bf9dd27a207c2581b6bc3ec0bdfad92b3a47ff1e75bec53d8d1e1223e5023e6e0f66
7
+ data.tar.gz: c1b03ad4decab3a575873e8925d207809ab9201c805525eaa97d5d2822eb9d797c47354220035d4ef01eadc3a2f2dd2c5faf7de8ce34d327b071c6f398fb47db
@@ -6,7 +6,6 @@ INSTALL = install
6
6
  POD2MAN = pod2man
7
7
  -include ../GIT-VERSION-FILE
8
8
  release := yahns $(VERSION)
9
- PANDOC_OPTS = -f markdown --email-obfuscation=none
10
9
  POD2MAN_OPTS = -v -r '$(release)' --stderr -d 1994-10-02 -c 'yahns user manual'
11
10
  pod2man = $(POD2MAN) $(POD2MAN_OPTS)
12
11
  POD2TEXT = pod2text
@@ -24,12 +24,12 @@ nothing but accepting sockets and injecting into to the event queue
24
24
  worker thread pool
25
25
  ------------------
26
26
 
27
- This is where all the interesting application dispatch happens in yahns.
28
- epoll(2) (or kqueue(2)) descriptor is the heart of event queue. This
29
- design allows clients to migrate between different threads as they
30
- become active, preventing head-of-line blocking in traditional designs
31
- where a client is pinned to a thread (at the cost of weaker cache
32
- locality).
27
+ This is where all the interesting application dispatch happens in
28
+ yahns. A descriptor returned by epoll_create1(2) (or kqueue(2)) is
29
+ the heart of event queue. This design allows clients to migrate
30
+ between different threads as they become active, preventing
31
+ head-of-line blocking in traditional designs where a client is
32
+ pinned to a thread (at the cost of weaker cache locality).
33
33
 
34
34
  The critical component for implementing this thread pool is "one-shot"
35
35
  notifications in the epoll and kqueue APIs, allowing them to be used as
@@ -37,8 +37,8 @@ readiness queues for feeding the thread pool. Used correctly, this
37
37
  allows us to guarantee exclusive access to a client socket without
38
38
  additional locks managed in userspace.
39
39
 
40
- Idle threads will sit performing epoll_wait (or kqueue) indefinitely
41
- until a socket is reported as "ready" by the kernel.
40
+ Idle threads will sit performing epoll_wait(2) (or kevent(2))
41
+ indefinitely until a socket is reported as "ready" by the kernel.
42
42
 
43
43
  queue flow
44
44
  ----------
@@ -46,7 +46,7 @@ queue flow
46
46
  Once a client is accept(2)-ed, it is immediately pushed into the worker
47
47
  thread pool (via EPOLL_CTL_ADD or EV_ADD). This mimics the effect of
48
48
  TCP_DEFER_ACCEPT (in Linux) and the "dataready" accept filter (in
49
- FreeBSD) from the perspective of the epoll_wait(2)/kqueue(2) caller.
49
+ FreeBSD) from the perspective of the epoll_wait(2)/kevent(2) caller.
50
50
  No explicit locking controlled from userspace is necessary.
51
51
 
52
52
  TCP_DEFER_ACCEPT/"dataready"/"httpready" themselves are not used as it
@@ -70,12 +70,13 @@ have completed processing.
70
70
 
71
71
  "Yielding" a client is accomplished by re-arming the already "ready"
72
72
  socket by using EPOLL_CTL_MOD (with EPOLLONESHOT) with a one-shot
73
- notification requeues the descriptor at the end of the internal epoll
74
- ready queue; achieving a similar effect to yielding a thread (via
75
- sched_yield or Thread.pass) in a purely multi-threaded design.
73
+ notification requeues the descriptor at the end of the internal
74
+ epoll (or kevent) ready queue; achieving a similar effect to
75
+ yielding a thread (via sched_yield or Thread.pass) in a purely
76
+ multi-threaded design.
76
77
 
77
- Once the client is yielded, epoll_wait is called again to pull
78
- the next client off the ready queue.
78
+ Once the client is yielded, epoll_wait or kevent is called again to
79
+ pull the next client off the ready queue.
79
80
 
80
81
  Output buffering notes
81
82
  ----------------------
@@ -110,7 +110,7 @@ be given without a block to associate an app block with a named
110
110
  queue.
111
111
 
112
112
  Usually, only one queue is necessary. Each queue corresponds to
113
- an epoll descriptor and worker thread pool.
113
+ an epoll or kqueue descriptor and worker thread pool.
114
114
 
115
115
  Default: NAME defaults to :default
116
116
 
@@ -161,9 +161,10 @@ Default: / if daemonized, current working directory if not
161
161
  =item max_events INTEGER
162
162
 
163
163
  This controls the number of events a worker thread will fetch at
164
- once via L<epoll_wait(2)>. There is no good reason to change this
164
+ once via L<epoll_wait(2)> or L<kevent(2)>.
165
+ There is no good reason to change this
165
166
  unless you use very few (e.g. 1) worker_threads. Leaving this at
166
- 1 will give the fairest load balancing behavior with epoll.
167
+ 1 will give the fairest load balancing behavior with epoll or kqueue.
167
168
 
168
169
  Default: 1
169
170
 
@@ -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.12.0"
8
+ DEF_VER = "v1.12.1"
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
@@ -4,10 +4,10 @@ development dependencies
4
4
  * minitest RubyGem (version 4 or 5, standard in Ruby 2.0+)
5
5
  * curl - http://curl.haxx.se/ - we don't trust our own Ruby abilities :>
6
6
  * dd(1) - standard POSIX tool - to feed curl
7
- * ab - http://httpd.apache.org - for concurrent testing
7
+ * ab - http://httpd.apache.org/ - for concurrent testing
8
8
  * GNU make - https://www.gnu.org/software/make/
9
9
  * git - http://www.git-scm.com/
10
- * ruby - http://www.ruby-lang.org/
10
+ * ruby - http://www.ruby-lang.org/en/
11
11
 
12
12
  git clone git://yhbt.net/yahns
13
13
 
@@ -32,14 +32,12 @@ V - set to 1 for verbose test output (may be mangled if multithreaded)
32
32
  documentation
33
33
  -------------
34
34
 
35
- We use pandoc for converting Markdown to (groff) man:
36
-
37
- * pandoc - http://johnmacfarlane.net/pandoc/
35
+ We use pod2man(1) distributed with Perl 5 for generating manpages.
38
36
 
39
37
  installing from git
40
38
  -------------------
41
39
 
42
- * make install-gem (pandoc is required for generating manpages)
40
+ * make install-gem
43
41
 
44
42
  contact
45
43
  -------
@@ -51,7 +49,7 @@ formatted using git-request-pull(1).
51
49
 
52
50
  Mailing list archives: http://yhbt.net/yahns-public/
53
51
  No subscription is necessary to post to the mailing list.
54
- Please remember to Cc: all recipients.
52
+ Please remember to Cc: all recipients as subscription is optional.
55
53
 
56
54
  Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
57
55
  License: GPL-3.0+ <http://www.gnu.org/licenses/gpl-3.0.txt>
data/README CHANGED
@@ -102,17 +102,14 @@ multiple threads.
102
102
  1. blocking acceptors
103
103
  2. non-blocking event loop workers
104
104
  * epoll (or kqueue) acts as a queue (by using one-shot notifications)
105
- * acceptors accept new clients and put them in the epoll "queue"
105
+ * acceptors accept new clients and put them in the queue
106
106
  * workers pull clients off the queue, rearming them to epoll on EAGAIN
107
107
 
108
108
  The end result is clients transition freely and fairly between threads
109
109
  and will always be able to find the next idle thread to run on.
110
110
 
111
- This design works with kqueue, too, and we support kqueue. In fact, got
112
- our design inspiration from the name "kqueue" when working on another
113
- project. We may also support libkqueue:
114
-
115
- http://sourceforge.net/projects/libkqueue/
111
+ The design inspiration from the name "kqueue" when working on another
112
+ project.
116
113
 
117
114
  In addition to multiple threads, yahns optionally supports multiple
118
115
  processes to work around low FD limits as well as contention in the:
@@ -125,7 +122,7 @@ processes to work around low FD limits as well as contention in the:
125
122
  Copyright
126
123
  ---------
127
124
 
128
- Copyright 2013-2015, all contributors (see git repo).
125
+ Copyright 2013-2016, all contributors (see git repo).
129
126
  License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
130
127
 
131
128
  yahns is copyrighted Free Software by all contributors, see logs in
@@ -14,13 +14,29 @@ class Autoindex
14
14
  FN = %{<a href="%s">%s</a>}
15
15
  TFMT = "%Y-%m-%d %H:%M"
16
16
 
17
- def initialize(app, index = %w(index.html), skip_gzip_static = true)
17
+ def initialize(app, *args)
18
18
  app.respond_to?(:root) or raise ArgumentError,
19
- "wrapped app #{app.inspect} does not respond to :root"
19
+ "wrapped app #{app.inspect} does not respond to #root"
20
20
  @app = app
21
21
  @root = app.root
22
- @index = index
23
- @skip_gz = skip_gzip_static
22
+
23
+ @index = case args[0]
24
+ when Array then args.shift
25
+ when String then Array(args.shift)
26
+ else
27
+ %w(index.html)
28
+ end
29
+
30
+ @skip_gzip_static = @skip_dotfiles = nil
31
+ case args[0]
32
+ when Hash
33
+ @skip_gzip_static = args[0][:skip_gzip_static]
34
+ @skip_dotfiles = args[0][:skip_dotfiles]
35
+ when true, false
36
+ @skip_gzip_static = args.shift
37
+ end
38
+ @skip_gzip_static = true if @skip_gzip_static.nil?
39
+ @skip_dotfiles = false if @skip_dotfiles.nil?
24
40
  end
25
41
 
26
42
  def redirect_slash(env)
@@ -69,13 +85,15 @@ def call(env)
69
85
  # generate the index, show directories first
70
86
  dirs = []
71
87
  files = []
72
- ngz_idx = {} if @skip_gz # used to avoid redundant stat()
88
+ ngz_idx = {} if @skip_gzip_static # used to avoid redundant stat()
73
89
  dir.each do |base|
74
90
  case base
75
91
  when "."
76
92
  next
77
93
  when ".."
78
94
  next if path_info == "/"
95
+ when /\A\./
96
+ next if @skip_dotfiles
79
97
  end
80
98
 
81
99
  begin
@@ -206,8 +206,17 @@ def app_call(input)
206
206
  end
207
207
  end
208
208
 
209
+ env.merge!(k.app_defaults)
210
+
211
+ # workaround stupid unicorn_http parser behavior when it parses HTTP_HOST
212
+ if env['HTTPS'] == 'on'.freeze &&
213
+ env['HTTP_HOST'] &&
214
+ env['SERVER_PORT'] == '80'.freeze
215
+ env['SERVER_PORT'] = '443'.freeze
216
+ end
217
+
209
218
  # run the rack app
210
- status, headers, body = k.app.call(env.merge!(k.app_defaults))
219
+ status, headers, body = k.app.call(env)
211
220
  return :ignore if app_hijacked?(env, body)
212
221
  if status.to_i == 100
213
222
  rv = http_100_response(env) and return rv
@@ -1,3 +1,4 @@
1
+ # -*- encoding: binary -*-
1
2
  # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
2
3
  # License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
3
4
  # frozen_string_literal: true
@@ -7,8 +8,6 @@
7
8
  # this is to be included into a Kgio::Socket-derived class
8
9
  # this requires Ruby 2.1 and later for "exception: false"
9
10
  module Yahns::OpenSSLClient # :nodoc:
10
- include Yahns::SendfileCompat
11
-
12
11
  def self.included(cls)
13
12
  # Forward these methods to OpenSSL::SSL::SSLSocket so hijackers
14
13
  # can rely on stdlib methods instead of ugly kgio stuff that
@@ -37,15 +36,25 @@ def sync
37
36
  def yahns_init_ssl(ssl_ctx)
38
37
  @need_accept = true
39
38
  @ssl = OpenSSL::SSL::SSLSocket.new(self, ssl_ctx)
39
+ @ssl_blocked = nil
40
40
  end
41
41
 
42
42
  def kgio_trywrite(buf)
43
- rv = @ssl.write_nonblock(buf, exception: false)
44
- Integer === rv and
45
- rv = buf.bytesize == rv ? nil : buf.byteslice(rv, buf.bytesize)
43
+ buf = @ssl_blocked = buf.dup
44
+ case rv = @ssl.write_nonblock(buf, exception: false)
45
+ when :wait_readable, :wait_writable
46
+ return rv # do not clear ssl_blocked
47
+ when Integer
48
+ rv = buf.bytesize == rv ? nil : buf.byteslice(rv, buf.bytesize - rv)
49
+ end
50
+ @ssl_blocked = nil
46
51
  rv
47
52
  end
48
53
 
54
+ def kgio_trywritev(buf)
55
+ kgio_trywrite(buf.join)
56
+ end
57
+
49
58
  def kgio_syssend(buf, flags)
50
59
  kgio_trywrite(buf)
51
60
  end
@@ -63,6 +72,27 @@ def kgio_tryread(len, buf)
63
72
  @ssl.read_nonblock(len, buf, exception: false)
64
73
  end
65
74
 
75
+ def trysendfile(io, offset, count)
76
+ return 0 if count == 0
77
+
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
83
+ buf = @ssl_blocked = buf.dup
84
+ end
85
+
86
+ # call write_nonblock directly since kgio_trywrite allocates
87
+ # an unnecessary string
88
+ case rv = @ssl.write_nonblock(buf, exception: false)
89
+ when :wait_readable, :wait_writable
90
+ return rv # do not clear ssl_blocked
91
+ end
92
+ @ssl_blocked = nil
93
+ rv
94
+ end
95
+
66
96
  def close
67
97
  @ssl.close # flushes SSLSocket
68
98
  super # IO#close
@@ -71,7 +71,7 @@ def test_ssl_basic
71
71
  cfg.instance_eval do
72
72
  ru = lambda do |env|
73
73
  case path_info = env['PATH_INFO']
74
- when '/rack.url_scheme', '/HTTPS'
74
+ when '/rack.url_scheme', '/HTTPS', '/SERVER_PORT'
75
75
  s = env[path_info[1..-1]] # remove leading slash
76
76
  s = s.inspect if s.nil?
77
77
  [ 200, {
@@ -100,7 +100,8 @@ def test_ssl_basic
100
100
  buf = ''.dup
101
101
  { '/' => 'HI',
102
102
  '/rack.url_scheme' => 'https',
103
- '/HTTPS' => 'on'
103
+ '/HTTPS' => 'on',
104
+ '/SERVER_PORT' => '443',
104
105
  }.each do |path, exp|
105
106
  client.write("GET #{path} HTTP/1.1\r\nHost: example.com\r\n\r\n")
106
107
  buf.clear
@@ -113,11 +114,25 @@ def test_ssl_basic
113
114
  assert_match %r{\AHTTP/1\.\d 200 OK\r\n}, head
114
115
  end
115
116
 
117
+ # use port in Host: header (implemented by unicorn_http parser)
118
+ exp = '666'
119
+ client.write("GET /SERVER_PORT HTTP/1.1\r\nHost: example.com:#{exp}\r\n\r\n")
120
+ re = /#{Regexp.escape(exp)}\z/
121
+ buf.clear
122
+ Timeout.timeout(60) do
123
+ buf << client.readpartial(111) until buf =~ re
124
+ end
125
+ head, body = buf.split("\r\n\r\n", 2)
126
+ assert_equal exp, body
127
+ assert_match %r{\AHTTP/1\.\d 200 OK\r\n}, head
128
+
116
129
  Net::HTTP.start(insecure.addr[3], insecure.addr[1]) do |h|
117
130
  res = h.get('/rack.url_scheme')
118
131
  assert_equal 'http', res.body
119
132
  res = h.get('/HTTPS')
120
133
  assert_equal 'nil', res.body
134
+ res = h.get('/SERVER_PORT')
135
+ assert_equal insecure.addr[1].to_s, res.body
121
136
  end
122
137
 
123
138
  # read static file
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.12.0
4
+ version: 1.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - yahns hackers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-14 00:00:00.000000000 Z
11
+ date: 2016-02-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kgio