unicorn-rupcio 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.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +144 -0
  8. data/.olddoc.yml +25 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +39 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +99 -0
  13. data/Documentation/.gitignore +3 -0
  14. data/Documentation/unicorn.1 +222 -0
  15. data/Documentation/unicorn_rails.1 +207 -0
  16. data/FAQ +70 -0
  17. data/GIT-VERSION-FILE +1 -0
  18. data/GIT-VERSION-GEN +39 -0
  19. data/GNUmakefile +318 -0
  20. data/HACKING +117 -0
  21. data/ISSUES +102 -0
  22. data/KNOWN_ISSUES +79 -0
  23. data/LICENSE +67 -0
  24. data/Links +58 -0
  25. data/PHILOSOPHY +139 -0
  26. data/README +165 -0
  27. data/Rakefile +17 -0
  28. data/SIGNALS +123 -0
  29. data/Sandbox +104 -0
  30. data/TODO +1 -0
  31. data/TUNING +119 -0
  32. data/archive/.gitignore +3 -0
  33. data/archive/slrnpull.conf +4 -0
  34. data/bin/unicorn +129 -0
  35. data/bin/unicorn_rails +210 -0
  36. data/examples/big_app_gc.rb +3 -0
  37. data/examples/echo.ru +27 -0
  38. data/examples/init.sh +102 -0
  39. data/examples/logger_mp_safe.rb +26 -0
  40. data/examples/logrotate.conf +44 -0
  41. data/examples/nginx.conf +156 -0
  42. data/examples/unicorn.conf.minimal.rb +14 -0
  43. data/examples/unicorn.conf.rb +111 -0
  44. data/examples/unicorn.socket +11 -0
  45. data/examples/unicorn@.service +40 -0
  46. data/ext/unicorn_http/CFLAGS +13 -0
  47. data/ext/unicorn_http/c_util.h +115 -0
  48. data/ext/unicorn_http/common_field_optimization.h +128 -0
  49. data/ext/unicorn_http/epollexclusive.h +128 -0
  50. data/ext/unicorn_http/ext_help.h +38 -0
  51. data/ext/unicorn_http/extconf.rb +40 -0
  52. data/ext/unicorn_http/global_variables.h +97 -0
  53. data/ext/unicorn_http/httpdate.c +91 -0
  54. data/ext/unicorn_http/unicorn_http.c +4348 -0
  55. data/ext/unicorn_http/unicorn_http.rl +1054 -0
  56. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  57. data/lib/unicorn/app/old_rails/static.rb +60 -0
  58. data/lib/unicorn/app/old_rails.rb +36 -0
  59. data/lib/unicorn/cgi_wrapper.rb +148 -0
  60. data/lib/unicorn/configurator.rb +749 -0
  61. data/lib/unicorn/const.rb +22 -0
  62. data/lib/unicorn/http_request.rb +180 -0
  63. data/lib/unicorn/http_response.rb +95 -0
  64. data/lib/unicorn/http_server.rb +860 -0
  65. data/lib/unicorn/launcher.rb +63 -0
  66. data/lib/unicorn/oob_gc.rb +82 -0
  67. data/lib/unicorn/preread_input.rb +34 -0
  68. data/lib/unicorn/select_waiter.rb +7 -0
  69. data/lib/unicorn/socket_helper.rb +186 -0
  70. data/lib/unicorn/stream_input.rb +152 -0
  71. data/lib/unicorn/tee_input.rb +132 -0
  72. data/lib/unicorn/tmpio.rb +34 -0
  73. data/lib/unicorn/util.rb +91 -0
  74. data/lib/unicorn/version.rb +1 -0
  75. data/lib/unicorn/worker.rb +166 -0
  76. data/lib/unicorn.rb +137 -0
  77. data/man/man1/unicorn.1 +222 -0
  78. data/man/man1/unicorn_rails.1 +207 -0
  79. data/setup.rb +1587 -0
  80. data/t/.gitignore +4 -0
  81. data/t/GNUmakefile +5 -0
  82. data/t/README +49 -0
  83. data/t/active-unix-socket.t +110 -0
  84. data/t/back-out-of-upgrade.t +44 -0
  85. data/t/bin/unused_listen +40 -0
  86. data/t/client_body_buffer_size.ru +15 -0
  87. data/t/client_body_buffer_size.t +79 -0
  88. data/t/detach.ru +12 -0
  89. data/t/env.ru +4 -0
  90. data/t/fails-rack-lint.ru +6 -0
  91. data/t/heartbeat-timeout.ru +13 -0
  92. data/t/heartbeat-timeout.t +60 -0
  93. data/t/integration.ru +129 -0
  94. data/t/integration.t +509 -0
  95. data/t/lib.perl +309 -0
  96. data/t/listener_names.ru +5 -0
  97. data/t/my-tap-lib.sh +201 -0
  98. data/t/oob_gc.ru +18 -0
  99. data/t/oob_gc_path.ru +18 -0
  100. data/t/pid.ru +4 -0
  101. data/t/preread_input.ru +23 -0
  102. data/t/reload-bad-config.t +49 -0
  103. data/t/reopen-logs.ru +14 -0
  104. data/t/reopen-logs.t +36 -0
  105. data/t/t0010-reap-logging.sh +55 -0
  106. data/t/t0012-reload-empty-config.sh +86 -0
  107. data/t/t0013-rewindable-input-false.sh +24 -0
  108. data/t/t0013.ru +13 -0
  109. data/t/t0014-rewindable-input-true.sh +24 -0
  110. data/t/t0014.ru +13 -0
  111. data/t/t0015-configurator-internals.sh +25 -0
  112. data/t/t0020-at_exit-handler.sh +49 -0
  113. data/t/t0021-process_detach.sh +29 -0
  114. data/t/t0022-listener_names-preload_app.sh +32 -0
  115. data/t/t0300-no-default-middleware.sh +20 -0
  116. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  117. data/t/t0301.ru +14 -0
  118. data/t/t9001-oob_gc.sh +47 -0
  119. data/t/t9002-oob_gc-path.sh +75 -0
  120. data/t/test-lib.sh +125 -0
  121. data/t/winch_ttin.t +64 -0
  122. data/t/working_directory.t +86 -0
  123. data/test/aggregate.rb +16 -0
  124. data/test/benchmark/README +60 -0
  125. data/test/benchmark/dd.ru +19 -0
  126. data/test/benchmark/ddstream.ru +51 -0
  127. data/test/benchmark/readinput.ru +41 -0
  128. data/test/benchmark/stack.ru +9 -0
  129. data/test/benchmark/uconnect.perl +66 -0
  130. data/test/exec/README +5 -0
  131. data/test/exec/test_exec.rb +1030 -0
  132. data/test/test_helper.rb +307 -0
  133. data/test/unit/test_configurator.rb +176 -0
  134. data/test/unit/test_droplet.rb +29 -0
  135. data/test/unit/test_http_parser.rb +885 -0
  136. data/test/unit/test_http_parser_ng.rb +715 -0
  137. data/test/unit/test_server.rb +245 -0
  138. data/test/unit/test_signals.rb +189 -0
  139. data/test/unit/test_socket_helper.rb +160 -0
  140. data/test/unit/test_stream_input.rb +211 -0
  141. data/test/unit/test_tee_input.rb +304 -0
  142. data/test/unit/test_util.rb +132 -0
  143. data/test/unit/test_waiter.rb +35 -0
  144. data/unicorn.gemspec +49 -0
  145. metadata +266 -0
