unicorn 6.0.0 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|