unicorn 6.0.0 → 6.1.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 +4 -4
- data/.manifest +3 -0
- data/.olddoc.yml +7 -4
- data/CONTRIBUTORS +6 -2
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/HACKING +1 -8
- data/ISSUES +18 -20
- data/LATEST +23 -22
- data/NEWS +132 -0
- data/README +5 -6
- data/Sandbox +1 -1
- data/ext/unicorn_http/c_util.h +5 -13
- data/ext/unicorn_http/common_field_optimization.h +0 -1
- data/ext/unicorn_http/epollexclusive.h +124 -0
- data/ext/unicorn_http/ext_help.h +0 -24
- data/ext/unicorn_http/extconf.rb +2 -11
- data/ext/unicorn_http/global_variables.h +1 -1
- data/ext/unicorn_http/httpdate.c +1 -0
- data/ext/unicorn_http/unicorn_http.c +215 -223
- data/ext/unicorn_http/unicorn_http.rl +5 -13
- data/lib/unicorn/http_server.rb +21 -26
- data/lib/unicorn/select_waiter.rb +6 -0
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn.rb +0 -2
- data/t/README +1 -1
- data/t/test-lib.sh +2 -1
- data/test/unit/test_util.rb +4 -3
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +3 -3
- metadata +7 -3
@@ -12,6 +12,7 @@
|
|
12
12
|
#include "common_field_optimization.h"
|
13
13
|
#include "global_variables.h"
|
14
14
|
#include "c_util.h"
|
15
|
+
#include "epollexclusive.h"
|
15
16
|
|
16
17
|
void init_unicorn_httpdate(void);
|
17
18
|
|
@@ -65,18 +66,6 @@ struct http_parser {
|
|
65
66
|
static ID id_set_backtrace, id_is_chunked_p;
|
66
67
|
static VALUE cHttpParser;
|
67
68
|
|
68
|
-
#ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
|
69
|
-
# define my_hash_clear(h) (void)rb_hash_clear(h)
|
70
|
-
#else /* !HAVE_RB_HASH_CLEAR - Ruby <= 1.9.3 */
|
71
|
-
|
72
|
-
static ID id_clear;
|
73
|
-
|
74
|
-
static void my_hash_clear(VALUE h)
|
75
|
-
{
|
76
|
-
rb_funcall(h, id_clear, 0);
|
77
|
-
}
|
78
|
-
#endif /* HAVE_RB_HASH_CLEAR */
|
79
|
-
|
80
69
|
static void finalize_header(struct http_parser *hp);
|
81
70
|
|
82
71
|
static void parser_raise(VALUE klass, const char *msg)
|
@@ -650,7 +639,7 @@ static VALUE HttpParser_clear(VALUE self)
|
|
650
639
|
return HttpParser_init(self);
|
651
640
|
|
652
641
|
http_parser_init(hp);
|
653
|
-
|
642
|
+
rb_hash_clear(hp->env);
|
654
643
|
|
655
644
|
return self;
|
656
645
|
}
|
@@ -979,6 +968,7 @@ void Init_unicorn_http(void)
|
|
979
968
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
980
969
|
eHttpParserError);
|
981
970
|
|
971
|
+
id_uminus = rb_intern("-@");
|
982
972
|
init_globals();
|
983
973
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
984
974
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
@@ -1029,5 +1019,7 @@ void Init_unicorn_http(void)
|
|
1029
1019
|
id_clear = rb_intern("clear");
|
1030
1020
|
#endif
|
1031
1021
|
id_is_chunked_p = rb_intern("is_chunked?");
|
1022
|
+
|
1023
|
+
init_epollexclusive(mUnicorn);
|
1032
1024
|
}
|
1033
1025
|
#undef SET_GLOBAL
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -609,7 +609,7 @@ class Unicorn::HttpServer
|
|
609
609
|
def e100_response_write(client, env)
|
610
610
|
# We use String#freeze to avoid allocations under Ruby 2.1+
|
611
611
|
# Not many users hit this code path, so it's better to reduce the
|
612
|
-
# constant table sizes even for
|
612
|
+
# constant table sizes even for Ruby 2.0 users who'll hit extra
|
613
613
|
# allocations here.
|
614
614
|
client.write(@request.response_start_sent ?
|
615
615
|
"100 Continue\r\n\r\nHTTP/1.1 ".freeze :
|
@@ -685,7 +685,6 @@ class Unicorn::HttpServer
|
|
685
685
|
LISTENERS.each { |sock| sock.close_on_exec = true }
|
686
686
|
|
687
687
|
worker.user(*user) if user.kind_of?(Array) && ! worker.switched
|
688
|
-
self.timeout /= 2.0 # halve it for select()
|
689
688
|
@config = nil
|
690
689
|
build_app! unless preload_app
|
691
690
|
@after_fork = @listener_opts = @orig_app = nil
|
@@ -699,59 +698,55 @@ class Unicorn::HttpServer
|
|
699
698
|
logger.info "worker=#{worker_nr} reopening logs..."
|
700
699
|
Unicorn::Util.reopen_logs
|
701
700
|
logger.info "worker=#{worker_nr} done reopening logs"
|
701
|
+
false
|
702
702
|
rescue => e
|
703
703
|
logger.error(e) rescue nil
|
704
704
|
exit!(77) # EX_NOPERM in sysexits.h
|
705
705
|
end
|
706
706
|
|
707
|
+
def prep_readers(readers)
|
708
|
+
wtr = Unicorn::Waiter.prep_readers(readers)
|
709
|
+
@timeout *= 500 # to milliseconds for epoll, but halved
|
710
|
+
wtr
|
711
|
+
rescue
|
712
|
+
require_relative 'select_waiter'
|
713
|
+
@timeout /= 2.0 # halved for IO.select
|
714
|
+
Unicorn::SelectWaiter.new
|
715
|
+
end
|
716
|
+
|
707
717
|
# runs inside each forked worker, this sits around and waits
|
708
718
|
# for connections and doesn't die until the parent dies (or is
|
709
719
|
# given a INT, QUIT, or TERM signal)
|
710
720
|
def worker_loop(worker)
|
711
|
-
ppid = @master_pid
|
712
721
|
readers = init_worker_process(worker)
|
713
|
-
|
722
|
+
waiter = prep_readers(readers)
|
723
|
+
reopen = false
|
714
724
|
|
715
725
|
# this only works immediately if the master sent us the signal
|
716
726
|
# (which is the normal case)
|
717
|
-
trap(:USR1) {
|
727
|
+
trap(:USR1) { reopen = true }
|
718
728
|
|
719
729
|
ready = readers.dup
|
720
|
-
nr_listeners = readers.size
|
721
730
|
@after_worker_ready.call(self, worker)
|
722
731
|
|
723
732
|
begin
|
724
|
-
|
725
|
-
nr = 0
|
733
|
+
reopen = reopen_worker_logs(worker.nr) if reopen
|
726
734
|
worker.tick = time_now.to_i
|
727
|
-
|
728
|
-
while sock = tmp.shift
|
735
|
+
while sock = ready.shift
|
729
736
|
# Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
|
730
737
|
# but that will return false
|
731
738
|
if client = sock.kgio_tryaccept
|
732
739
|
process_client(client)
|
733
|
-
nr += 1
|
734
740
|
worker.tick = time_now.to_i
|
735
741
|
end
|
736
|
-
break if
|
742
|
+
break if reopen
|
737
743
|
end
|
738
744
|
|
739
|
-
#
|
740
|
-
# we're probably reasonably busy, so avoid calling select()
|
741
|
-
# and do a speculative non-blocking accept() on ready listeners
|
742
|
-
# before we sleep again in select().
|
743
|
-
if nr == nr_listeners
|
744
|
-
tmp = ready.dup
|
745
|
-
redo
|
746
|
-
end
|
747
|
-
|
748
|
-
ppid == Process.ppid or return
|
749
|
-
|
750
|
-
# timeout used so we can detect parent death:
|
745
|
+
# timeout so we can .tick and keep parent from SIGKILL-ing us
|
751
746
|
worker.tick = time_now.to_i
|
752
|
-
|
747
|
+
waiter.get_readers(ready, readers, @timeout)
|
753
748
|
rescue => e
|
754
|
-
redo if
|
749
|
+
redo if reopen && readers[0]
|
755
750
|
Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
|
756
751
|
end while readers[0]
|
757
752
|
end
|
data/lib/unicorn/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Unicorn::Const::UNICORN_VERSION = '6.
|
1
|
+
Unicorn::Const::UNICORN_VERSION = '6.1.0'
|
data/lib/unicorn.rb
CHANGED
@@ -114,8 +114,6 @@ module Unicorn
|
|
114
114
|
|
115
115
|
def self.pipe # :nodoc:
|
116
116
|
Kgio::Pipe.new.each do |io|
|
117
|
-
io.close_on_exec = true # remove this when we only support Ruby >= 2.0
|
118
|
-
|
119
117
|
# shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
|
120
118
|
# limits.
|
121
119
|
if defined?(F_SETPIPE_SZ)
|
data/t/README
CHANGED
@@ -10,7 +10,7 @@ comfortable writing integration tests with.
|
|
10
10
|
|
11
11
|
== Requirements
|
12
12
|
|
13
|
-
* {Ruby
|
13
|
+
* {Ruby 2.0.0+}[https://www.ruby-lang.org/en/] (duh!)
|
14
14
|
* {GNU make}[https://www.gnu.org/software/make/]
|
15
15
|
* {socat}[http://www.dest-unreach.org/socat/]
|
16
16
|
* {curl}[https://curl.haxx.se/]
|
data/t/test-lib.sh
CHANGED
@@ -94,7 +94,8 @@ check_stderr () {
|
|
94
94
|
set +u
|
95
95
|
_r_err=${1-${r_err}}
|
96
96
|
set -u
|
97
|
-
if grep -v $T $_r_err | grep -i Error
|
97
|
+
if grep -v $T $_r_err | grep -i Error | \
|
98
|
+
grep -v NameError.*Unicorn::Waiter
|
98
99
|
then
|
99
100
|
die "Errors found in $_r_err"
|
100
101
|
elif grep SIGKILL $_r_err
|
data/test/unit/test_util.rb
CHANGED
@@ -51,7 +51,7 @@ class TestUtil < Test::Unit::TestCase
|
|
51
51
|
def test_reopen_logs_renamed_with_encoding
|
52
52
|
tmp = Tempfile.new('')
|
53
53
|
tmp_path = tmp.path.dup.freeze
|
54
|
-
Encoding.list.each { |encoding|
|
54
|
+
Encoding.list.sample(5).each { |encoding|
|
55
55
|
File.open(tmp_path, "a:#{encoding.to_s}") { |fp|
|
56
56
|
fp.sync = true
|
57
57
|
assert_equal encoding, fp.external_encoding
|
@@ -74,8 +74,9 @@ class TestUtil < Test::Unit::TestCase
|
|
74
74
|
def test_reopen_logs_renamed_with_internal_encoding
|
75
75
|
tmp = Tempfile.new('')
|
76
76
|
tmp_path = tmp.path.dup.freeze
|
77
|
-
Encoding.list
|
78
|
-
|
77
|
+
full = Encoding.list
|
78
|
+
full.sample(2).each { |ext|
|
79
|
+
full.sample(2).each { |int|
|
79
80
|
next if ext == int
|
80
81
|
File.open(tmp_path, "a:#{ext.to_s}:#{int.to_s}") { |fp|
|
81
82
|
fp.sync = true
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'unicorn'
|
3
|
+
require 'unicorn/select_waiter'
|
4
|
+
class TestSelectWaiter < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_select_timeout # n.b. this is level-triggered
|
7
|
+
sw = Unicorn::SelectWaiter.new
|
8
|
+
IO.pipe do |r,w|
|
9
|
+
sw.get_readers(ready = [], [r], 0)
|
10
|
+
assert_equal [], ready
|
11
|
+
w.syswrite '.'
|
12
|
+
sw.get_readers(ready, [r], 1000)
|
13
|
+
assert_equal [r], ready
|
14
|
+
sw.get_readers(ready, [r], 0)
|
15
|
+
assert_equal [r], ready
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_linux # ugh, also level-triggered, unlikely to change
|
20
|
+
IO.pipe do |r,w|
|
21
|
+
wtr = Unicorn::Waiter.prep_readers([r])
|
22
|
+
wtr.get_readers(ready = [], [r], 0)
|
23
|
+
assert_equal [], ready
|
24
|
+
w.syswrite '.'
|
25
|
+
wtr.get_readers(ready = [], [r], 1000)
|
26
|
+
assert_equal [r], ready
|
27
|
+
wtr.get_readers(ready = [], [r], 1000)
|
28
|
+
assert_equal [r], ready, 'still ready (level-triggered :<)'
|
29
|
+
assert_nil wtr.close
|
30
|
+
end
|
31
|
+
rescue SystemCallError => e
|
32
|
+
warn "#{e.message} (#{e.class})"
|
33
|
+
end if Unicorn.const_defined?(:Waiter)
|
34
|
+
end
|
data/unicorn.gemspec
CHANGED
@@ -11,7 +11,7 @@ end.compact
|
|
11
11
|
|
12
12
|
Gem::Specification.new do |s|
|
13
13
|
s.name = %q{unicorn}
|
14
|
-
s.version = (ENV['VERSION'] || '6.
|
14
|
+
s.version = (ENV['VERSION'] || '6.1.0').dup
|
15
15
|
s.authors = ['unicorn hackers']
|
16
16
|
s.summary = 'Rack HTTP server for fast clients and Unix'
|
17
17
|
s.description = File.read('README').split("\n\n")[1]
|
@@ -25,11 +25,11 @@ Gem::Specification.new do |s|
|
|
25
25
|
s.homepage = 'https://yhbt.net/unicorn/'
|
26
26
|
s.test_files = test_files
|
27
27
|
|
28
|
-
#
|
28
|
+
# 2.0.0 is the minimum supported version. We don't specify
|
29
29
|
# a maximum version to make it easier to test pre-releases,
|
30
30
|
# but we do warn users if they install unicorn on an untested
|
31
31
|
# version in extconf.rb
|
32
|
-
s.required_ruby_version = ">=
|
32
|
+
s.required_ruby_version = ">= 2.0.0"
|
33
33
|
|
34
34
|
# We do not have a hard dependency on rack, it's possible to load
|
35
35
|
# things which respond to #call. HTTP status lines in responses
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- unicorn hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -157,6 +157,7 @@ files:
|
|
157
157
|
- ext/unicorn_http/CFLAGS
|
158
158
|
- ext/unicorn_http/c_util.h
|
159
159
|
- ext/unicorn_http/common_field_optimization.h
|
160
|
+
- ext/unicorn_http/epollexclusive.h
|
160
161
|
- ext/unicorn_http/ext_help.h
|
161
162
|
- ext/unicorn_http/extconf.rb
|
162
163
|
- ext/unicorn_http/global_variables.h
|
@@ -176,6 +177,7 @@ files:
|
|
176
177
|
- lib/unicorn/launcher.rb
|
177
178
|
- lib/unicorn/oob_gc.rb
|
178
179
|
- lib/unicorn/preread_input.rb
|
180
|
+
- lib/unicorn/select_waiter.rb
|
179
181
|
- lib/unicorn/socket_helper.rb
|
180
182
|
- lib/unicorn/stream_input.rb
|
181
183
|
- lib/unicorn/tee_input.rb
|
@@ -266,6 +268,7 @@ files:
|
|
266
268
|
- test/unit/test_tee_input.rb
|
267
269
|
- test/unit/test_upload.rb
|
268
270
|
- test/unit/test_util.rb
|
271
|
+
- test/unit/test_waiter.rb
|
269
272
|
- unicorn.gemspec
|
270
273
|
- unicorn_1
|
271
274
|
- unicorn_rails_1
|
@@ -282,7 +285,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
282
285
|
requirements:
|
283
286
|
- - ">="
|
284
287
|
- !ruby/object:Gem::Version
|
285
|
-
version:
|
288
|
+
version: 2.0.0
|
286
289
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
287
290
|
requirements:
|
288
291
|
- - ">="
|
@@ -301,3 +304,4 @@ test_files:
|
|
301
304
|
- test/unit/test_server.rb
|
302
305
|
- test/unit/test_upload.rb
|
303
306
|
- test/unit/test_util.rb
|
307
|
+
- test/unit/test_waiter.rb
|