unicorn 5.2.0 → 5.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.manifest +1 -0
- data/.olddoc.yml +0 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/HACKING +0 -1
- data/ISSUES +12 -6
- data/LATEST +93 -27
- data/Links +1 -1
- data/NEWS +216 -0
- data/TUNING +19 -1
- data/ext/unicorn_http/common_field_optimization.h +2 -2
- data/ext/unicorn_http/ext_help.h +0 -20
- data/ext/unicorn_http/extconf.rb +1 -1
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +2 -2
- data/ext/unicorn_http/unicorn_http.c +9 -4
- data/ext/unicorn_http/unicorn_http.rl +9 -4
- data/lib/unicorn.rb +1 -1
- data/lib/unicorn/configurator.rb +65 -7
- data/lib/unicorn/http_request.rb +89 -9
- data/lib/unicorn/http_server.rb +63 -19
- data/lib/unicorn/oob_gc.rb +1 -2
- data/lib/unicorn/socket_helper.rb +19 -4
- data/lib/unicorn/stream_input.rb +5 -4
- data/lib/unicorn/tee_input.rb +8 -10
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +17 -6
- data/t/t0011-active-unix-socket.sh +1 -1
- data/t/t0012-reload-empty-config.sh +2 -1
- data/t/test-lib.sh +2 -2
- data/test/exec/test_exec.rb +6 -5
- data/test/unit/test_ccc.rb +90 -0
- data/test/unit/test_http_parser.rb +0 -18
- data/test/unit/test_socket_helper.rb +8 -4
- data/test/unit/test_util.rb +2 -2
- data/unicorn.gemspec +10 -12
- metadata +4 -17
data/TUNING
CHANGED
@@ -72,10 +72,28 @@ See Unicorn::Configurator for details on the config file format.
|
|
72
72
|
have them unbuffered (File#sync = true) or they are
|
73
73
|
record(line)-buffered in userspace before any writes.
|
74
74
|
|
75
|
-
== Kernel Parameters (Linux sysctl)
|
75
|
+
== Kernel Parameters (Linux sysctl and sysfs)
|
76
76
|
|
77
77
|
WARNING: Do not change system parameters unless you know what you're doing!
|
78
78
|
|
79
|
+
* Transparent hugepages (THP) improves performance in many cases,
|
80
|
+
but can also increase memory use when relying on a
|
81
|
+
copy-on-write(CoW)-friendly GC (Ruby 2.0+) with "preload_app true".
|
82
|
+
CoW operates at the page level, so writing to a huge page would
|
83
|
+
trigger a 2 MB copy (x86-64), as opposed to a 4 KB copy on a
|
84
|
+
regular (non-huge) page.
|
85
|
+
|
86
|
+
Consider only allowing THP to be used when it is requested via the
|
87
|
+
madvise(2) syscall:
|
88
|
+
|
89
|
+
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
|
90
|
+
|
91
|
+
Or disabling it system-wide, via "never".
|
92
|
+
|
93
|
+
n.b. "page" in this context only applies to the OS kernel,
|
94
|
+
Ruby GC implementations also use this term for the same concept
|
95
|
+
in a way that is agnostic to the OS.
|
96
|
+
|
79
97
|
* net.core.rmem_max and net.core.wmem_max can increase the allowed
|
80
98
|
size of :rcvbuf and :sndbuf respectively. This is mostly only useful
|
81
99
|
for UNIX domain sockets which do not have auto-tuning buffer sizes.
|
@@ -60,7 +60,7 @@ static struct common_field common_http_fields[] = {
|
|
60
60
|
#define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
|
61
61
|
|
62
62
|
/* this function is not performance-critical, called only at load time */
|
63
|
-
static void init_common_fields(
|
63
|
+
static void init_common_fields(VALUE mark_ary)
|
64
64
|
{
|
65
65
|
int i;
|
66
66
|
struct common_field *cf = common_http_fields;
|
@@ -77,7 +77,7 @@ static void init_common_fields(void)
|
|
77
77
|
cf->value = rb_str_new(tmp, HTTP_PREFIX_LEN + cf->len);
|
78
78
|
}
|
79
79
|
cf->value = rb_obj_freeze(cf->value);
|
80
|
-
|
80
|
+
rb_ary_push(mark_ary, cf->value);
|
81
81
|
}
|
82
82
|
}
|
83
83
|
|
data/ext/unicorn_http/ext_help.h
CHANGED
@@ -1,26 +1,6 @@
|
|
1
1
|
#ifndef ext_help_h
|
2
2
|
#define ext_help_h
|
3
3
|
|
4
|
-
#ifndef RSTRING_PTR
|
5
|
-
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
6
|
-
#endif /* !defined(RSTRING_PTR) */
|
7
|
-
#ifndef RSTRING_LEN
|
8
|
-
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
9
|
-
#endif /* !defined(RSTRING_LEN) */
|
10
|
-
|
11
|
-
#ifndef HAVE_RB_STR_SET_LEN
|
12
|
-
# ifdef RUBINIUS
|
13
|
-
# error we should never get here with current Rubinius (1.x)
|
14
|
-
# endif
|
15
|
-
/* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
|
16
|
-
static void rb_18_str_set_len(VALUE str, long len)
|
17
|
-
{
|
18
|
-
RSTRING(str)->len = len;
|
19
|
-
RSTRING(str)->ptr[len] = '\0';
|
20
|
-
}
|
21
|
-
# define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
|
22
|
-
#endif /* !defined(HAVE_RB_STR_SET_LEN) */
|
23
|
-
|
24
4
|
/* not all Ruby implementations support frozen objects (Rubinius does not) */
|
25
5
|
#if defined(OBJ_FROZEN)
|
26
6
|
# define assert_frozen(f) assert(OBJ_FROZEN(f) && "unfrozen object")
|
data/ext/unicorn_http/extconf.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
|
5
5
|
have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
|
6
6
|
have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
|
7
|
-
have_func("rb_str_set_len", "ruby.h")
|
7
|
+
have_func("rb_str_set_len", "ruby.h") or abort 'Ruby 1.9.3+ required'
|
8
8
|
have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
|
9
9
|
have_func("gmtime_r", "time.h")
|
10
10
|
|
@@ -56,7 +56,7 @@ NORETURN(static void parser_raise(VALUE klass, const char *));
|
|
56
56
|
/** Defines global strings in the init method. */
|
57
57
|
#define DEF_GLOBAL(N, val) do { \
|
58
58
|
g_##N = rb_obj_freeze(rb_str_new(val, sizeof(val) - 1)); \
|
59
|
-
|
59
|
+
rb_ary_push(mark_ary, g_##N); \
|
60
60
|
} while (0)
|
61
61
|
|
62
62
|
/* Defines the maximum allowed lengths for various input elements.*/
|
@@ -67,7 +67,7 @@ DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewh
|
|
67
67
|
DEF_MAX_LENGTH(REQUEST_PATH, 4096); /* common PATH_MAX on modern systems */
|
68
68
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
69
69
|
|
70
|
-
static void init_globals(
|
70
|
+
static void init_globals(VALUE mark_ary)
|
71
71
|
{
|
72
72
|
DEF_GLOBAL(rack_url_scheme, "rack.url_scheme");
|
73
73
|
DEF_GLOBAL(request_method, "REQUEST_METHOD");
|
data/ext/unicorn_http/httpdate.c
CHANGED
@@ -64,13 +64,13 @@ static VALUE httpdate(VALUE self)
|
|
64
64
|
return buf;
|
65
65
|
}
|
66
66
|
|
67
|
-
void init_unicorn_httpdate(
|
67
|
+
void init_unicorn_httpdate(VALUE mark_ary)
|
68
68
|
{
|
69
69
|
VALUE mod = rb_define_module("Unicorn");
|
70
70
|
mod = rb_define_module_under(mod, "HttpResponse");
|
71
71
|
|
72
72
|
buf = rb_str_new(0, buf_capa - 1);
|
73
|
-
|
73
|
+
rb_ary_push(mark_ary, buf);
|
74
74
|
buf_ptr = RSTRING_PTR(buf);
|
75
75
|
httpdate(Qnil);
|
76
76
|
|
@@ -15,7 +15,7 @@
|
|
15
15
|
#include "global_variables.h"
|
16
16
|
#include "c_util.h"
|
17
17
|
|
18
|
-
void init_unicorn_httpdate(
|
18
|
+
void init_unicorn_httpdate(VALUE mark_ary);
|
19
19
|
|
20
20
|
#define UH_FL_CHUNKED 0x1
|
21
21
|
#define UH_FL_HASBODY 0x2
|
@@ -4211,8 +4211,11 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
4211
4211
|
|
4212
4212
|
void Init_unicorn_http(void)
|
4213
4213
|
{
|
4214
|
+
static VALUE mark_ary;
|
4214
4215
|
VALUE mUnicorn, cHttpParser;
|
4215
4216
|
|
4217
|
+
mark_ary = rb_ary_new();
|
4218
|
+
rb_global_variable(&mark_ary);
|
4216
4219
|
mUnicorn = rb_define_module("Unicorn");
|
4217
4220
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
4218
4221
|
eHttpParserError =
|
@@ -4222,7 +4225,7 @@ void Init_unicorn_http(void)
|
|
4222
4225
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
4223
4226
|
eHttpParserError);
|
4224
4227
|
|
4225
|
-
init_globals();
|
4228
|
+
init_globals(mark_ary);
|
4226
4229
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
4227
4230
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
4228
4231
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -4258,14 +4261,16 @@ void Init_unicorn_http(void)
|
|
4258
4261
|
|
4259
4262
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
4260
4263
|
|
4261
|
-
init_common_fields();
|
4264
|
+
init_common_fields(mark_ary);
|
4262
4265
|
SET_GLOBAL(g_http_host, "HOST");
|
4263
4266
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
4264
4267
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
4265
4268
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
4266
4269
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
4267
4270
|
id_set_backtrace = rb_intern("set_backtrace");
|
4268
|
-
init_unicorn_httpdate();
|
4271
|
+
init_unicorn_httpdate(mark_ary);
|
4272
|
+
|
4273
|
+
OBJ_FREEZE(mark_ary);
|
4269
4274
|
|
4270
4275
|
#ifndef HAVE_RB_HASH_CLEAR
|
4271
4276
|
id_clear = rb_intern("clear");
|
@@ -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(VALUE mark_ary);
|
17
17
|
|
18
18
|
#define UH_FL_CHUNKED 0x1
|
19
19
|
#define UH_FL_HASBODY 0x2
|
@@ -917,8 +917,11 @@ static VALUE HttpParser_rssget(VALUE self)
|
|
917
917
|
|
918
918
|
void Init_unicorn_http(void)
|
919
919
|
{
|
920
|
+
static VALUE mark_ary;
|
920
921
|
VALUE mUnicorn, cHttpParser;
|
921
922
|
|
923
|
+
mark_ary = rb_ary_new();
|
924
|
+
rb_global_variable(&mark_ary);
|
922
925
|
mUnicorn = rb_define_module("Unicorn");
|
923
926
|
cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
|
924
927
|
eHttpParserError =
|
@@ -928,7 +931,7 @@ void Init_unicorn_http(void)
|
|
928
931
|
e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
|
929
932
|
eHttpParserError);
|
930
933
|
|
931
|
-
init_globals();
|
934
|
+
init_globals(mark_ary);
|
932
935
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
933
936
|
rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
|
934
937
|
rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
|
@@ -964,14 +967,16 @@ void Init_unicorn_http(void)
|
|
964
967
|
|
965
968
|
rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
|
966
969
|
|
967
|
-
init_common_fields();
|
970
|
+
init_common_fields(mark_ary);
|
968
971
|
SET_GLOBAL(g_http_host, "HOST");
|
969
972
|
SET_GLOBAL(g_http_trailer, "TRAILER");
|
970
973
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
971
974
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
972
975
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
973
976
|
id_set_backtrace = rb_intern("set_backtrace");
|
974
|
-
init_unicorn_httpdate();
|
977
|
+
init_unicorn_httpdate(mark_ary);
|
978
|
+
|
979
|
+
OBJ_FREEZE(mark_ary);
|
975
980
|
|
976
981
|
#ifndef HAVE_RB_HASH_CLEAR
|
977
982
|
id_clear = rb_intern("clear");
|
data/lib/unicorn.rb
CHANGED
@@ -95,7 +95,7 @@ def self.builder(ru, op)
|
|
95
95
|
|
96
96
|
# returns an array of strings representing TCP listen socket addresses
|
97
97
|
# and Unix domain socket paths. This is useful for use with
|
98
|
-
# Raindrops::Middleware under Linux:
|
98
|
+
# Raindrops::Middleware under Linux: https://bogomips.org/raindrops/
|
99
99
|
def self.listener_names
|
100
100
|
Unicorn::HttpServer::LISTENERS.map do |io|
|
101
101
|
Unicorn::SocketHelper.sock_name(io)
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -41,10 +41,22 @@ class Unicorn::Configurator
|
|
41
41
|
:before_exec => lambda { |server|
|
42
42
|
server.logger.info("forked child re-executing...")
|
43
43
|
},
|
44
|
+
:after_worker_exit => lambda { |server, worker, status|
|
45
|
+
m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
|
46
|
+
if status.success?
|
47
|
+
server.logger.info(m)
|
48
|
+
else
|
49
|
+
server.logger.error(m)
|
50
|
+
end
|
51
|
+
},
|
52
|
+
:after_worker_ready => lambda { |server, worker|
|
53
|
+
server.logger.info("worker=#{worker.nr} ready")
|
54
|
+
},
|
44
55
|
:pid => nil,
|
56
|
+
:worker_exec => false,
|
45
57
|
:preload_app => false,
|
46
58
|
:check_client_connection => false,
|
47
|
-
:rewindable_input => true,
|
59
|
+
:rewindable_input => true,
|
48
60
|
:client_body_buffer_size => Unicorn::Const::MAX_BODY,
|
49
61
|
}
|
50
62
|
#:startdoc:
|
@@ -151,6 +163,38 @@ def after_fork(*args, &block)
|
|
151
163
|
set_hook(:after_fork, block_given? ? block : args[0])
|
152
164
|
end
|
153
165
|
|
166
|
+
# sets after_worker_exit hook to a given block. This block will be called
|
167
|
+
# by the master process after a worker exits:
|
168
|
+
#
|
169
|
+
# after_worker_exit do |server,worker,status|
|
170
|
+
# # status is a Process::Status instance for the exited worker process
|
171
|
+
# unless status.success?
|
172
|
+
# server.logger.error("worker process failure: #{status.inspect}")
|
173
|
+
# end
|
174
|
+
# end
|
175
|
+
#
|
176
|
+
# after_worker_exit is only available in unicorn 5.3.0+
|
177
|
+
def after_worker_exit(*args, &block)
|
178
|
+
set_hook(:after_worker_exit, block_given? ? block : args[0], 3)
|
179
|
+
end
|
180
|
+
|
181
|
+
# sets after_worker_ready hook to a given block. This block will be called
|
182
|
+
# by a worker process after it has been fully loaded, directly before it
|
183
|
+
# starts responding to requests:
|
184
|
+
#
|
185
|
+
# after_worker_ready do |server,worker|
|
186
|
+
# server.logger.info("worker #{worker.nr} ready, dropping privileges")
|
187
|
+
# worker.user('username', 'groupname')
|
188
|
+
# end
|
189
|
+
#
|
190
|
+
# Do not use Configurator#user if you rely on changing users in the
|
191
|
+
# after_worker_ready hook.
|
192
|
+
#
|
193
|
+
# after_worker_ready is only available in unicorn 5.3.0+
|
194
|
+
def after_worker_ready(*args, &block)
|
195
|
+
set_hook(:after_worker_ready, block_given? ? block : args[0])
|
196
|
+
end
|
197
|
+
|
154
198
|
# sets before_fork got be a given Proc object. This Proc
|
155
199
|
# object will be called by the master process before forking
|
156
200
|
# each worker.
|
@@ -200,6 +244,17 @@ def timeout(seconds)
|
|
200
244
|
set[:timeout] = seconds > max ? max : seconds
|
201
245
|
end
|
202
246
|
|
247
|
+
# Whether to exec in each worker process after forking. This changes the
|
248
|
+
# memory layout of each worker process, which is a security feature designed
|
249
|
+
# to defeat possible address space discovery attacks. Note that using
|
250
|
+
# worker_exec only makes sense if you are not preloading the application,
|
251
|
+
# and will result in higher memory usage.
|
252
|
+
#
|
253
|
+
# worker_exec is only available in unicorn 5.3.0+
|
254
|
+
def worker_exec(bool)
|
255
|
+
set_bool(:worker_exec, bool)
|
256
|
+
end
|
257
|
+
|
203
258
|
# sets the current number of worker_processes to +nr+. Each worker
|
204
259
|
# process will serve exactly one client at a time. You can
|
205
260
|
# increment or decrement this value at runtime by sending SIGTTIN
|
@@ -466,13 +521,12 @@ def preload_app(bool)
|
|
466
521
|
# Disabling rewindability can improve performance by lowering
|
467
522
|
# I/O and memory usage for applications that accept uploads.
|
468
523
|
# Keep in mind that the Rack 1.x spec requires
|
469
|
-
# \env[\"rack.input\"] to be rewindable,
|
470
|
-
#
|
524
|
+
# \env[\"rack.input\"] to be rewindable,
|
525
|
+
# but the Rack 2.x spec does not.
|
471
526
|
#
|
472
|
-
# +rewindable_input+ defaults to +true+
|
473
|
-
#
|
474
|
-
#
|
475
|
-
# (less demanding) spec.
|
527
|
+
# +rewindable_input+ defaults to +true+ for compatibility.
|
528
|
+
# Setting it to +false+ may be safe for applications and
|
529
|
+
# frameworks developed for Rack 2.x and later.
|
476
530
|
def rewindable_input(bool)
|
477
531
|
set_bool(:rewindable_input, bool)
|
478
532
|
end
|
@@ -548,6 +602,10 @@ def working_directory(path)
|
|
548
602
|
# This switch will occur after calling the after_fork hook, and only
|
549
603
|
# if the Worker#user method is not called in the after_fork hook
|
550
604
|
# +group+ is optional and will not change if unspecified.
|
605
|
+
#
|
606
|
+
# Do not use Configurator#user if you rely on changing users in the
|
607
|
+
# after_worker_ready hook. Instead, you need to call Worker#user
|
608
|
+
# directly in after_worker_ready.
|
551
609
|
def user(user, group = nil)
|
552
610
|
# raises ArgumentError on invalid user/group
|
553
611
|
Etc.getpwnam(user)
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
# :enddoc:
|
3
3
|
# no stable API here
|
4
4
|
require 'unicorn_http'
|
5
|
+
require 'raindrops'
|
5
6
|
|
6
7
|
# TODO: remove redundant names
|
7
8
|
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
@@ -24,12 +25,11 @@ class Unicorn::HttpParser
|
|
24
25
|
NULL_IO = StringIO.new("")
|
25
26
|
|
26
27
|
# :stopdoc:
|
27
|
-
|
28
|
-
|
29
|
-
# 2.2+ optimizes hash assignments when used with literal string keys
|
30
|
-
HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
|
28
|
+
HTTP_RESPONSE_START = [ 'HTTP'.freeze, '/1.1 '.freeze ]
|
29
|
+
EMPTY_ARRAY = [].freeze
|
31
30
|
@@input_class = Unicorn::TeeInput
|
32
31
|
@@check_client_connection = false
|
32
|
+
@@tcpi_inspect_ok = Socket.const_defined?(:TCP_INFO)
|
33
33
|
|
34
34
|
def self.input_class
|
35
35
|
@@input_class
|
@@ -83,11 +83,7 @@ def read(socket)
|
|
83
83
|
false until add_parse(socket.kgio_read!(16384))
|
84
84
|
end
|
85
85
|
|
86
|
-
|
87
|
-
if @@check_client_connection && headers?
|
88
|
-
self.response_start_sent = true
|
89
|
-
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
90
|
-
end
|
86
|
+
check_client_connection(socket) if @@check_client_connection
|
91
87
|
|
92
88
|
e['rack.input'] = 0 == content_length ?
|
93
89
|
NULL_IO : @@input_class.new(socket, self)
|
@@ -108,4 +104,88 @@ def call
|
|
108
104
|
def hijacked?
|
109
105
|
env.include?('rack.hijack_io'.freeze)
|
110
106
|
end
|
107
|
+
|
108
|
+
if Raindrops.const_defined?(:TCP_Info)
|
109
|
+
TCPI = Raindrops::TCP_Info.allocate
|
110
|
+
|
111
|
+
def check_client_connection(socket) # :nodoc:
|
112
|
+
if Unicorn::TCPClient === socket
|
113
|
+
# Raindrops::TCP_Info#get!, #state (reads struct tcp_info#tcpi_state)
|
114
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
115
|
+
EMPTY_ARRAY if closed_state?(TCPI.get!(socket).state)
|
116
|
+
else
|
117
|
+
write_http_header(socket)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
if Raindrops.const_defined?(:TCP)
|
122
|
+
# raindrops 0.18.0+ supports FreeBSD + Linux using the same names
|
123
|
+
# Evaluate these hash lookups at load time so we can
|
124
|
+
# generate an opt_case_dispatch instruction
|
125
|
+
eval <<-EOS
|
126
|
+
def closed_state?(state) # :nodoc:
|
127
|
+
case state
|
128
|
+
when #{Raindrops::TCP[:ESTABLISHED]}
|
129
|
+
false
|
130
|
+
when #{Raindrops::TCP.values_at(
|
131
|
+
:CLOSE_WAIT, :TIME_WAIT, :CLOSE, :LAST_ACK, :CLOSING).join(',')}
|
132
|
+
true
|
133
|
+
else
|
134
|
+
false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
EOS
|
138
|
+
else
|
139
|
+
# raindrops before 0.18 only supported TCP_INFO under Linux
|
140
|
+
def closed_state?(state) # :nodoc:
|
141
|
+
case state
|
142
|
+
when 1 # ESTABLISHED
|
143
|
+
false
|
144
|
+
when 8, 6, 7, 9, 11 # CLOSE_WAIT, TIME_WAIT, CLOSE, LAST_ACK, CLOSING
|
145
|
+
true
|
146
|
+
else
|
147
|
+
false
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
else
|
152
|
+
|
153
|
+
# Ruby 2.2+ can show struct tcp_info as a string Socket::Option#inspect.
|
154
|
+
# Not that efficient, but probably still better than doing unnecessary
|
155
|
+
# work after a client gives up.
|
156
|
+
def check_client_connection(socket) # :nodoc:
|
157
|
+
if Unicorn::TCPClient === socket && @@tcpi_inspect_ok
|
158
|
+
opt = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO).inspect
|
159
|
+
if opt =~ /\bstate=(\S+)/
|
160
|
+
raise Errno::EPIPE, "client closed connection".freeze,
|
161
|
+
EMPTY_ARRAY if closed_state_str?($1)
|
162
|
+
else
|
163
|
+
@@tcpi_inspect_ok = false
|
164
|
+
write_http_header(socket)
|
165
|
+
end
|
166
|
+
opt.clear
|
167
|
+
else
|
168
|
+
write_http_header(socket)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def closed_state_str?(state)
|
173
|
+
case state
|
174
|
+
when 'ESTABLISHED'
|
175
|
+
false
|
176
|
+
# not a typo, ruby maps TCP_CLOSE (no 'D') to state=CLOSED (w/ 'D')
|
177
|
+
when 'CLOSE_WAIT', 'TIME_WAIT', 'CLOSED', 'LAST_ACK', 'CLOSING'
|
178
|
+
true
|
179
|
+
else
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def write_http_header(socket) # :nodoc:
|
186
|
+
if headers?
|
187
|
+
self.response_start_sent = true
|
188
|
+
HTTP_RESPONSE_START.each { |c| socket.write(c) }
|
189
|
+
end
|
190
|
+
end
|
111
191
|
end
|