unicorn 5.3.1 → 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 +5 -5
- data/.manifest +10 -5
- data/.olddoc.yml +15 -7
- data/Application_Timeouts +4 -4
- data/CONTRIBUTORS +6 -2
- 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 +117 -57
- data/HACKING +2 -9
- data/ISSUES +33 -32
- data/KNOWN_ISSUES +2 -2
- data/LATEST +16 -95
- data/LICENSE +2 -2
- data/Links +13 -11
- data/NEWS +239 -0
- data/README +27 -14
- data/SIGNALS +1 -1
- data/Sandbox +5 -5
- 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/c_util.h +5 -13
- data/ext/unicorn_http/common_field_optimization.h +23 -6
- data/ext/unicorn_http/epollexclusive.h +124 -0
- data/ext/unicorn_http/ext_help.h +0 -24
- data/ext/unicorn_http/extconf.rb +32 -6
- data/ext/unicorn_http/global_variables.h +3 -3
- data/ext/unicorn_http/httpdate.c +3 -2
- data/ext/unicorn_http/unicorn_http.c +277 -237
- data/ext/unicorn_http/unicorn_http.rl +67 -27
- data/lib/unicorn/configurator.rb +26 -5
- data/lib/unicorn/http_request.rb +13 -3
- data/lib/unicorn/http_response.rb +3 -2
- data/lib/unicorn/http_server.rb +76 -51
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +5 -5
- data/lib/unicorn/select_waiter.rb +6 -0
- data/lib/unicorn/socket_helper.rb +4 -3
- data/lib/unicorn/tmpio.rb +8 -2
- data/lib/unicorn/util.rb +3 -3
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +16 -2
- data/lib/unicorn.rb +25 -10
- data/man/man1/unicorn.1 +88 -85
- data/man/man1/unicorn_rails.1 +79 -81
- 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/t/test-lib.sh +2 -1
- 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 +20 -19
- data/test/test_helper.rb +38 -30
- data/test/unit/test_ccc.rb +5 -4
- data/test/unit/test_droplet.rb +1 -1
- data/test/unit/test_http_parser.rb +16 -0
- data/test/unit/test_http_parser_ng.rb +81 -0
- data/test/unit/test_request.rb +10 -10
- data/test/unit/test_server.rb +86 -12
- data/test/unit/test_signals.rb +8 -8
- data/test/unit/test_socket_helper.rb +5 -5
- data/test/unit/test_upload.rb +9 -14
- data/test/unit/test_util.rb +29 -3
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +8 -7
- metadata +19 -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 -43
- data/t/t0200-rack-hijack.sh +0 -30
@@ -0,0 +1,124 @@
|
|
1
|
+
/*
|
2
|
+
* This is only intended for use inside a unicorn worker, nowhere else.
|
3
|
+
* EPOLLEXCLUSIVE somewhat mitigates the thundering herd problem for
|
4
|
+
* mostly idle processes since we can't use blocking accept4.
|
5
|
+
* This is NOT intended for use with multi-threaded servers, nor
|
6
|
+
* single-threaded multi-client ("C10K") servers or anything advanced
|
7
|
+
* like that. This use of epoll is only appropriate for a primitive,
|
8
|
+
* single-client, single-threaded servers like unicorn that need to
|
9
|
+
* support SIGKILL timeouts and parent death detection.
|
10
|
+
*/
|
11
|
+
#if defined(HAVE_EPOLL_CREATE1)
|
12
|
+
# include <sys/epoll.h>
|
13
|
+
# include <errno.h>
|
14
|
+
# include <ruby/io.h>
|
15
|
+
# include <ruby/thread.h>
|
16
|
+
#endif /* __linux__ */
|
17
|
+
|
18
|
+
#if defined(EPOLLEXCLUSIVE) && defined(HAVE_EPOLL_CREATE1)
|
19
|
+
# define USE_EPOLL (1)
|
20
|
+
#else
|
21
|
+
# define USE_EPOLL (0)
|
22
|
+
#endif
|
23
|
+
|
24
|
+
#if USE_EPOLL
|
25
|
+
/*
|
26
|
+
* :nodoc:
|
27
|
+
* returns IO object if EPOLLEXCLUSIVE works and arms readers
|
28
|
+
*/
|
29
|
+
static VALUE prep_readers(VALUE cls, VALUE readers)
|
30
|
+
{
|
31
|
+
long i;
|
32
|
+
int epfd = epoll_create1(EPOLL_CLOEXEC);
|
33
|
+
VALUE epio;
|
34
|
+
|
35
|
+
if (epfd < 0) rb_sys_fail("epoll_create1");
|
36
|
+
|
37
|
+
epio = rb_funcall(cls, rb_intern("for_fd"), 1, INT2NUM(epfd));
|
38
|
+
|
39
|
+
Check_Type(readers, T_ARRAY);
|
40
|
+
for (i = 0; i < RARRAY_LEN(readers); i++) {
|
41
|
+
int rc;
|
42
|
+
struct epoll_event e;
|
43
|
+
rb_io_t *fptr;
|
44
|
+
VALUE io = rb_ary_entry(readers, i);
|
45
|
+
|
46
|
+
e.data.u64 = i; /* the reason readers shouldn't change */
|
47
|
+
|
48
|
+
/*
|
49
|
+
* I wanted to use EPOLLET here, but maintaining our own
|
50
|
+
* equivalent of ep->rdllist in Ruby-space doesn't fit
|
51
|
+
* our design at all (and the kernel already has it's own
|
52
|
+
* code path for doing it). So let the kernel spend
|
53
|
+
* cycles on maintaining level-triggering.
|
54
|
+
*/
|
55
|
+
e.events = EPOLLEXCLUSIVE | EPOLLIN;
|
56
|
+
io = rb_io_get_io(io);
|
57
|
+
GetOpenFile(io, fptr);
|
58
|
+
rc = epoll_ctl(epfd, EPOLL_CTL_ADD, fptr->fd, &e);
|
59
|
+
if (rc < 0) rb_sys_fail("epoll_ctl");
|
60
|
+
}
|
61
|
+
return epio;
|
62
|
+
}
|
63
|
+
#endif /* USE_EPOLL */
|
64
|
+
|
65
|
+
#if USE_EPOLL
|
66
|
+
struct ep_wait {
|
67
|
+
struct epoll_event *events;
|
68
|
+
rb_io_t *fptr;
|
69
|
+
int maxevents;
|
70
|
+
int timeout_msec;
|
71
|
+
};
|
72
|
+
|
73
|
+
static void *do_wait(void *ptr) /* runs w/o GVL */
|
74
|
+
{
|
75
|
+
struct ep_wait *epw = ptr;
|
76
|
+
|
77
|
+
return (void *)(long)epoll_wait(epw->fptr->fd, epw->events,
|
78
|
+
epw->maxevents, epw->timeout_msec);
|
79
|
+
}
|
80
|
+
|
81
|
+
/* :nodoc: */
|
82
|
+
/* readers must not change between prepare_readers and get_readers */
|
83
|
+
static VALUE
|
84
|
+
get_readers(VALUE epio, VALUE ready, VALUE readers, VALUE timeout_msec)
|
85
|
+
{
|
86
|
+
struct ep_wait epw;
|
87
|
+
long i, n;
|
88
|
+
VALUE buf;
|
89
|
+
|
90
|
+
Check_Type(ready, T_ARRAY);
|
91
|
+
Check_Type(readers, T_ARRAY);
|
92
|
+
epw.maxevents = RARRAY_LENINT(readers);
|
93
|
+
buf = rb_str_buf_new(sizeof(struct epoll_event) * epw.maxevents);
|
94
|
+
epw.events = (struct epoll_event *)RSTRING_PTR(buf);
|
95
|
+
epio = rb_io_get_io(epio);
|
96
|
+
GetOpenFile(epio, epw.fptr);
|
97
|
+
|
98
|
+
epw.timeout_msec = NUM2INT(timeout_msec);
|
99
|
+
n = (long)rb_thread_call_without_gvl(do_wait, &epw, RUBY_UBF_IO, NULL);
|
100
|
+
if (n < 0) {
|
101
|
+
if (errno != EINTR) rb_sys_fail("epoll_wait");
|
102
|
+
n = 0;
|
103
|
+
}
|
104
|
+
/* Linux delivers events in order received */
|
105
|
+
for (i = 0; i < n; i++) {
|
106
|
+
struct epoll_event *ev = &epw.events[i];
|
107
|
+
VALUE obj = rb_ary_entry(readers, ev->data.u64);
|
108
|
+
|
109
|
+
if (RTEST(obj))
|
110
|
+
rb_ary_push(ready, obj);
|
111
|
+
}
|
112
|
+
rb_str_resize(buf, 0);
|
113
|
+
return Qfalse;
|
114
|
+
}
|
115
|
+
#endif /* USE_EPOLL */
|
116
|
+
|
117
|
+
static void init_epollexclusive(VALUE mUnicorn)
|
118
|
+
{
|
119
|
+
#if USE_EPOLL
|
120
|
+
VALUE cWaiter = rb_define_class_under(mUnicorn, "Waiter", rb_cIO);
|
121
|
+
rb_define_singleton_method(cWaiter, "prep_readers", prep_readers, 1);
|
122
|
+
rb_define_method(cWaiter, "get_readers", get_readers, 3);
|
123
|
+
#endif
|
124
|
+
}
|
data/ext/unicorn_http/ext_help.h
CHANGED
@@ -8,30 +8,6 @@
|
|
8
8
|
# define assert_frozen(f) do {} while (0)
|
9
9
|
#endif /* !defined(OBJ_FROZEN) */
|
10
10
|
|
11
|
-
#if !defined(OFFT2NUM)
|
12
|
-
# if SIZEOF_OFF_T == SIZEOF_LONG
|
13
|
-
# define OFFT2NUM(n) LONG2NUM(n)
|
14
|
-
# else
|
15
|
-
# define OFFT2NUM(n) LL2NUM(n)
|
16
|
-
# endif
|
17
|
-
#endif /* ! defined(OFFT2NUM) */
|
18
|
-
|
19
|
-
#if !defined(SIZET2NUM)
|
20
|
-
# if SIZEOF_SIZE_T == SIZEOF_LONG
|
21
|
-
# define SIZET2NUM(n) ULONG2NUM(n)
|
22
|
-
# else
|
23
|
-
# define SIZET2NUM(n) ULL2NUM(n)
|
24
|
-
# endif
|
25
|
-
#endif /* ! defined(SIZET2NUM) */
|
26
|
-
|
27
|
-
#if !defined(NUM2SIZET)
|
28
|
-
# if SIZEOF_SIZE_T == SIZEOF_LONG
|
29
|
-
# define NUM2SIZET(n) ((size_t)NUM2ULONG(n))
|
30
|
-
# else
|
31
|
-
# define NUM2SIZET(n) ((size_t)NUM2ULL(n))
|
32
|
-
# endif
|
33
|
-
#endif /* ! defined(NUM2SIZET) */
|
34
|
-
|
35
11
|
static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
|
36
12
|
{
|
37
13
|
return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
|
data/ext/unicorn_http/extconf.rb
CHANGED
@@ -1,11 +1,37 @@
|
|
1
1
|
# -*- encoding: binary -*-
|
2
2
|
require 'mkmf'
|
3
3
|
|
4
|
-
|
5
|
-
have_macro("SIZEOF_SIZE_T", "ruby.h") or check_sizeof("size_t", "sys/types.h")
|
6
|
-
have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
|
7
|
-
have_func("rb_str_set_len", "ruby.h") or abort 'Ruby 1.9.3+ required'
|
8
|
-
have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
|
9
|
-
have_func("gmtime_r", "time.h")
|
4
|
+
have_func("rb_hash_clear", "ruby.h") or abort 'Ruby 2.0+ required'
|
10
5
|
|
6
|
+
message('checking if String#-@ (str_uminus) dedupes... ')
|
7
|
+
begin
|
8
|
+
a = -(%w(t e s t).join)
|
9
|
+
b = -(%w(t e s t).join)
|
10
|
+
if a.equal?(b)
|
11
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=1 '
|
12
|
+
message("yes\n")
|
13
|
+
else
|
14
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=0 '
|
15
|
+
message("no, needs Ruby 2.5+\n")
|
16
|
+
end
|
17
|
+
rescue NoMethodError
|
18
|
+
$CPPFLAGS += ' -DSTR_UMINUS_DEDUPE=0 '
|
19
|
+
message("no, String#-@ not available\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
message('checking if Hash#[]= (rb_hash_aset) dedupes... ')
|
23
|
+
h = {}
|
24
|
+
x = {}
|
25
|
+
r = rand.to_s
|
26
|
+
h[%W(#{r}).join('')] = :foo
|
27
|
+
x[%W(#{r}).join('')] = :foo
|
28
|
+
if x.keys[0].equal?(h.keys[0])
|
29
|
+
$CPPFLAGS += ' -DHASH_ASET_DEDUPE=1 '
|
30
|
+
message("yes\n")
|
31
|
+
else
|
32
|
+
$CPPFLAGS += ' -DHASH_ASET_DEDUPE=0 '
|
33
|
+
message("no, needs Ruby 2.6+\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
have_func('epoll_create1', %w(sys/epoll.h))
|
11
37
|
create_makefile("unicorn_http")
|
@@ -55,8 +55,8 @@ NORETURN(static void parser_raise(VALUE klass, const char *));
|
|
55
55
|
|
56
56
|
/** Defines global strings in the init method. */
|
57
57
|
#define DEF_GLOBAL(N, val) do { \
|
58
|
-
g_##N =
|
59
|
-
|
58
|
+
g_##N = str_new_dd_freeze(val, (long)sizeof(val) - 1); \
|
59
|
+
rb_gc_register_mark_object(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(void)
|
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
@@ -11,6 +11,7 @@ static const char months[] = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
|
|
11
11
|
|
12
12
|
/* for people on wonky systems only */
|
13
13
|
#ifndef HAVE_GMTIME_R
|
14
|
+
# warning using fake gmtime_r
|
14
15
|
static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
|
15
16
|
{
|
16
17
|
struct tm *global = gmtime(now);
|
@@ -64,13 +65,13 @@ static VALUE httpdate(VALUE self)
|
|
64
65
|
return buf;
|
65
66
|
}
|
66
67
|
|
67
|
-
void init_unicorn_httpdate(
|
68
|
+
void init_unicorn_httpdate(void)
|
68
69
|
{
|
69
70
|
VALUE mod = rb_define_module("Unicorn");
|
70
71
|
mod = rb_define_module_under(mod, "HttpResponse");
|
71
72
|
|
72
73
|
buf = rb_str_new(0, buf_capa - 1);
|
73
|
-
|
74
|
+
rb_gc_register_mark_object(buf);
|
74
75
|
buf_ptr = RSTRING_PTR(buf);
|
75
76
|
httpdate(Qnil);
|
76
77
|
|