unicorn 5.4.1 → 6.0.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 +7 -5
- data/.olddoc.yml +12 -7
- data/Application_Timeouts +4 -4
- data/Documentation/.gitignore +1 -3
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +1 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +112 -57
- data/HACKING +1 -1
- data/ISSUES +21 -23
- data/KNOWN_ISSUES +2 -2
- data/LATEST +22 -28
- data/LICENSE +2 -2
- data/Links +13 -11
- data/NEWS +65 -0
- data/README +25 -11
- data/SIGNALS +1 -1
- data/Sandbox +4 -4
- data/archive/slrnpull.conf +1 -1
- data/bin/unicorn +3 -1
- data/bin/unicorn_rails +2 -2
- data/examples/big_app_gc.rb +1 -1
- data/examples/logrotate.conf +3 -3
- data/examples/nginx.conf +4 -3
- data/examples/unicorn.conf.minimal.rb +2 -2
- data/examples/unicorn.conf.rb +2 -2
- data/examples/unicorn@.service +7 -0
- data/ext/unicorn_http/common_field_optimization.h +24 -6
- data/ext/unicorn_http/extconf.rb +35 -0
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +2 -2
- data/ext/unicorn_http/unicorn_http.c +257 -224
- data/ext/unicorn_http/unicorn_http.rl +47 -14
- data/lib/unicorn/configurator.rb +25 -4
- data/lib/unicorn/http_request.rb +12 -3
- data/lib/unicorn/http_server.rb +43 -12
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +5 -5
- data/lib/unicorn/socket_helper.rb +1 -0
- data/lib/unicorn/tmpio.rb +8 -2
- data/lib/unicorn/util.rb +1 -1
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +16 -2
- data/lib/unicorn.rb +23 -9
- data/man/man1/unicorn.1 +89 -88
- data/man/man1/unicorn_rails.1 +78 -83
- data/t/GNUmakefile +3 -72
- data/t/README +4 -4
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/test/benchmark/README +14 -4
- data/test/benchmark/ddstream.ru +50 -0
- data/test/benchmark/readinput.ru +40 -0
- data/test/benchmark/uconnect.perl +66 -0
- data/test/exec/test_exec.rb +14 -12
- data/test/test_helper.rb +38 -30
- data/test/unit/test_ccc.rb +4 -3
- data/test/unit/test_http_parser.rb +16 -0
- data/test/unit/test_http_parser_ng.rb +81 -0
- data/test/unit/test_server.rb +81 -7
- data/test/unit/test_signals.rb +6 -6
- data/test/unit/test_socket_helper.rb +1 -1
- data/test/unit/test_upload.rb +9 -14
- data/test/unit/test_util.rb +25 -0
- data/unicorn.gemspec +8 -7
- metadata +15 -13
- data/Documentation/GNUmakefile +0 -30
- data/Documentation/unicorn.1.txt +0 -187
- data/Documentation/unicorn_rails.1.txt +0 -175
- data/t/hijack.ru +0 -55
- data/t/t0200-rack-hijack.sh +0 -51
@@ -13,7 +13,7 @@
|
|
13
13
|
#include "global_variables.h"
|
14
14
|
#include "c_util.h"
|
15
15
|
|
16
|
-
void init_unicorn_httpdate(
|
16
|
+
void init_unicorn_httpdate(void);
|
17
17
|
|
18
18
|
#define UH_FL_CHUNKED 0x1
|
19
19
|
#define UH_FL_HASBODY 0x2
|
@@ -62,7 +62,8 @@ struct http_parser {
|
|
62
62
|
} len;
|
63
63
|
};
|
64
64
|
|
65
|
-
static ID id_set_backtrace;
|
65
|
+
static ID id_set_backtrace, id_is_chunked_p;
|
66
|
+
static VALUE cHttpParser;
|
66
67
|
|
67
68
|
#ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
|
68
69
|
# define my_hash_clear(h) (void)rb_hash_clear(h)
|
@@ -220,6 +221,19 @@ static void write_cont_value(struct http_parser *hp,
|
|
220
221
|
rb_str_buf_cat(hp->cont, vptr, end + 1);
|
221
222
|
}
|
222
223
|
|
224
|
+
static int is_chunked(VALUE v)
|
225
|
+
{
|
226
|
+
/* common case first */
|
227
|
+
if (STR_CSTR_CASE_EQ(v, "chunked"))
|
228
|
+
return 1;
|
229
|
+
|
230
|
+
/*
|
231
|
+
* call Ruby function in unicorn/http_request.rb to deal with unlikely
|
232
|
+
* comma-delimited case
|
233
|
+
*/
|
234
|
+
return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
|
235
|
+
}
|
236
|
+
|
223
237
|
static void write_value(struct http_parser *hp,
|
224
238
|
const char *buffer, const char *p)
|
225
239
|
{
|
@@ -246,7 +260,9 @@ static void write_value(struct http_parser *hp,
|
|
246
260
|
f = uncommon_field(field, flen);
|
247
261
|
} else if (f == g_http_connection) {
|
248
262
|
hp_keepalive_connection(hp, v);
|
249
|
-
} else if (f == g_content_length) {
|
263
|
+
} else if (f == g_content_length && !HP_FL_TEST(hp, CHUNKED)) {
|
264
|
+
if (hp->len.content)
|
265
|
+
parser_raise(eHttpParserError, "Content-Length already set");
|
250
266
|
hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
|
251
267
|
if (hp->len.content < 0)
|
252
268
|
parser_raise(eHttpParserError, "invalid Content-Length");
|
@@ -254,9 +270,30 @@ static void write_value(struct http_parser *hp,
|
|
254
270
|
HP_FL_SET(hp, HASBODY);
|
255
271
|
hp_invalid_if_trailer(hp);
|
256
272
|
} else if (f == g_http_transfer_encoding) {
|
257
|
-
if (
|
273
|
+
if (is_chunked(v)) {
|
274
|
+
if (HP_FL_TEST(hp, CHUNKED))
|
275
|
+
/*
|
276
|
+
* RFC 7230 3.3.1:
|
277
|
+
* A sender MUST NOT apply chunked more than once to a message body
|
278
|
+
* (i.e., chunking an already chunked message is not allowed).
|
279
|
+
*/
|
280
|
+
parser_raise(eHttpParserError, "Transfer-Encoding double chunked");
|
281
|
+
|
258
282
|
HP_FL_SET(hp, CHUNKED);
|
259
283
|
HP_FL_SET(hp, HASBODY);
|
284
|
+
|
285
|
+
/* RFC 7230 3.3.3, 3: favor chunked if Content-Length exists */
|
286
|
+
hp->len.content = 0;
|
287
|
+
} else if (HP_FL_TEST(hp, CHUNKED)) {
|
288
|
+
/*
|
289
|
+
* RFC 7230 3.3.3, point 3 states:
|
290
|
+
* If a Transfer-Encoding header field is present in a request and
|
291
|
+
* the chunked transfer coding is not the final encoding, the
|
292
|
+
* message body length cannot be determined reliably; the server
|
293
|
+
* MUST respond with the 400 (Bad Request) status code and then
|
294
|
+
* close the connection.
|
295
|
+
*/
|
296
|
+
parser_raise(eHttpParserError, "invalid Transfer-Encoding");
|
260
297
|
}
|
261
298
|
hp_invalid_if_trailer(hp);
|
262
299
|
} else if (f == g_http_trailer) {
|
@@ -487,7 +524,7 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
|
|
487
524
|
* and X-Forwarded-Proto handling from this parser? We've had it
|
488
525
|
* forever and nobody has said anything against it, either.
|
489
526
|
* Anyways, please send comments to our public mailing list:
|
490
|
-
* unicorn-public@
|
527
|
+
* unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
|
491
528
|
*/
|
492
529
|
scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
|
493
530
|
if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
|
@@ -931,11 +968,8 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
931
968
|
|
932
969
|
void Init_unicorn_http(void)
|
933
970
|
{
|
934
|
-
|
935
|
-
VALUE mUnicorn, cHttpParser;
|
971
|
+
VALUE mUnicorn;
|
936
972
|
|
937
|
-
mark_ary = rb_ary_new();
|
938
|
-
rb_global_variable(&mark_ary);
|
939
973
|
mUnicorn = rb_define_module("Unicorn");
|
940
974
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
941
975
|
eHttpParserError =
|
@@ -945,7 +979,7 @@ void Init_unicorn_http(void)
|
|
945
979
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
946
980
|
eHttpParserError);
|
947
981
|
|
948
|
-
init_globals(
|
982
|
+
init_globals();
|
949
983
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
950
984
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
951
985
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -982,19 +1016,18 @@ void Init_unicorn_http(void)
|
|
982
1016
|
|
983
1017
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
984
1018
|
|
985
|
-
init_common_fields(
|
1019
|
+
init_common_fields();
|
986
1020
|
SET_GLOBAL(g_http_host, "HOST");
|
987
1021
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
988
1022
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
989
1023
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
990
1024
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
991
1025
|
id_set_backtrace = rb_intern("set_backtrace");
|
992
|
-
init_unicorn_httpdate(
|
993
|
-
|
994
|
-
OBJ_FREEZE(mark_ary);
|
1026
|
+
init_unicorn_httpdate();
|
995
1027
|
|
996
1028
|
#ifndef HAVE_RB_HASH_CLEAR
|
997
1029
|
id_clear = rb_intern("clear");
|
998
1030
|
#endif
|
1031
|
+
id_is_chunked_p = rb_intern("is_chunked?");
|
999
1032
|
}
|
1000
1033
|
#undef SET_GLOBAL
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -3,11 +3,11 @@ require 'logger'
|
|
3
3
|
|
4
4
|
# Implements a simple DSL for configuring a unicorn server.
|
5
5
|
#
|
6
|
-
# See https://
|
7
|
-
# https://
|
6
|
+
# See https://yhbt.net/unicorn/examples/unicorn.conf.rb and
|
7
|
+
# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
|
8
8
|
# example configuration files. An example config file for use with
|
9
9
|
# nginx is also available at
|
10
|
-
# https://
|
10
|
+
# https://yhbt.net/unicorn/examples/nginx.conf
|
11
11
|
#
|
12
12
|
# See the link:/TUNING.html document for more information on tuning unicorn.
|
13
13
|
class Unicorn::Configurator
|
@@ -53,6 +53,7 @@ class Unicorn::Configurator
|
|
53
53
|
server.logger.info("worker=#{worker.nr} ready")
|
54
54
|
},
|
55
55
|
:pid => nil,
|
56
|
+
:early_hints => false,
|
56
57
|
:worker_exec => false,
|
57
58
|
:preload_app => false,
|
58
59
|
:check_client_connection => false,
|
@@ -88,6 +89,9 @@ class Unicorn::Configurator
|
|
88
89
|
RACKUP[:set_listener] and
|
89
90
|
set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
|
90
91
|
|
92
|
+
RACKUP[:no_default_middleware] and
|
93
|
+
set[:default_middleware] = false
|
94
|
+
|
91
95
|
# unicorn_rails creates dirs here after working_directory is bound
|
92
96
|
after_reload.call if after_reload
|
93
97
|
|
@@ -235,7 +239,7 @@ class Unicorn::Configurator
|
|
235
239
|
# server 192.168.0.9:8080 fail_timeout=0;
|
236
240
|
# }
|
237
241
|
#
|
238
|
-
# See
|
242
|
+
# See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
|
239
243
|
# for more details on nginx upstream configuration.
|
240
244
|
def timeout(seconds)
|
241
245
|
set_int(:timeout, seconds, 3)
|
@@ -265,6 +269,23 @@ class Unicorn::Configurator
|
|
265
269
|
set_int(:worker_processes, nr, 1)
|
266
270
|
end
|
267
271
|
|
272
|
+
# sets whether to add default middleware in the development and
|
273
|
+
# deployment RACK_ENVs.
|
274
|
+
#
|
275
|
+
# default_middleware is only available in unicorn 5.5.0+
|
276
|
+
def default_middleware(bool)
|
277
|
+
set_bool(:default_middleware, bool)
|
278
|
+
end
|
279
|
+
|
280
|
+
# sets whether to enable the proposed early hints Rack API.
|
281
|
+
# If enabled, Rails 5.2+ will automatically send a 103 Early Hint
|
282
|
+
# for all the `javascript_include_tag` and `stylesheet_link_tag`
|
283
|
+
# in your response. See: https://api.rubyonrails.org/v5.2/classes/ActionDispatch/Request.html#method-i-send_early_hints
|
284
|
+
# See also https://tools.ietf.org/html/rfc8297
|
285
|
+
def early_hints(bool)
|
286
|
+
set_bool(:early_hints, bool)
|
287
|
+
end
|
288
|
+
|
268
289
|
# sets listeners to the given +addresses+, replacing or augmenting the
|
269
290
|
# current set. This is for the global listener pool shared by all
|
270
291
|
# worker processes. For per-worker listeners, see the after_fork example
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
# no stable API here
|
4
4
|
require 'unicorn_http'
|
5
|
-
require 'raindrops'
|
6
5
|
|
7
6
|
# TODO: remove redundant names
|
8
7
|
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
@@ -63,10 +62,9 @@ class Unicorn::HttpParser
|
|
63
62
|
# This does minimal exception trapping and it is up to the caller
|
64
63
|
# to handle any socket errors (e.g. user aborted upload).
|
65
64
|
def read(socket)
|
66
|
-
clear
|
67
65
|
e = env
|
68
66
|
|
69
|
-
# From
|
67
|
+
# From https://www.ietf.org/rfc/rfc3875:
|
70
68
|
# "Script authors should be aware that the REMOTE_ADDR and
|
71
69
|
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
72
70
|
# may not identify the ultimate source of the request. They
|
@@ -189,4 +187,15 @@ class Unicorn::HttpParser
|
|
189
187
|
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
190
188
|
end
|
191
189
|
end
|
190
|
+
|
191
|
+
# called by ext/unicorn_http/unicorn_http.rl via rb_funcall
|
192
|
+
def self.is_chunked?(v) # :nodoc:
|
193
|
+
vals = v.split(/[ \t]*,[ \t]*/).map!(&:downcase)
|
194
|
+
if vals.pop == 'chunked'.freeze
|
195
|
+
return true unless vals.include?('chunked'.freeze)
|
196
|
+
raise Unicorn::HttpParserError, 'double chunked', []
|
197
|
+
end
|
198
|
+
return false unless vals.include?('chunked'.freeze)
|
199
|
+
raise Unicorn::HttpParserError, 'chunked not last', []
|
200
|
+
end
|
192
201
|
end
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
# forked worker children.
|
7
7
|
#
|
8
8
|
# Users do not need to know the internals of this class, but reading the
|
9
|
-
# {source}[https://
|
9
|
+
# {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
|
10
10
|
# is education for programmers wishing to learn how unicorn works.
|
11
11
|
# See Unicorn::Configurator for information on how to configure unicorn.
|
12
12
|
class Unicorn::HttpServer
|
@@ -14,7 +14,8 @@ class Unicorn::HttpServer
|
|
14
14
|
attr_accessor :app, :timeout, :worker_processes,
|
15
15
|
:before_fork, :after_fork, :before_exec,
|
16
16
|
:listener_opts, :preload_app,
|
17
|
-
:orig_app, :config, :ready_pipe, :user
|
17
|
+
:orig_app, :config, :ready_pipe, :user,
|
18
|
+
:default_middleware, :early_hints
|
18
19
|
attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
|
19
20
|
|
20
21
|
attr_reader :pid, :logger
|
@@ -68,8 +69,8 @@ class Unicorn::HttpServer
|
|
68
69
|
# incoming requests on the socket.
|
69
70
|
def initialize(app, options = {})
|
70
71
|
@app = app
|
71
|
-
@request = Unicorn::HttpRequest.new
|
72
72
|
@reexec_pid = 0
|
73
|
+
@default_middleware = true
|
73
74
|
options = options.dup
|
74
75
|
@ready_pipe = options.delete(:ready_pipe)
|
75
76
|
@init_listeners = options[:listeners] ? options[:listeners].dup : []
|
@@ -82,7 +83,7 @@ class Unicorn::HttpServer
|
|
82
83
|
# * The master process never closes or reinitializes this once
|
83
84
|
# initialized. Signal handlers in the master process will write to
|
84
85
|
# it to wake up the master from IO.select in exactly the same manner
|
85
|
-
# djb describes in
|
86
|
+
# djb describes in https://cr.yp.to/docs/selfpipe.html
|
86
87
|
#
|
87
88
|
# * The workers immediately close the pipe they inherit. See the
|
88
89
|
# Unicorn::Worker class for the pipe workers use.
|
@@ -380,7 +381,7 @@ class Unicorn::HttpServer
|
|
380
381
|
|
381
382
|
# wait for a signal hander to wake us up and then consume the pipe
|
382
383
|
def master_sleep(sec)
|
383
|
-
@self_pipe[0].
|
384
|
+
@self_pipe[0].wait(sec) or return
|
384
385
|
# 11 bytes is the maximum string length which can be embedded within
|
385
386
|
# the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
|
386
387
|
# Most reads are only one byte here and uncommon, so it's not worth a
|
@@ -520,9 +521,6 @@ class Unicorn::HttpServer
|
|
520
521
|
Unicorn::Configurator::RACKUP.clear
|
521
522
|
@ready_pipe = @init_listeners = @before_exec = @before_fork = nil
|
522
523
|
|
523
|
-
# http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
|
524
|
-
srand # remove in unicorn 6
|
525
|
-
|
526
524
|
# The OpenSSL PRNG is seeded with only the pid, and apps with frequently
|
527
525
|
# dying workers can recycle pids
|
528
526
|
OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
|
@@ -589,6 +587,25 @@ class Unicorn::HttpServer
|
|
589
587
|
rescue
|
590
588
|
end
|
591
589
|
|
590
|
+
def e103_response_write(client, headers)
|
591
|
+
response = if @request.response_start_sent
|
592
|
+
"103 Early Hints\r\n"
|
593
|
+
else
|
594
|
+
"HTTP/1.1 103 Early Hints\r\n"
|
595
|
+
end
|
596
|
+
|
597
|
+
headers.each_pair do |k, vs|
|
598
|
+
next if !vs || vs.empty?
|
599
|
+
values = vs.to_s.split("\n".freeze)
|
600
|
+
values.each do |v|
|
601
|
+
response << "#{k}: #{v}\r\n"
|
602
|
+
end
|
603
|
+
end
|
604
|
+
response << "\r\n".freeze
|
605
|
+
response << "HTTP/1.1 ".freeze if @request.response_start_sent
|
606
|
+
client.write(response)
|
607
|
+
end
|
608
|
+
|
592
609
|
def e100_response_write(client, env)
|
593
610
|
# We use String#freeze to avoid allocations under Ruby 2.1+
|
594
611
|
# Not many users hit this code path, so it's better to reduce the
|
@@ -603,7 +620,18 @@ class Unicorn::HttpServer
|
|
603
620
|
# once a client is accepted, it is processed in its entirety here
|
604
621
|
# in 3 easy steps: read request, call app, write app response
|
605
622
|
def process_client(client)
|
606
|
-
|
623
|
+
@request = Unicorn::HttpRequest.new
|
624
|
+
env = @request.read(client)
|
625
|
+
|
626
|
+
if early_hints
|
627
|
+
env["rack.early_hints"] = lambda do |headers|
|
628
|
+
e103_response_write(client, headers)
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
env["rack.after_reply"] = []
|
633
|
+
|
634
|
+
status, headers, body = @app.call(env)
|
607
635
|
|
608
636
|
begin
|
609
637
|
return if @request.hijacked?
|
@@ -625,6 +653,8 @@ class Unicorn::HttpServer
|
|
625
653
|
end
|
626
654
|
rescue => e
|
627
655
|
handle_error(client, e)
|
656
|
+
ensure
|
657
|
+
env["rack.after_reply"].each(&:call) if env
|
628
658
|
end
|
629
659
|
|
630
660
|
def nuke_listeners!(readers)
|
@@ -687,6 +717,7 @@ class Unicorn::HttpServer
|
|
687
717
|
trap(:USR1) { nr = -65536 }
|
688
718
|
|
689
719
|
ready = readers.dup
|
720
|
+
nr_listeners = readers.size
|
690
721
|
@after_worker_ready.call(self, worker)
|
691
722
|
|
692
723
|
begin
|
@@ -709,7 +740,7 @@ class Unicorn::HttpServer
|
|
709
740
|
# we're probably reasonably busy, so avoid calling select()
|
710
741
|
# and do a speculative non-blocking accept() on ready listeners
|
711
742
|
# before we sleep again in select().
|
712
|
-
|
743
|
+
if nr == nr_listeners
|
713
744
|
tmp = ready.dup
|
714
745
|
redo
|
715
746
|
end
|
@@ -787,12 +818,12 @@ class Unicorn::HttpServer
|
|
787
818
|
end
|
788
819
|
|
789
820
|
def build_app!
|
790
|
-
if app.respond_to?(:arity) && app.arity == 0
|
821
|
+
if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
|
791
822
|
if defined?(Gem) && Gem.respond_to?(:refresh)
|
792
823
|
logger.info "Refreshing Gem list"
|
793
824
|
Gem.refresh
|
794
825
|
end
|
795
|
-
self.app = app.call
|
826
|
+
self.app = app.arity == 0 ? app.call : app.call(nil, self)
|
796
827
|
end
|
797
828
|
end
|
798
829
|
|
data/lib/unicorn/launcher.rb
CHANGED
data/lib/unicorn/oob_gc.rb
CHANGED
@@ -43,8 +43,8 @@
|
|
43
43
|
# use Unicorn::OobGC, 2, %r{\A/(?:expensive/foo|more_expensive/foo)}
|
44
44
|
#
|
45
45
|
# Feedback from users of early implementations of this module:
|
46
|
-
# * https://
|
47
|
-
# * https://
|
46
|
+
# * https://yhbt.net/unicorn-public/0BFC98E9-072B-47EE-9A70-05478C20141B@lukemelia.com/
|
47
|
+
# * https://yhbt.net/unicorn-public/AANLkTilUbgdyDv9W1bi-s_W6kq9sOhWfmuYkKLoKGOLj@mail.gmail.com/
|
48
48
|
|
49
49
|
module Unicorn::OobGC
|
50
50
|
|
@@ -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/tmpio.rb
CHANGED
@@ -11,12 +11,18 @@ class Unicorn::TmpIO < File
|
|
11
11
|
# immediately, switched to binary mode, and userspace output
|
12
12
|
# buffering is disabled
|
13
13
|
def self.new
|
14
|
+
path = nil
|
15
|
+
|
16
|
+
# workaround File#path being tainted:
|
17
|
+
# https://bugs.ruby-lang.org/issues/14485
|
14
18
|
fp = begin
|
15
|
-
|
19
|
+
path = "#{Dir::tmpdir}/#{rand}"
|
20
|
+
super(path, RDWR|CREAT|EXCL, 0600)
|
16
21
|
rescue Errno::EEXIST
|
17
22
|
retry
|
18
23
|
end
|
19
|
-
|
24
|
+
|
25
|
+
unlink(path)
|
20
26
|
fp.binmode
|
21
27
|
fp.sync = true
|
22
28
|
fp
|
data/lib/unicorn/util.rb
CHANGED
@@ -64,7 +64,7 @@ module Unicorn::Util # :nodoc:
|
|
64
64
|
fp.reopen(fp.path, "a")
|
65
65
|
else
|
66
66
|
# We should not need this workaround, Ruby can be fixed:
|
67
|
-
#
|
67
|
+
# https://bugs.ruby-lang.org/issues/9036
|
68
68
|
# MRI will not call call fclose(3) or freopen(3) here
|
69
69
|
# since there's no associated std{in,out,err} FILE * pointer
|
70
70
|
# This should atomically use dup3(2) (or dup2(2)) syscall
|