unicorn 4.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 +7 -0
- data/.document +0 -1
- data/.gitattributes +5 -0
- data/.gitignore +2 -2
- data/.manifest +17 -21
- data/.olddoc.yml +25 -0
- data/Application_Timeouts +7 -7
- data/CONTRIBUTORS +6 -2
- data/DESIGN +2 -4
- data/Documentation/.gitignore +1 -3
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +23 -6
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +138 -92
- data/HACKING +10 -32
- data/ISSUES +80 -19
- data/KNOWN_ISSUES +18 -18
- data/LATEST +23 -27
- data/LICENSE +2 -2
- data/Links +24 -22
- data/NEWS +823 -0
- data/PHILOSOPHY +0 -6
- data/README +49 -48
- data/Rakefile +0 -44
- data/SIGNALS +2 -2
- data/Sandbox +11 -10
- data/TODO +0 -2
- data/TUNING +30 -9
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +4 -2
- data/bin/unicorn_rails +3 -3
- data/examples/big_app_gc.rb +1 -1
- data/examples/init.sh +36 -8
- data/examples/logrotate.conf +17 -2
- data/examples/nginx.conf +14 -14
- data/examples/unicorn.conf.minimal.rb +2 -2
- data/examples/unicorn.conf.rb +14 -6
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +40 -0
- data/ext/unicorn_http/c_util.h +5 -13
- data/ext/unicorn_http/common_field_optimization.h +22 -5
- data/ext/unicorn_http/epollexclusive.h +124 -0
- data/ext/unicorn_http/ext_help.h +0 -44
- data/ext/unicorn_http/extconf.rb +32 -5
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +3 -2
- data/ext/unicorn_http/unicorn_http.c +926 -638
- data/ext/unicorn_http/unicorn_http.rl +159 -170
- data/ext/unicorn_http/unicorn_http_common.rl +1 -1
- data/lib/unicorn/configurator.rb +110 -46
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +110 -31
- data/lib/unicorn/http_response.rb +17 -31
- data/lib/unicorn/http_server.rb +272 -180
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +16 -6
- data/lib/unicorn/select_waiter.rb +6 -0
- data/lib/unicorn/socket_helper.rb +58 -78
- data/lib/unicorn/stream_input.rb +8 -7
- data/lib/unicorn/tee_input.rb +16 -11
- data/lib/unicorn/tmpio.rb +10 -6
- data/lib/unicorn/util.rb +5 -4
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +36 -23
- data/lib/unicorn.rb +64 -44
- data/man/man1/unicorn.1 +124 -122
- data/man/man1/unicorn_rails.1 +113 -127
- data/t/.gitignore +0 -1
- data/t/GNUmakefile +3 -80
- data/t/README +4 -4
- data/t/t0002-parser-error.sh +3 -3
- data/t/t0011-active-unix-socket.sh +1 -1
- data/t/t0012-reload-empty-config.sh +2 -1
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/t/test-lib.sh +4 -3
- 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 +74 -20
- data/test/test_helper.rb +42 -33
- data/test/unit/test_ccc.rb +91 -0
- data/test/unit/test_droplet.rb +1 -1
- data/test/unit/test_http_parser.rb +49 -19
- data/test/unit/test_http_parser_ng.rb +98 -115
- data/test/unit/test_request.rb +11 -11
- data/test/unit/test_response.rb +31 -19
- data/test/unit/test_server.rb +89 -15
- data/test/unit/test_signals.rb +9 -9
- data/test/unit/test_socket_helper.rb +16 -12
- data/test/unit/test_tee_input.rb +10 -0
- data/test/unit/test_upload.rb +10 -15
- data/test/unit/test_util.rb +32 -6
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +28 -23
- data/unicorn_1 +1 -0
- data/unicorn_rails_1 +1 -0
- metadata +68 -134
- data/.wrongdoc.yml +0 -10
- data/ChangeLog +0 -4861
- data/Documentation/GNUmakefile +0 -30
- data/Documentation/unicorn.1.txt +0 -178
- data/Documentation/unicorn_rails.1.txt +0 -175
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/local.mk.sample +0 -59
- data/script/isolate_for_tests +0 -32
- data/t/hijack.ru +0 -42
- data/t/sslgen.sh +0 -71
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/t/t0200-rack-hijack.sh +0 -27
- data/t/t0600-https-server-basic.sh +0 -48
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
data/examples/nginx.conf
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# This is example contains the bare mininum to get nginx going with
|
|
2
|
-
#
|
|
2
|
+
# unicorn servers. Generally these configuration settings
|
|
3
3
|
# are applicable to other HTTP application servers (and not just Ruby
|
|
4
4
|
# ones), so if you have one working well for proxying another app
|
|
5
5
|
# server, feel free to continue using it.
|
|
@@ -44,8 +44,8 @@ http {
|
|
|
44
44
|
# click tracking!
|
|
45
45
|
access_log /path/to/nginx.access.log combined;
|
|
46
46
|
|
|
47
|
-
# you generally want to serve static files with nginx since
|
|
48
|
-
#
|
|
47
|
+
# you generally want to serve static files with nginx since
|
|
48
|
+
# unicorn is not and will never be optimized for it
|
|
49
49
|
sendfile on;
|
|
50
50
|
|
|
51
51
|
tcp_nopush on; # off may be better for *some* Comet/long-poll stuff
|
|
@@ -56,7 +56,8 @@ http {
|
|
|
56
56
|
# to configure it all in one place here for static files and also
|
|
57
57
|
# to disable gzip for clients who don't get gzip/deflate right.
|
|
58
58
|
# There are other gzip settings that may be needed used to deal with
|
|
59
|
-
# bad clients out there, see
|
|
59
|
+
# bad clients out there, see
|
|
60
|
+
# https://nginx.org/en/docs/http/ngx_http_gzip_module.html
|
|
60
61
|
gzip on;
|
|
61
62
|
gzip_http_version 1.0;
|
|
62
63
|
gzip_proxied any;
|
|
@@ -67,10 +68,10 @@ http {
|
|
|
67
68
|
text/javascript application/x-javascript
|
|
68
69
|
application/atom+xml;
|
|
69
70
|
|
|
70
|
-
# this can be any application server, not just
|
|
71
|
+
# this can be any application server, not just unicorn
|
|
71
72
|
upstream app_server {
|
|
72
73
|
# fail_timeout=0 means we always retry an upstream even if it failed
|
|
73
|
-
# to return a good HTTP response (in case the
|
|
74
|
+
# to return a good HTTP response (in case the unicorn master nukes a
|
|
74
75
|
# single worker for timing out).
|
|
75
76
|
|
|
76
77
|
# for UNIX domain socket setups:
|
|
@@ -112,12 +113,12 @@ http {
|
|
|
112
113
|
# try_files directive appeared in in nginx 0.7.27 and has stabilized
|
|
113
114
|
# over time. Older versions of nginx (e.g. 0.6.x) requires
|
|
114
115
|
# "if (!-f $request_filename)" which was less efficient:
|
|
115
|
-
#
|
|
116
|
+
# https://yhbt.net/unicorn.git/tree/examples/nginx.conf?id=v3.3.1#n127
|
|
116
117
|
try_files $uri/index.html $uri.html $uri @app;
|
|
117
118
|
|
|
118
119
|
location @app {
|
|
119
120
|
# an HTTP header important enough to have its own Wikipedia entry:
|
|
120
|
-
#
|
|
121
|
+
# https://en.wikipedia.org/wiki/X-Forwarded-For
|
|
121
122
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
122
123
|
|
|
123
124
|
# enable this if you forward HTTPS traffic to unicorn,
|
|
@@ -132,12 +133,11 @@ http {
|
|
|
132
133
|
# redirects, we set the Host: header above already.
|
|
133
134
|
proxy_redirect off;
|
|
134
135
|
|
|
135
|
-
# set
|
|
136
|
-
#
|
|
137
|
-
#
|
|
138
|
-
#
|
|
139
|
-
#
|
|
140
|
-
# client can become a bottleneck of Unicorn.
|
|
136
|
+
# It's also safe to set if you're using only serving fast clients
|
|
137
|
+
# with unicorn + nginx, but not slow clients. You normally want
|
|
138
|
+
# nginx to buffer responses to slow clients, even with Rails 3.1
|
|
139
|
+
# streaming because otherwise a slow client can become a bottleneck
|
|
140
|
+
# of unicorn.
|
|
141
141
|
#
|
|
142
142
|
# The Rack application may also set "X-Accel-Buffering (yes|no)"
|
|
143
143
|
# in the response headers do disable/enable buffering on a
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Minimal sample configuration file for Unicorn (not Rack) when used
|
|
2
2
|
# with daemonization (unicorn -D) started in your working directory.
|
|
3
3
|
#
|
|
4
|
-
# See
|
|
4
|
+
# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
|
|
5
5
|
# documentation.
|
|
6
|
-
# See also
|
|
6
|
+
# See also https://yhbt.net/unicorn/examples/unicorn.conf.rb for
|
|
7
7
|
# a more verbose configuration using more features.
|
|
8
8
|
|
|
9
9
|
listen 2007 # by default Unicorn listens on port 8080
|
data/examples/unicorn.conf.rb
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
#
|
|
3
3
|
# This configuration file documents many features of Unicorn
|
|
4
4
|
# that may not be needed for some applications. See
|
|
5
|
-
#
|
|
5
|
+
# https://yhbt.net/unicorn/examples/unicorn.conf.minimal.rb
|
|
6
6
|
# for a much simpler configuration file.
|
|
7
7
|
#
|
|
8
|
-
# See
|
|
8
|
+
# See https://yhbt.net/unicorn/Unicorn/Configurator.html for complete
|
|
9
9
|
# documentation.
|
|
10
10
|
|
|
11
11
|
# Use at least one worker per core if you're on a dedicated server,
|
|
@@ -40,11 +40,8 @@ pid "/path/to/app/shared/pids/unicorn.pid"
|
|
|
40
40
|
stderr_path "/path/to/app/shared/log/unicorn.stderr.log"
|
|
41
41
|
stdout_path "/path/to/app/shared/log/unicorn.stdout.log"
|
|
42
42
|
|
|
43
|
-
# combine Ruby 2.0.
|
|
44
|
-
# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
|
|
43
|
+
# combine Ruby 2.0.0+ with "preload_app true" for memory savings
|
|
45
44
|
preload_app true
|
|
46
|
-
GC.respond_to?(:copy_on_write_friendly=) and
|
|
47
|
-
GC.copy_on_write_friendly = true
|
|
48
45
|
|
|
49
46
|
# Enable this flag to have unicorn test client connections by writing the
|
|
50
47
|
# beginning of the HTTP headers before calling the application. This
|
|
@@ -54,12 +51,23 @@ GC.respond_to?(:copy_on_write_friendly=) and
|
|
|
54
51
|
# fast LAN.
|
|
55
52
|
check_client_connection false
|
|
56
53
|
|
|
54
|
+
# local variable to guard against running a hook multiple times
|
|
55
|
+
run_once = true
|
|
56
|
+
|
|
57
57
|
before_fork do |server, worker|
|
|
58
58
|
# the following is highly recomended for Rails + "preload_app true"
|
|
59
59
|
# as there's no need for the master process to hold a connection
|
|
60
60
|
defined?(ActiveRecord::Base) and
|
|
61
61
|
ActiveRecord::Base.connection.disconnect!
|
|
62
62
|
|
|
63
|
+
# Occasionally, it may be necessary to run non-idempotent code in the
|
|
64
|
+
# master before forking. Keep in mind the above disconnect! example
|
|
65
|
+
# is idempotent and does not need a guard.
|
|
66
|
+
if run_once
|
|
67
|
+
# do_something_once_here ...
|
|
68
|
+
run_once = false # prevent from firing again
|
|
69
|
+
end
|
|
70
|
+
|
|
63
71
|
# The following is only recommended for memory/DB-constrained
|
|
64
72
|
# installations. It is not needed if your system can house
|
|
65
73
|
# twice as many worker_processes as you have configured.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# ==> /etc/systemd/system/unicorn@.service <==
|
|
2
|
+
# Since SIGUSR2 upgrades do not work under systemd, this service file
|
|
3
|
+
# allows starting two simultaneous services during upgrade time
|
|
4
|
+
# (e.g. unicorn@1 unicorn@2) with the intention that they take
|
|
5
|
+
# turns running in-between upgrades. This should allow upgrading
|
|
6
|
+
# without downtime.
|
|
7
|
+
|
|
8
|
+
[Unit]
|
|
9
|
+
Description = unicorn Rack application server %i
|
|
10
|
+
Wants = unicorn.socket
|
|
11
|
+
After = unicorn.socket
|
|
12
|
+
|
|
13
|
+
[Service]
|
|
14
|
+
# bundler users must use the "--keep-file-descriptors" switch, here:
|
|
15
|
+
# ExecStart = bundle exec --keep-file-descriptors unicorn -c ...
|
|
16
|
+
ExecStart = /usr/bin/unicorn -c /path/to/unicorn.conf.rb /path/to/config.ru
|
|
17
|
+
|
|
18
|
+
# NonBlocking MUST be true if using socket activation with unicorn.
|
|
19
|
+
# Otherwise, there's a small window in-between when the non-blocking
|
|
20
|
+
# flag is set by us and our accept4 call where systemd can momentarily
|
|
21
|
+
# make the socket blocking, causing us to block on accept4:
|
|
22
|
+
NonBlocking = true
|
|
23
|
+
Sockets = unicorn.socket
|
|
24
|
+
|
|
25
|
+
KillSignal = SIGQUIT
|
|
26
|
+
User = nobody
|
|
27
|
+
Group = nogroup
|
|
28
|
+
ExecReload = /bin/kill -HUP $MAINPID
|
|
29
|
+
|
|
30
|
+
# This is based on the Unicorn::Configurator#timeout directive,
|
|
31
|
+
# adding a few seconds for scheduling differences:
|
|
32
|
+
TimeoutStopSec = 62
|
|
33
|
+
|
|
34
|
+
# Only kill the master process, it may be harmful to signal
|
|
35
|
+
# workers via default "control-group" setting since some
|
|
36
|
+
# Ruby extensions and applications misbehave on interrupts
|
|
37
|
+
KillMode = process
|
|
38
|
+
|
|
39
|
+
[Install]
|
|
40
|
+
WantedBy = multi-user.target
|
data/ext/unicorn_http/c_util.h
CHANGED
|
@@ -8,23 +8,15 @@
|
|
|
8
8
|
|
|
9
9
|
#include <unistd.h>
|
|
10
10
|
#include <assert.h>
|
|
11
|
+
#include <limits.h>
|
|
11
12
|
|
|
12
13
|
#define MIN(a,b) (a < b ? a : b)
|
|
13
14
|
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
14
15
|
|
|
15
|
-
#
|
|
16
|
-
# define
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
#if SIZEOF_OFF_T == 4
|
|
21
|
-
# define UH_OFF_T_MAX 0x7fffffff
|
|
22
|
-
#elif SIZEOF_OFF_T == 8
|
|
23
|
-
# if SIZEOF_LONG == 4
|
|
24
|
-
# define UH_OFF_T_MAX 0x7fffffffffffffffLL
|
|
25
|
-
# else
|
|
26
|
-
# define UH_OFF_T_MAX 0x7fffffffffffffff
|
|
27
|
-
# endif
|
|
16
|
+
#if SIZEOF_OFF_T == SIZEOF_INT
|
|
17
|
+
# define UH_OFF_T_MAX INT_MAX
|
|
18
|
+
#elif SIZEOF_OFF_T == SIZEOF_LONG_LONG
|
|
19
|
+
# define UH_OFF_T_MAX LLONG_MAX
|
|
28
20
|
#else
|
|
29
21
|
# error off_t size unknown for this platform!
|
|
30
22
|
#endif /* SIZEOF_OFF_T check */
|
|
@@ -58,6 +58,23 @@ static struct common_field common_http_fields[] = {
|
|
|
58
58
|
|
|
59
59
|
#define HTTP_PREFIX "HTTP_"
|
|
60
60
|
#define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
|
|
61
|
+
static ID id_uminus;
|
|
62
|
+
|
|
63
|
+
/* this dedupes under Ruby 2.5+ (December 2017) */
|
|
64
|
+
static VALUE str_dd_freeze(VALUE str)
|
|
65
|
+
{
|
|
66
|
+
if (STR_UMINUS_DEDUPE)
|
|
67
|
+
return rb_funcall(str, id_uminus, 0);
|
|
68
|
+
|
|
69
|
+
/* freeze,since it speeds up older MRI slightly */
|
|
70
|
+
OBJ_FREEZE(str);
|
|
71
|
+
return str;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
static VALUE str_new_dd_freeze(const char *ptr, long len)
|
|
75
|
+
{
|
|
76
|
+
return str_dd_freeze(rb_str_new(ptr, len));
|
|
77
|
+
}
|
|
61
78
|
|
|
62
79
|
/* this function is not performance-critical, called only at load time */
|
|
63
80
|
static void init_common_fields(void)
|
|
@@ -65,19 +82,19 @@ static void init_common_fields(void)
|
|
|
65
82
|
int i;
|
|
66
83
|
struct common_field *cf = common_http_fields;
|
|
67
84
|
char tmp[64];
|
|
85
|
+
|
|
68
86
|
memcpy(tmp, HTTP_PREFIX, HTTP_PREFIX_LEN);
|
|
69
87
|
|
|
70
88
|
for(i = ARRAY_SIZE(common_http_fields); --i >= 0; cf++) {
|
|
71
89
|
/* Rack doesn't like certain headers prefixed with "HTTP_" */
|
|
72
90
|
if (!strcmp("CONTENT_LENGTH", cf->name) ||
|
|
73
91
|
!strcmp("CONTENT_TYPE", cf->name)) {
|
|
74
|
-
cf->value =
|
|
92
|
+
cf->value = str_new_dd_freeze(cf->name, cf->len);
|
|
75
93
|
} else {
|
|
76
94
|
memcpy(tmp + HTTP_PREFIX_LEN, cf->name, cf->len + 1);
|
|
77
|
-
cf->value =
|
|
95
|
+
cf->value = str_new_dd_freeze(tmp, HTTP_PREFIX_LEN + cf->len);
|
|
78
96
|
}
|
|
79
|
-
|
|
80
|
-
rb_global_variable(&cf->value);
|
|
97
|
+
rb_gc_register_mark_object(cf->value);
|
|
81
98
|
}
|
|
82
99
|
}
|
|
83
100
|
|
|
@@ -105,7 +122,7 @@ static VALUE uncommon_field(const char *field, size_t flen)
|
|
|
105
122
|
memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
|
|
106
123
|
assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0' &&
|
|
107
124
|
"string didn't end with \\0"); /* paranoia */
|
|
108
|
-
return
|
|
125
|
+
return HASH_ASET_DEDUPE ? f : str_dd_freeze(f);
|
|
109
126
|
}
|
|
110
127
|
|
|
111
128
|
#endif /* common_field_optimization_h */
|
|
@@ -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
|
@@ -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")
|
|
@@ -28,30 +8,6 @@ static void rb_18_str_set_len(VALUE str, long len)
|
|
|
28
8
|
# define assert_frozen(f) do {} while (0)
|
|
29
9
|
#endif /* !defined(OBJ_FROZEN) */
|
|
30
10
|
|
|
31
|
-
#if !defined(OFFT2NUM)
|
|
32
|
-
# if SIZEOF_OFF_T == SIZEOF_LONG
|
|
33
|
-
# define OFFT2NUM(n) LONG2NUM(n)
|
|
34
|
-
# else
|
|
35
|
-
# define OFFT2NUM(n) LL2NUM(n)
|
|
36
|
-
# endif
|
|
37
|
-
#endif /* ! defined(OFFT2NUM) */
|
|
38
|
-
|
|
39
|
-
#if !defined(SIZET2NUM)
|
|
40
|
-
# if SIZEOF_SIZE_T == SIZEOF_LONG
|
|
41
|
-
# define SIZET2NUM(n) ULONG2NUM(n)
|
|
42
|
-
# else
|
|
43
|
-
# define SIZET2NUM(n) ULL2NUM(n)
|
|
44
|
-
# endif
|
|
45
|
-
#endif /* ! defined(SIZET2NUM) */
|
|
46
|
-
|
|
47
|
-
#if !defined(NUM2SIZET)
|
|
48
|
-
# if SIZEOF_SIZE_T == SIZEOF_LONG
|
|
49
|
-
# define NUM2SIZET(n) ((size_t)NUM2ULONG(n))
|
|
50
|
-
# else
|
|
51
|
-
# define NUM2SIZET(n) ((size_t)NUM2ULL(n))
|
|
52
|
-
# endif
|
|
53
|
-
#endif /* ! defined(NUM2SIZET) */
|
|
54
|
-
|
|
55
11
|
static inline int str_cstr_eq(VALUE val, const char *ptr, long len)
|
|
56
12
|
{
|
|
57
13
|
return (RSTRING_LEN(val) == len && !memcmp(ptr, RSTRING_PTR(val), len));
|
data/ext/unicorn_http/extconf.rb
CHANGED
|
@@ -1,10 +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")
|
|
8
|
-
have_func("gmtime_r", "time.h")
|
|
4
|
+
have_func("rb_hash_clear", "ruby.h") or abort 'Ruby 2.0+ required'
|
|
9
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))
|
|
10
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.*/
|
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);
|
|
@@ -66,11 +67,11 @@ static VALUE httpdate(VALUE self)
|
|
|
66
67
|
|
|
67
68
|
void init_unicorn_httpdate(void)
|
|
68
69
|
{
|
|
69
|
-
VALUE mod =
|
|
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
|
|