data/t/lib.perl ADDED
@@ -0,0 +1,309 @@
1
+ #!perl -w
2
+ # Copyright (C) unicorn hackers <unicorn-public@80x24.org>
3
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
+ package UnicornTest;
5
+ use v5.14;
6
+ use parent qw(Exporter);
7
+ use autodie;
8
+ use Test::More;
9
+ use Socket qw(SOMAXCONN);
10
+ use Time::HiRes qw(sleep time);
11
+ use IO::Socket::INET;
12
+ use IO::Socket::UNIX;
13
+ use Carp qw(croak);
14
+ use POSIX qw(dup2 _exit setpgid :signal_h SEEK_SET F_SETFD);
15
+ use File::Temp 0.19 (); # 0.19 for ->newdir
16
+ our ($tmpdir, $errfh, $err_log, $u_sock, $u_conf, $daemon_pid,
17
+ $pid_file, $wtest_sock, $fifo);
18
+ our @EXPORT = qw(unicorn slurp tcp_server tcp_start unicorn
19
+ $tmpdir $errfh $err_log $u_sock $u_conf $daemon_pid $pid_file
20
+ $wtest_sock $fifo
21
+ SEEK_SET tcp_host_port which spawn check_stderr unix_start slurp_hdr
22
+ do_req stop_daemon sleep time mkfifo_die kill_until_dead write_file);
23
+
24
+ my ($base) = ($0 =~ m!\b([^/]+)\.[^\.]+\z!);
25
+ $tmpdir = File::Temp->newdir("unicorn-$base-XXXX", TMPDIR => 1);
26
+
27
+ $wtest_sock = "$tmpdir/wtest.sock";
28
+ $err_log = "$tmpdir/err.log";
29
+ $pid_file = "$tmpdir/pid";
30
+ $fifo = "$tmpdir/fifo";
31
+ $u_sock = "$tmpdir/u.sock";
32
+ $u_conf = "$tmpdir/u.conf.rb";
33
+ open($errfh, '+>>', $err_log);
34
+
35
+ if (my $t = $ENV{TAIL}) {
36
+ my @tail = $t =~ /tail/ ? split(/\s+/, $t) : (qw(tail -F));
37
+ push @tail, $err_log;
38
+ my $pid = fork;
39
+ if ($pid == 0) {
40
+ open STDOUT, '>&', \*STDERR;
41
+ exec @tail;
42
+ die "exec(@tail): $!";
43
+ }
44
+ say "# @tail";
45
+ sleep 0.2;
46
+ UnicornTest::AutoReap->new($pid);
47
+ }
48
+
49
+ sub kill_until_dead ($;%) {
50
+ my ($pid, %opt) = @_;
51
+ my $tries = $opt{tries} // 1000;
52
+ my $sig = $opt{sig} // 0;
53
+ while (CORE::kill($sig, $pid) && --$tries) { sleep(0.01) }
54
+ $tries or croak "PID: $pid died after signal ($sig)";
55
+ }
56
+
57
+ sub stop_daemon (;$) {
58
+ my ($is_END) = @_;
59
+ kill('TERM', $daemon_pid);
60
+ kill_until_dead $daemon_pid;
61
+ if ($is_END && CORE::kill(0, $daemon_pid)) { # after done_testing
62
+ CORE::kill('KILL', $daemon_pid);
63
+ die "daemon_pid=$daemon_pid did not die";
64
+ } else {
65
+ ok(!CORE::kill(0, $daemon_pid), 'daemonized unicorn gone');
66
+ undef $daemon_pid;
67
+ }
68
+ };
69
+
70
+ END {
71
+ diag slurp($err_log) if $tmpdir;
72
+ stop_daemon(1) if defined $daemon_pid;
73
+ };
74
+
75
+ sub check_stderr (@) {
76
+ my @log = @_;
77
+ slurp($err_log) if !@log;
78
+ diag("@log") if $ENV{V};
79
+ my @err = grep(!/NameError.*Unicorn::Waiter/, grep(/error/i, @log));
80
+ @err = grep(!/failed to set accept_filter=/, @err);
81
+ @err = grep(!/perhaps accf_.*? needs to be loaded/, @err);
82
+ is_deeply(\@err, [], 'no unexpected errors in stderr');
83
+ is_deeply([grep(/SIGKILL/, @log)], [], 'no SIGKILL in stderr');
84
+ }
85
+
86
+ sub slurp_hdr {
87
+ my ($c) = @_;
88
+ local $/ = "\r\n\r\n"; # affects both readline+chomp
89
+ chomp(my $hdr = readline($c));
90
+ my ($status, @hdr) = split(/\r\n/, $hdr);
91
+ diag explain([ $status, \@hdr ]) if $ENV{V};
92
+ ($status, \@hdr);
93
+ }
94
+
95
+ sub unix_server (;$@) {
96
+ my $l = shift // $u_sock;
97
+ IO::Socket::UNIX->new(Listen => SOMAXCONN, Local => $l, Blocking => 0,
98
+ Type => SOCK_STREAM, @_);
99
+ }
100
+
101
+ sub unix_connect ($) {
102
+ IO::Socket::UNIX->new(Peer => $_[0], Type => SOCK_STREAM);
103
+ }
104
+
105
+ sub tcp_server {
106
+ my %opt = (
107
+ ReuseAddr => 1,
108
+ Proto => 'tcp',
109
+ Type => SOCK_STREAM,
110
+ Listen => SOMAXCONN,
111
+ Blocking => 0,
112
+ @_,
113
+ );
114
+ eval {
115
+ die 'IPv4-only' if $ENV{TEST_IPV4_ONLY};
116
+ require IO::Socket::INET6;
117
+ IO::Socket::INET6->new(%opt, LocalAddr => '[::1]')
118
+ } || eval {
119
+ die 'IPv6-only' if $ENV{TEST_IPV6_ONLY};
120
+ IO::Socket::INET->new(%opt, LocalAddr => '127.0.0.1')
121
+ } || BAIL_OUT "failed to create TCP server: $! ($@)";
122
+ }
123
+
124
+ sub tcp_host_port {
125
+ my ($s) = @_;
126
+ my ($h, $p) = ($s->sockhost, $s->sockport);
127
+ my $ipv4 = $s->sockdomain == AF_INET;
128
+ if (wantarray) {
129
+ $ipv4 ? ($h, $p) : ("[$h]", $p);
130
+ } else {
131
+ $ipv4 ? "$h:$p" : "[$h]:$p";
132
+ }
133
+ }
134
+
135
+ sub unix_start ($@) {
136
+ my ($dst, @req) = @_;
137
+ my $s = unix_connect($dst) or BAIL_OUT "unix connect $dst: $!";
138
+ $s->autoflush(1);
139
+ print $s @req, "\r\n\r\n" if @req;
140
+ $s;
141
+ }
142
+
143
+ sub tcp_start ($@) {
144
+ my ($dst, @req) = @_;
145
+ my $addr = tcp_host_port($dst);
146
+ my $s = ref($dst)->new(
147
+ Proto => 'tcp',
148
+ Type => SOCK_STREAM,
149
+ PeerAddr => $addr,
150
+ ) or BAIL_OUT "failed to connect to $addr: $!";
151
+ $s->autoflush(1);
152
+ print $s @req, "\r\n\r\n" if @req;
153
+ $s;
154
+ }
155
+
156
+ sub slurp {
157
+ open my $fh, '<', $_[0];
158
+ local $/ if !wantarray;
159
+ readline($fh);
160
+ }
161
+
162
+ sub spawn {
163
+ my $env = ref($_[0]) eq 'HASH' ? shift : undef;
164
+ my $opt = ref($_[-1]) eq 'HASH' ? pop : {};
165
+ my @cmd = @_;
166
+ my $old = POSIX::SigSet->new;
167
+ my $set = POSIX::SigSet->new;
168
+ $set->fillset or die "sigfillset: $!";
169
+ sigprocmask(SIG_SETMASK, $set, $old) or die "SIG_SETMASK: $!";
170
+ pipe(my $r, my $w);
171
+ my $pid = fork;
172
+ if ($pid == 0) {
173
+ close $r;
174
+ $SIG{__DIE__} = sub {
175
+ warn(@_);
176
+ syswrite($w, my $num = $! + 0);
177
+ _exit(1);
178
+ };
179
+
180
+ # pretend to be systemd (cf. sd_listen_fds(3))
181
+ my $cfd;
182
+ for ($cfd = 0; ($cfd < 3) || defined($opt->{$cfd}); $cfd++) {
183
+ my $io = $opt->{$cfd} // next;
184
+ my $pfd = fileno($io);
185
+ if ($pfd == $cfd) {
186
+ fcntl($io, F_SETFD, 0);
187
+ } else {
188
+ dup2($pfd, $cfd) // die "dup2($pfd, $cfd): $!";
189
+ }
190
+ }
191
+ if (($cfd - 3) > 0) {
192
+ $env->{LISTEN_PID} = $$;
193
+ $env->{LISTEN_FDS} = $cfd - 3;
194
+ }
195
+
196
+ if (defined(my $pgid = $opt->{pgid})) {
197
+ setpgid(0, $pgid) // die "setpgid(0, $pgid): $!";
198
+ }
199
+ $SIG{$_} = 'DEFAULT' for grep(!/^__/, keys %SIG);
200
+ if (defined(my $cd = $opt->{-C})) { chdir $cd }
201
+ $old->delset(POSIX::SIGCHLD) or die "sigdelset CHLD: $!";
202
+ sigprocmask(SIG_SETMASK, $old) or die "SIG_SETMASK: ~CHLD: $!";
203
+ @ENV{keys %$env} = values(%$env) if $env;
204
+ exec { $cmd[0] } @cmd;
205
+ die "exec @cmd: $!";
206
+ }
207
+ close $w;
208
+ sigprocmask(SIG_SETMASK, $old) or die "SIG_SETMASK(old): $!";
209
+ if (my $cerrnum = do { local $/, <$r> }) {
210
+ $! = $cerrnum;
211
+ die "@cmd PID=$pid died: $!";
212
+ }
213
+ $pid;
214
+ }
215
+
216
+ sub which {
217
+ my ($file) = @_;
218
+ return $file if index($file, '/') >= 0;
219
+ for my $p (split(/:/, $ENV{PATH})) {
220
+ $p .= "/$file";
221
+ return $p if -x $p;
222
+ }
223
+ undef;
224
+ }
225
+
226
+ # returns an AutoReap object
227
+ sub unicorn {
228
+ my %env;
229
+ if (ref($_[0]) eq 'HASH') {
230
+ my $e = shift;
231
+ %env = %$e;
232
+ }
233
+ my @args = @_;
234
+ push(@args, {}) if ref($args[-1]) ne 'HASH';
235
+ $args[-1]->{2} //= $errfh; # stderr default
236
+
237
+ state $ruby = which($ENV{RUBY} // 'ruby');
238
+ state $lib = File::Spec->rel2abs('lib');
239
+ state $ver = $ENV{TEST_RUBY_VERSION} // `$ruby -e 'print RUBY_VERSION'`;
240
+ state $eng = $ENV{TEST_RUBY_ENGINE} // `$ruby -e 'print RUBY_ENGINE'`;
241
+ state $ext = File::Spec->rel2abs("test/$eng-$ver/ext/unicorn_http");
242
+ state $exe = File::Spec->rel2abs("test/$eng-$ver/bin/unicorn");
243
+ state $rl = $ENV{RUBYLIB} ? "$lib:$ext:$ENV{RUBYLIB}" : "$lib:$ext";
244
+ $env{RUBYLIB} = $rl;
245
+ my $pid = spawn(\%env, $ruby, $exe, @args);
246
+ UnicornTest::AutoReap->new($pid);
247
+ }
248
+
249
+ sub do_req ($@) {
250
+ my ($dst, @req) = @_;
251
+ my $c = ref($dst) ? tcp_start($dst, @req) : unix_start($dst, @req);
252
+ return $c if !wantarray;
253
+ my ($status, $hdr);
254
+ # read headers iff HTTP/1.x request, HTTP/0.9 remains supported
255
+ my ($first) = (join('', @req) =~ m!\A([^\r\n]+)!);
256
+ ($status, $hdr) = slurp_hdr($c) if $first =~ m{\s*HTTP/\S+$};
257
+ my $bdy = do { local $/; <$c> };
258
+ close $c;
259
+ ($status, $hdr, $bdy);
260
+ }
261
+
262
+ sub mkfifo_die ($;$) {
263
+ POSIX::mkfifo($_[0], $_[1] // 0600) or croak "mkfifo: $!";
264
+ }
265
+
266
+ sub write_file ($$@) { # mode, filename, LIST (for print)
267
+ open(my $fh, shift, shift);
268
+ print $fh @_;
269
+ # return $fh for futher writes if user wants it:
270
+ defined(wantarray) && !wantarray ? $fh : close $fh;
271
+ }
272
+
273
+ # automatically kill + reap children when this goes out-of-scope
274
+ package UnicornTest::AutoReap;
275
+ use v5.14;
276
+ use autodie;
277
+
278
+ sub new {
279
+ my (undef, $pid) = @_;
280
+ bless { pid => $pid, owner => $$ }, __PACKAGE__
281
+ }
282
+
283
+ sub do_kill {
284
+ my ($self, $sig) = @_;
285
+ kill($sig // 'TERM', $self->{pid});
286
+ }
287
+
288
+ sub join {
289
+ my ($self, $sig) = @_;
290
+ my $pid = delete $self->{pid} or return;
291
+ kill($sig, $pid) if defined $sig;
292
+ my $ret = waitpid($pid, 0);
293
+ $ret == $pid or die "BUG: waitpid($pid) != $ret";
294
+ }
295
+
296
+ sub DESTROY {
297
+ my ($self) = @_;
298
+ return if $self->{owner} != $$;
299
+ $self->join('TERM');
300
+ }
301
+
302
+ package main; # inject ourselves into the t/*.t script
303
+ UnicornTest->import;
304
+ Test::More->import;
305
+ # try to ensure ->DESTROY fires:
306
+ $SIG{TERM} = sub { exit(15 + 128) };
307
+ $SIG{INT} = sub { exit(2 + 128) };
308
+ $SIG{PIPE} = sub { exit(13 + 128) };
309
+ 1;
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: false
2
+ use Rack::ContentLength
3
+ use Rack::ContentType, "text/plain"
4
+ names = Unicorn.listener_names.inspect # rely on preload_app=true
5
+ run(lambda { |_| [ 200, {}, [ names ] ] })
data/t/my-tap-lib.sh ADDED
@@ -0,0 +1,201 @@
1
+ #!/bin/sh
2
+ # Copyright (c) 2009, 2010 Eric Wong <normalperson@yhbt.net>
3
+ #
4
+ # TAP-producing shell library for POSIX-compliant Bourne shells We do
5
+ # not _rely_ on Bourne Again features, though we will use "set -o
6
+ # pipefail" from ksh93 or bash 3 if available
7
+ #
8
+ # Only generic, non-project/non-language-specific stuff goes here. We
9
+ # only have POSIX dependencies for the core tests (without --verbose),
10
+ # though we'll enable useful non-POSIX things if they're available.
11
+ #
12
+ # This test library is intentionally unforgiving, it does not support
13
+ # skipping tests nor continuing after any failure. Any failures
14
+ # immediately halt execution as do any references to undefined
15
+ # variables.
16
+ #
17
+ # When --verbose is specified, we always prefix stdout/stderr
18
+ # output with "#" to avoid confusing TAP consumers. Otherwise
19
+ # the normal stdout/stderr streams are redirected to /dev/null
20
+
21
+ # dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
22
+ # normal TAP output goes to fd=3, nothing should go to fd=4
23
+ exec 3>&1 4>&2
24
+
25
+ # ensure a sane environment
26
+ TZ=UTC LC_ALL=C LANG=C
27
+ export LANG LC_ALL TZ
28
+ unset CDPATH
29
+
30
+ # pipefail is non-POSIX, but very useful in ksh93/bash
31
+ ( set -o pipefail 2>/dev/null ) && set -o pipefail
32
+
33
+ SED=${SED-sed}
34
+
35
+ # Unlike other test frameworks, we are unforgiving and bail immediately
36
+ # on any failures. We do this because we're lazy about error handling
37
+ # and also because we believe anything broken should not be allowed to
38
+ # propagate throughout the rest of the test
39
+ set -e
40
+ set -u
41
+
42
+ # name of our test
43
+ T=${0##*/}
44
+
45
+ t_expect_nr=-1
46
+ t_nr=0
47
+ t_current=
48
+ t_complete=false
49
+
50
+ # list of files to remove unconditionally on exit
51
+ T_RM_LIST=
52
+
53
+ # list of files to remove only on successful exit
54
+ T_OK_RM_LIST=
55
+
56
+ # emit output to stdout, it'll be parsed by the TAP consumer
57
+ # so it must be TAP-compliant output
58
+ t_echo () {
59
+ echo >&3 "$@"
60
+ }
61
+
62
+ # emits non-parsed information to stdout, it will be prefixed with a '#'
63
+ # to not throw off TAP consumers
64
+ t_info () {
65
+ t_echo '#' "$@"
66
+ }
67
+
68
+ # exit with an error and print a diagnostic
69
+ die () {
70
+ echo >&2 "$@"
71
+ exit 1
72
+ }
73
+
74
+ # our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
75
+ t_at_exit () {
76
+ code=$?
77
+ set +e
78
+ if test $code -eq 0
79
+ then
80
+ $t_complete || {
81
+ t_info "t_done not called"
82
+ code=1
83
+ }
84
+ elif test -n "$t_current"
85
+ then
86
+ t_echo "not ok $t_nr - $t_current"
87
+ fi
88
+ if test $t_expect_nr -ne -1
89
+ then
90
+ test $t_expect_nr -eq $t_nr || {
91
+ t_info "planned $t_expect_nr tests but ran $t_nr"
92
+ test $code -ne 0 || code=1
93
+ }
94
+ fi
95
+ $t_complete || {
96
+ t_info "unexpected test failure"
97
+ test $code -ne 0 || code=1
98
+ }
99
+ rm -f $T_RM_LIST
100
+ test $code -eq 0 && rm -f $T_OK_RM_LIST
101
+ set +x
102
+ exec >&3 2>&4
103
+ t_close_fds
104
+ exit $code
105
+ }
106
+
107
+ # close test-specific extra file descriptors
108
+ t_close_fds () {
109
+ exec 3>&- 4>&-
110
+ }
111
+
112
+ # call this at the start of your test to specify the number of tests
113
+ # you plan to run
114
+ t_plan () {
115
+ test "$1" -ge 1 || die "must plan at least one test"
116
+ test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
117
+ t_expect_nr=$1
118
+ shift
119
+ t_echo 1..$t_expect_nr "#" "$@"
120
+ trap t_at_exit EXIT
121
+ }
122
+
123
+ _t_checkup () {
124
+ test $t_expect_nr -le 0 && die "no tests planned"
125
+ test -n "$t_current" && t_echo "ok $t_nr - $t_current"
126
+ true
127
+ }
128
+
129
+ # finalizes any previously test and starts a new one
130
+ t_begin () {
131
+ _t_checkup
132
+ t_nr=$(( $t_nr + 1 ))
133
+ t_current="$1"
134
+
135
+ # just in case somebody wanted to cheat us:
136
+ set -e
137
+ }
138
+
139
+ # finalizes the current test without starting a new one
140
+ t_end () {
141
+ _t_checkup
142
+ t_current=
143
+ }
144
+
145
+ # run this to signify the end of your test
146
+ t_done () {
147
+ _t_checkup
148
+ t_current=
149
+ t_complete=true
150
+ test $t_expect_nr -eq $t_nr || exit 1
151
+ exit 0
152
+ }
153
+
154
+ # create and assign named-pipes to variable _names_ passed to this function
155
+ t_fifos () {
156
+ for _id in "$@"
157
+ do
158
+ _name=$_id
159
+ _tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
160
+ eval "$_id=$_tmp"
161
+ rm -f $_tmp
162
+ mkfifo $_tmp
163
+ T_RM_LIST="$T_RM_LIST $_tmp"
164
+ done
165
+ }
166
+
167
+ t_verbose=false t_trace=false
168
+
169
+ while test "$#" -ne 0
170
+ do
171
+ arg="$1"
172
+ shift
173
+ case $arg in
174
+ -v|--verbose) t_verbose=true ;;
175
+ --trace) t_trace=true t_verbose=true ;;
176
+ *) die "Unknown option: $arg" ;;
177
+ esac
178
+ done
179
+
180
+ # we always only setup stdout, nothing should end up in the "real" stderr
181
+ if $t_verbose
182
+ then
183
+ if test x"$(which mktemp 2>/dev/null)" = x
184
+ then
185
+ die "mktemp(1) not available for --verbose"
186
+ fi
187
+ t_fifos t_stdout t_stderr
188
+
189
+ (
190
+ # use a subshell so seds are not waitable
191
+ $SED -e 's/^/#: /' < $t_stdout &
192
+ $SED -e 's/^/#! /' < $t_stderr &
193
+ ) &
194
+ wait
195
+ exec > $t_stdout 2> $t_stderr
196
+ else
197
+ exec > /dev/null 2> /dev/null
198
+ fi
199
+
200
+ $t_trace && set -x
201
+ true
data/t/oob_gc.ru ADDED
@@ -0,0 +1,18 @@
1
+ #\-E none
2
+ # frozen_string_literal: false
3
+ require 'unicorn/oob_gc'
4
+ use Rack::ContentLength
5
+ use Rack::ContentType, "text/plain"
6
+ use Unicorn::OobGC
7
+ $gc_started = false
8
+
9
+ # Mock GC.start
10
+ def GC.start
11
+ $gc_started = true
12
+ end
13
+ run lambda { |env|
14
+ if "/gc_reset" == env["PATH_INFO"] && "POST" == env["REQUEST_METHOD"]
15
+ $gc_started = false
16
+ end
17
+ [ 200, {}, [ "#$gc_started\n" ] ]
18
+ }
data/t/oob_gc_path.ru ADDED
@@ -0,0 +1,18 @@
1
+ #\-E none
2
+ # frozen_string_literal: false
3
+ require 'unicorn/oob_gc'
4
+ use Rack::ContentLength
5
+ use Rack::ContentType, "text/plain"
6
+ use Unicorn::OobGC, 5, /BAD/
7
+ $gc_started = false
8
+
9
+ # Mock GC.start
10
+ def GC.start
11
+ $gc_started = true
12
+ end
13
+ run lambda { |env|
14
+ if "/gc_reset" == env["PATH_INFO"] && "POST" == env["REQUEST_METHOD"]
15
+ $gc_started = false
16
+ end
17
+ [ 200, {}, [ "#$gc_started\n" ] ]
18
+ }
data/t/pid.ru ADDED
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: false
2
+ use Rack::ContentLength
3
+ use Rack::ContentType, "text/plain"
4
+ run lambda { |env| [ 200, {}, [ "#$$\n" ] ] }
@@ -0,0 +1,23 @@
1
+ #\-E none
2
+ # frozen_string_literal: false
3
+ require 'digest/md5'
4
+ require 'unicorn/preread_input'
5
+ use Unicorn::PrereadInput
6
+ nr = 0
7
+ run lambda { |env|
8
+ $stderr.write "app dispatch: #{nr += 1}\n"
9
+ input = env["rack.input"]
10
+ dig = Digest::MD5.new
11
+ if buf = input.read(16384)
12
+ begin
13
+ dig.update(buf)
14
+ end while input.read(16384, buf)
15
+ buf.clear # remove this call if Ruby ever gets escape analysis
16
+ end
17
+ if env['HTTP_TRAILER'] =~ /\bContent-MD5\b/i
18
+ cmd5_b64 = env['HTTP_CONTENT_MD5'] or return [500, {}, ['No Content-MD5']]
19
+ cmd5_bin = cmd5_b64.unpack('m')[0]
20
+ return [500, {}, [ cmd5_b64 ] ] if cmd5_bin != dig.digest
21
+ end
22
+ [ 200, {}, [ dig.hexdigest ] ]
23
+ }
@@ -0,0 +1,49 @@
1
+ #!perl -w
2
+ # Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
3
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
+ use v5.14; BEGIN { require './t/lib.perl' };
5
+ use autodie;
6
+ my $srv = tcp_server();
7
+ my $host_port = tcp_host_port($srv);
8
+ my $ru = "$tmpdir/config.ru";
9
+
10
+ write_file '>', $ru, <<'EOM';
11
+ use Rack::ContentLength
12
+ use Rack::ContentType, 'text/plain'
13
+ config = ru = "hello world\n" # check for config variable conflicts, too
14
+ run lambda { |env| [ 200, {}, [ ru.to_s ] ] }
15
+ EOM
16
+
17
+ write_file '>', $u_conf, <<EOM;
18
+ preload_app true
19
+ stderr_path "$err_log"
20
+ EOM
21
+
22
+ my $ar = unicorn(qw(-E none -c), $u_conf, $ru, { 3 => $srv });
23
+ my ($status, $hdr, $bdy) = do_req($srv, 'GET / HTTP/1.0');
24
+ like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid at start');
25
+ is($bdy, "hello world\n", 'body matches expected');
26
+
27
+ write_file '>>', $ru, <<'EOM';
28
+ ....this better be a syntax error in any version of ruby...
29
+ EOM
30
+
31
+ $ar->do_kill('HUP'); # reload
32
+ my @l;
33
+ for (1..1000) {
34
+ @l = grep(/(?:done|error) reloading/, slurp($err_log)) and
35
+ last;
36
+ sleep 0.011;
37
+ }
38
+ diag slurp($err_log) if $ENV{V};
39
+ ok(grep(/error reloading/, @l), 'got error reloading');
40
+ open my $fh, '>', $err_log; # truncate
41
+ close $fh;
42
+
43
+ ($status, $hdr, $bdy) = do_req($srv, 'GET / HTTP/1.0');
44
+ like($status, qr!\AHTTP/1\.[01] 200\b!, 'status line valid afte reload');
45
+ is($bdy, "hello world\n", 'body matches expected after reload');
46
+
47
+ check_stderr;
48
+ undef $tmpdir; # quiet t/lib.perl END{}
49
+ done_testing;
data/t/reopen-logs.ru ADDED
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: false
2
+ use Rack::ContentLength
3
+ use Rack::ContentType, "text/plain"
4
+ run lambda { |env|
5
+
6
+ # our File objects for stderr/stdout should always have #path
7
+ # and be sync=true
8
+ ok = $stderr.sync &&
9
+ $stdout.sync &&
10
+ String === $stderr.path &&
11
+ String === $stdout.path
12
+
13
+ [ 200, {}, [ "#{ok}\n" ] ]
14
+ }
data/t/reopen-logs.t ADDED
@@ -0,0 +1,36 @@
1
+ #!perl -w
2
+ # Copyright (C) unicorn hackers <unicorn-public@yhbt.net>
3
+ # License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
4
+ use v5.14; BEGIN { require './t/lib.perl' };
5
+ use autodie;
6
+ my $srv = tcp_server();
7
+ my $out_log = "$tmpdir/out.log";
8
+ write_file '>', $u_conf, <<EOM;
9
+ stderr_path "$err_log"
10
+ stdout_path "$out_log"
11
+ EOM
12
+
13
+ my $auto_reap = unicorn('-c', $u_conf, 't/reopen-logs.ru', { 3 => $srv } );
14
+ my ($status, $hdr, $bdy) = do_req($srv, 'GET / HTTP/1.0');
15
+ is($bdy, "true\n", 'logs opened');
16
+
17
+ rename($err_log, "$err_log.rot");
18
+ rename($out_log, "$out_log.rot");
19
+
20
+ $auto_reap->do_kill('USR1');
21
+
22
+ my $tries = 1000;
23
+ while (!-f $err_log && --$tries) { sleep 0.01 };
24
+ while (!-f $out_log && --$tries) { sleep 0.01 };
25
+
26
+ ok(-f $out_log, 'stdout_path recreated after USR1');
27
+ ok(-f $err_log, 'stderr_path recreated after USR1');
28
+
29
+ ($status, $hdr, $bdy) = do_req($srv, 'GET / HTTP/1.0');
30
+ is($bdy, "true\n", 'logs reopened with sync==true');
31
+
32
+ $auto_reap->join('QUIT');
33
+ is($?, 0, 'no error on exit');
34
+ check_stderr;
35
+ undef $tmpdir;
36
+ done_testing;