unicorn 5.8.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 -2
- 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_request.rb +0 -1
- data/lib/unicorn/http_server.rb +22 -27
- data/lib/unicorn/oob_gc.rb +3 -3
- 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/exec/test_exec.rb +5 -5
- data/test/test_helper.rb +18 -2
- data/test/unit/test_ccc.rb +4 -3
- data/test/unit/test_server.rb +10 -10
- data/test/unit/test_signals.rb +6 -6
- data/test/unit/test_socket_helper.rb +1 -1
- data/test/unit/test_upload.rb +5 -5
- data/test/unit/test_util.rb +4 -3
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +3 -3
- metadata +7 -5
- data/t/hijack.ru +0 -55
- data/t/t0200-rack-hijack.sh +0 -51
@@ -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_request.rb
CHANGED
data/lib/unicorn/http_server.rb
CHANGED
@@ -69,7 +69,6 @@ class Unicorn::HttpServer
|
|
69
69
|
# incoming requests on the socket.
|
70
70
|
def initialize(app, options = {})
|
71
71
|
@app = app
|
72
|
-
@request = Unicorn::HttpRequest.new
|
73
72
|
@reexec_pid = 0
|
74
73
|
@default_middleware = true
|
75
74
|
options = options.dup
|
@@ -610,7 +609,7 @@ class Unicorn::HttpServer
|
|
610
609
|
def e100_response_write(client, env)
|
611
610
|
# We use String#freeze to avoid allocations under Ruby 2.1+
|
612
611
|
# Not many users hit this code path, so it's better to reduce the
|
613
|
-
# constant table sizes even for
|
612
|
+
# constant table sizes even for Ruby 2.0 users who'll hit extra
|
614
613
|
# allocations here.
|
615
614
|
client.write(@request.response_start_sent ?
|
616
615
|
"100 Continue\r\n\r\nHTTP/1.1 ".freeze :
|
@@ -621,6 +620,7 @@ class Unicorn::HttpServer
|
|
621
620
|
# once a client is accepted, it is processed in its entirety here
|
622
621
|
# in 3 easy steps: read request, call app, write app response
|
623
622
|
def process_client(client)
|
623
|
+
@request = Unicorn::HttpRequest.new
|
624
624
|
env = @request.read(client)
|
625
625
|
|
626
626
|
if early_hints
|
@@ -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
|
737
|
-
end
|
738
|
-
|
739
|
-
# make the following bet: if we accepted clients this round,
|
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
|
742
|
+
break if reopen
|
746
743
|
end
|
747
744
|
|
748
|
-
|
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/oob_gc.rb
CHANGED
@@ -60,7 +60,6 @@ module Unicorn::OobGC
|
|
60
60
|
self.const_set :OOBGC_INTERVAL, interval
|
61
61
|
ObjectSpace.each_object(Unicorn::HttpServer) do |s|
|
62
62
|
s.extend(self)
|
63
|
-
self.const_set :OOBGC_ENV, s.instance_variable_get(:@request).env
|
64
63
|
end
|
65
64
|
app # pretend to be Rack middleware since it was in the past
|
66
65
|
end
|
@@ -68,9 +67,10 @@ module Unicorn::OobGC
|
|
68
67
|
#:stopdoc:
|
69
68
|
def process_client(client)
|
70
69
|
super(client) # Unicorn::HttpServer#process_client
|
71
|
-
|
70
|
+
env = instance_variable_get(:@request).env
|
71
|
+
if OOBGC_PATH =~ env['PATH_INFO'] && ((@@nr -= 1) <= 0)
|
72
72
|
@@nr = OOBGC_INTERVAL
|
73
|
-
|
73
|
+
env.clear
|
74
74
|
disabled = GC.enable
|
75
75
|
GC.start
|
76
76
|
GC.disable if disabled
|
data/lib/unicorn/version.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
Unicorn::Const::UNICORN_VERSION = '
|
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/exec/test_exec.rb
CHANGED
@@ -574,7 +574,7 @@ EOF
|
|
574
574
|
assert_equal String, results[0].class
|
575
575
|
worker_pid = results[0].to_i
|
576
576
|
assert_not_equal pid, worker_pid
|
577
|
-
s =
|
577
|
+
s = unix_socket(tmp.path)
|
578
578
|
s.syswrite("GET / HTTP/1.0\r\n\r\n")
|
579
579
|
results = ''
|
580
580
|
loop { results << s.sysread(4096) } rescue nil
|
@@ -732,7 +732,7 @@ EOF
|
|
732
732
|
wait_for_file(sock_path)
|
733
733
|
assert File.socket?(sock_path)
|
734
734
|
|
735
|
-
sock =
|
735
|
+
sock = unix_socket(sock_path)
|
736
736
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
737
737
|
results = sock.sysread(4096)
|
738
738
|
|
@@ -742,7 +742,7 @@ EOF
|
|
742
742
|
wait_for_file(sock_path)
|
743
743
|
assert File.socket?(sock_path)
|
744
744
|
|
745
|
-
sock =
|
745
|
+
sock = unix_socket(sock_path)
|
746
746
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
747
747
|
results = sock.sysread(4096)
|
748
748
|
|
@@ -777,7 +777,7 @@ EOF
|
|
777
777
|
assert_equal pid, File.read(pid_file).to_i
|
778
778
|
assert File.socket?(sock_path), "socket created"
|
779
779
|
|
780
|
-
sock =
|
780
|
+
sock = unix_socket(sock_path)
|
781
781
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
782
782
|
results = sock.sysread(4096)
|
783
783
|
|
@@ -803,7 +803,7 @@ EOF
|
|
803
803
|
wait_for_file(new_sock_path)
|
804
804
|
assert File.socket?(new_sock_path), "socket exists"
|
805
805
|
@sockets.each do |path|
|
806
|
-
sock =
|
806
|
+
sock = unix_socket(path)
|
807
807
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
808
808
|
results = sock.sysread(4096)
|
809
809
|
assert_equal String, results.class
|
data/test/test_helper.rb
CHANGED
@@ -28,6 +28,7 @@ require 'tempfile'
|
|
28
28
|
require 'fileutils'
|
29
29
|
require 'logger'
|
30
30
|
require 'unicorn'
|
31
|
+
require 'io/nonblock'
|
31
32
|
|
32
33
|
if ENV['DEBUG']
|
33
34
|
require 'ruby-debug'
|
@@ -42,6 +43,7 @@ end
|
|
42
43
|
def redirect_test_io
|
43
44
|
orig_err = STDERR.dup
|
44
45
|
orig_out = STDOUT.dup
|
46
|
+
rdr_pid = $$
|
45
47
|
new_out = File.open("test_stdout.#$$.log", "a")
|
46
48
|
new_err = File.open("test_stderr.#$$.log", "a")
|
47
49
|
new_out.sync = new_err.sync = true
|
@@ -59,8 +61,10 @@ def redirect_test_io
|
|
59
61
|
STDERR.sync = STDOUT.sync = true
|
60
62
|
|
61
63
|
at_exit do
|
62
|
-
|
63
|
-
|
64
|
+
if rdr_pid == $$
|
65
|
+
File.unlink(new_out.path) rescue nil
|
66
|
+
File.unlink(new_err.path) rescue nil
|
67
|
+
end
|
64
68
|
end
|
65
69
|
|
66
70
|
begin
|
@@ -288,3 +292,15 @@ def reset_sig_handlers
|
|
288
292
|
trap(sig, "DEFAULT")
|
289
293
|
end
|
290
294
|
end
|
295
|
+
|
296
|
+
def tcp_socket(*args)
|
297
|
+
sock = TCPSocket.new(*args)
|
298
|
+
sock.nonblock = false
|
299
|
+
sock
|
300
|
+
end
|
301
|
+
|
302
|
+
def unix_socket(*args)
|
303
|
+
sock = UNIXSocket.new(*args)
|
304
|
+
sock.nonblock = false
|
305
|
+
sock
|
306
|
+
end
|
data/test/unit/test_ccc.rb
CHANGED
@@ -3,6 +3,7 @@ require 'unicorn'
|
|
3
3
|
require 'io/wait'
|
4
4
|
require 'tempfile'
|
5
5
|
require 'test/unit'
|
6
|
+
require './test/test_helper'
|
6
7
|
|
7
8
|
class TestCccTCPI < Test::Unit::TestCase
|
8
9
|
def test_ccc_tcpi
|
@@ -42,7 +43,7 @@ class TestCccTCPI < Test::Unit::TestCase
|
|
42
43
|
wr.close
|
43
44
|
|
44
45
|
# make sure the server is running, at least
|
45
|
-
client =
|
46
|
+
client = tcp_socket(host, port)
|
46
47
|
client.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
|
47
48
|
assert client.wait(10), 'never got response from server'
|
48
49
|
res = client.read
|
@@ -51,13 +52,13 @@ class TestCccTCPI < Test::Unit::TestCase
|
|
51
52
|
client.close
|
52
53
|
|
53
54
|
# start a slow request...
|
54
|
-
sleeper =
|
55
|
+
sleeper = tcp_socket(host, port)
|
55
56
|
sleeper.write("GET /sleep HTTP/1.1\r\nHost: example.com\r\n\r\n")
|
56
57
|
|
57
58
|
# and a bunch of aborted ones
|
58
59
|
nr = 100
|
59
60
|
nr.times do |i|
|
60
|
-
client =
|
61
|
+
client = tcp_socket(host, port)
|
61
62
|
client.write("GET /collections/#{rand(10000)} HTTP/1.1\r\n" \
|
62
63
|
"Host: example.com\r\n\r\n")
|
63
64
|
client.close
|
data/test/unit/test_server.rb
CHANGED
@@ -121,7 +121,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
121
121
|
@server.start
|
122
122
|
end
|
123
123
|
|
124
|
-
sock =
|
124
|
+
sock = tcp_socket('127.0.0.1', @port)
|
125
125
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
126
126
|
|
127
127
|
responses = sock.read(4096)
|
@@ -141,14 +141,14 @@ class WebServerTest < Test::Unit::TestCase
|
|
141
141
|
@server.start
|
142
142
|
end
|
143
143
|
|
144
|
-
sock =
|
144
|
+
sock = tcp_socket('127.0.0.1', @port)
|
145
145
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
146
146
|
|
147
147
|
responses = sock.read(4096)
|
148
148
|
assert_match %r{\AHTTP/1.[01] 200\b}, responses
|
149
149
|
assert_match %r{^after_reply_called: false}, responses
|
150
150
|
|
151
|
-
sock =
|
151
|
+
sock = tcp_socket('127.0.0.1', @port)
|
152
152
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
153
153
|
|
154
154
|
responses = sock.read(4096)
|
@@ -166,7 +166,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
166
166
|
@server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
|
167
167
|
@server.start
|
168
168
|
end
|
169
|
-
sock =
|
169
|
+
sock = tcp_socket('127.0.0.1', @port)
|
170
170
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
171
171
|
assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
|
172
172
|
assert_nil sock.close
|
@@ -179,7 +179,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
179
179
|
|
180
180
|
def test_client_shutdown_writes
|
181
181
|
bs = 15609315 * rand
|
182
|
-
sock =
|
182
|
+
sock = tcp_socket('127.0.0.1', @port)
|
183
183
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
184
184
|
sock.syswrite("Host: example.com\r\n")
|
185
185
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
@@ -206,7 +206,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
206
206
|
|
207
207
|
def test_client_shutdown_write_truncates
|
208
208
|
bs = 15609315 * rand
|
209
|
-
sock =
|
209
|
+
sock = tcp_socket('127.0.0.1', @port)
|
210
210
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
211
211
|
sock.syswrite("Host: example.com\r\n")
|
212
212
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
@@ -232,7 +232,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
232
232
|
|
233
233
|
def test_client_malformed_body
|
234
234
|
bs = 15653984
|
235
|
-
sock =
|
235
|
+
sock = tcp_socket('127.0.0.1', @port)
|
236
236
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
237
237
|
sock.syswrite("Host: example.com\r\n")
|
238
238
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
@@ -254,7 +254,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
254
254
|
|
255
255
|
def do_test(string, chunk, close_after=nil, shutdown_delay=0)
|
256
256
|
# Do not use instance variables here, because it needs to be thread safe
|
257
|
-
socket =
|
257
|
+
socket = tcp_socket("127.0.0.1", @port);
|
258
258
|
request = StringIO.new(string)
|
259
259
|
chunks_out = 0
|
260
260
|
|
@@ -299,14 +299,14 @@ class WebServerTest < Test::Unit::TestCase
|
|
299
299
|
end
|
300
300
|
|
301
301
|
def test_bad_client_400
|
302
|
-
sock =
|
302
|
+
sock = tcp_socket('127.0.0.1', @port)
|
303
303
|
sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
|
304
304
|
assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
|
305
305
|
assert_nil sock.close
|
306
306
|
end
|
307
307
|
|
308
308
|
def test_http_0_9
|
309
|
-
sock =
|
309
|
+
sock = tcp_socket('127.0.0.1', @port)
|
310
310
|
sock.syswrite("GET /hello\r\n")
|
311
311
|
assert_match 'hello!\n', sock.sysread(4096)
|
312
312
|
assert_nil sock.close
|
data/test/unit/test_signals.rb
CHANGED
@@ -52,7 +52,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
52
52
|
redirect_test_io { HttpServer.new(app, opts).start.join }
|
53
53
|
}
|
54
54
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
55
|
-
sock =
|
55
|
+
sock = tcp_socket('127.0.0.1', @port)
|
56
56
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
57
57
|
buf = sock.readpartial(4096)
|
58
58
|
assert_nil sock.close
|
@@ -79,7 +79,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
79
79
|
}
|
80
80
|
wr.close
|
81
81
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
82
|
-
sock =
|
82
|
+
sock = tcp_socket('127.0.0.1', @port)
|
83
83
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
84
84
|
buf = rd.readpartial(1)
|
85
85
|
wait_master_ready("test_stderr.#{pid}.log")
|
@@ -102,7 +102,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
102
102
|
}
|
103
103
|
t0 = Time.now
|
104
104
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
105
|
-
sock =
|
105
|
+
sock = tcp_socket('127.0.0.1', @port)
|
106
106
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
107
107
|
|
108
108
|
buf = nil
|
@@ -125,7 +125,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
125
125
|
}
|
126
126
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
127
127
|
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
128
|
-
sock =
|
128
|
+
sock = tcp_socket('127.0.0.1', @port)
|
129
129
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
130
130
|
buf = ''
|
131
131
|
header_len = pid = nil
|
@@ -163,13 +163,13 @@ class SignalsTest < Test::Unit::TestCase
|
|
163
163
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
164
164
|
|
165
165
|
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
166
|
-
sock =
|
166
|
+
sock = tcp_socket('127.0.0.1', @port)
|
167
167
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
168
168
|
pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
169
169
|
assert_nil sock.close
|
170
170
|
|
171
171
|
assert pid > 0, "pid not positive: #{pid.inspect}"
|
172
|
-
sock =
|
172
|
+
sock = tcp_socket('127.0.0.1', @port)
|
173
173
|
sock.syswrite("PUT / HTTP/1.0\r\n")
|
174
174
|
sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
|
175
175
|
1000.times { Process.kill(:HUP, pid) }
|
@@ -116,7 +116,7 @@ class TestSocketHelper < Test::Unit::TestCase
|
|
116
116
|
client.syswrite('abcde')
|
117
117
|
exit 0
|
118
118
|
end
|
119
|
-
s =
|
119
|
+
s = unix_socket(@unix_listener_path)
|
120
120
|
IO.select([s])
|
121
121
|
assert_equal 'abcde', s.sysread(5)
|
122
122
|
pid, status = Process.waitpid2(pid)
|
data/test/unit/test_upload.rb
CHANGED
@@ -60,7 +60,7 @@ class UploadTest < Test::Unit::TestCase
|
|
60
60
|
|
61
61
|
def test_put
|
62
62
|
start_server(@sha1_app)
|
63
|
-
sock =
|
63
|
+
sock = tcp_socket(@addr, @port)
|
64
64
|
sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
|
65
65
|
@count.times do |i|
|
66
66
|
buf = @random.sysread(@bs)
|
@@ -77,7 +77,7 @@ class UploadTest < Test::Unit::TestCase
|
|
77
77
|
def test_put_content_md5
|
78
78
|
md5 = Digest::MD5.new
|
79
79
|
start_server(@sha1_app)
|
80
|
-
sock =
|
80
|
+
sock = tcp_socket(@addr, @port)
|
81
81
|
sock.syswrite("PUT / HTTP/1.0\r\nTransfer-Encoding: chunked\r\n" \
|
82
82
|
"Trailer: Content-MD5\r\n\r\n")
|
83
83
|
@count.times do |i|
|
@@ -103,7 +103,7 @@ class UploadTest < Test::Unit::TestCase
|
|
103
103
|
@count, @bs = 2, 128
|
104
104
|
start_server(@sha1_app)
|
105
105
|
assert_equal 256, length
|
106
|
-
sock =
|
106
|
+
sock = tcp_socket(@addr, @port)
|
107
107
|
hdr = "PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n"
|
108
108
|
@count.times do
|
109
109
|
buf = @random.sysread(@bs)
|
@@ -122,7 +122,7 @@ class UploadTest < Test::Unit::TestCase
|
|
122
122
|
|
123
123
|
def test_put_keepalive_truncates_small_overwrite
|
124
124
|
start_server(@sha1_app)
|
125
|
-
sock =
|
125
|
+
sock = tcp_socket(@addr, @port)
|
126
126
|
to_upload = length + 1
|
127
127
|
sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{to_upload}\r\n\r\n")
|
128
128
|
@count.times do
|
@@ -155,7 +155,7 @@ class UploadTest < Test::Unit::TestCase
|
|
155
155
|
tmp.write(nr.to_s)
|
156
156
|
[ 200, @hdr, [] ]
|
157
157
|
})
|
158
|
-
sock =
|
158
|
+
sock = tcp_socket(@addr, @port)
|
159
159
|
buf = ' ' * @bs
|
160
160
|
sock.syswrite("PUT / HTTP/1.0\r\nContent-Length: #{length}\r\n\r\n")
|
161
161
|
|
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'] || '
|
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
|