unicorn 0.91.0 → 0.92.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.
- data/{CHANGELOG → .CHANGELOG.old} +0 -0
- data/.document +4 -0
- data/.gitignore +5 -0
- data/.mailmap +26 -0
- data/CONTRIBUTORS +1 -1
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +158 -0
- data/Documentation/unicorn_rails.1.txt +150 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +102 -14
- data/README +3 -2
- data/Rakefile +104 -32
- data/SIGNALS +2 -4
- data/TODO +3 -9
- data/bin/unicorn +5 -2
- data/bin/unicorn_rails +5 -3
- data/ext/unicorn_http/c_util.h +2 -2
- data/ext/unicorn_http/common_field_optimization.h +2 -1
- data/ext/unicorn_http/ext_help.h +29 -4
- data/ext/unicorn_http/extconf.rb +5 -0
- data/ext/unicorn_http/unicorn_http.rl +131 -76
- data/lib/unicorn.rb +6 -2
- data/lib/unicorn/app/exec_cgi.rb +3 -1
- data/lib/unicorn/app/inetd.rb +2 -0
- data/lib/unicorn/app/old_rails.rb +2 -0
- data/lib/unicorn/app/old_rails/static.rb +3 -1
- data/lib/unicorn/cgi_wrapper.rb +3 -1
- data/lib/unicorn/configurator.rb +2 -0
- data/lib/unicorn/const.rb +8 -6
- data/lib/unicorn/http_request.rb +6 -5
- data/lib/unicorn/http_response.rb +4 -2
- data/lib/unicorn/launcher.rb +6 -0
- data/lib/unicorn/socket_helper.rb +5 -5
- data/lib/unicorn/tee_input.rb +2 -0
- data/lib/unicorn/util.rb +2 -0
- data/local.mk.sample +4 -2
- data/setup.rb +1 -0
- data/test/aggregate.rb +2 -0
- data/test/exec/test_exec.rb +157 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +2 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-1.2.3/config/boot.rb +2 -0
- data/test/rails/app-1.2.3/config/environment.rb +2 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +2 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +2 -0
- data/test/rails/app-1.2.3/config/routes.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.0.2/config/boot.rb +2 -0
- data/test/rails/app-2.0.2/config/environment.rb +2 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.0.2/config/routes.rb +2 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.1.2/config/boot.rb +2 -0
- data/test/rails/app-2.1.2/config/environment.rb +2 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.1.2/config/routes.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.2.2/config/boot.rb +2 -0
- data/test/rails/app-2.2.2/config/environment.rb +2 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +2 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +2 -0
- data/test/rails/app-2.2.2/config/routes.rb +2 -0
- data/test/rails/app-2.3.3.1/app/controllers/application_controller.rb +2 -0
- data/test/rails/app-2.3.3.1/app/controllers/foo_controller.rb +2 -0
- data/test/rails/app-2.3.3.1/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.3.3.1/config/boot.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environment.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environments/development.rb +2 -0
- data/test/rails/app-2.3.3.1/config/environments/production.rb +2 -0
- data/test/rails/app-2.3.3.1/config/routes.rb +2 -0
- data/test/rails/test_rails.rb +2 -0
- data/test/test_helper.rb +8 -0
- data/test/unit/test_configurator.rb +2 -0
- data/test/unit/test_http_parser.rb +13 -0
- data/test/unit/test_http_parser_ng.rb +2 -0
- data/test/unit/test_request.rb +2 -0
- data/test/unit/test_response.rb +2 -0
- data/test/unit/test_server.rb +2 -0
- data/test/unit/test_signals.rb +2 -0
- data/test/unit/test_socket_helper.rb +2 -0
- data/test/unit/test_tee_input.rb +2 -1
- data/test/unit/test_upload.rb +2 -0
- data/test/unit/test_util.rb +2 -0
- data/unicorn.gemspec +38 -28
- metadata +38 -42
- data/Manifest +0 -137
data/ext/unicorn_http/extconf.rb
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
1
2
|
require 'mkmf'
|
|
2
3
|
|
|
4
|
+
$CFLAGS += " -fPIC " # needed for Rubinius, MRI already uses it regardless
|
|
5
|
+
|
|
3
6
|
dir_config("unicorn_http")
|
|
4
7
|
|
|
5
8
|
have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
|
|
9
|
+
have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
|
|
6
10
|
have_func("rb_str_set_len", "ruby.h")
|
|
11
|
+
have_func("rb_str_modify", "ruby.h")
|
|
7
12
|
create_makefile("unicorn_http")
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
#define UH_FL_KAVERSION 0x80
|
|
23
23
|
#define UH_FL_HASHEADER 0x100
|
|
24
24
|
|
|
25
|
+
/* both of these flags need to be set for keepalive to be supported */
|
|
25
26
|
#define UH_FL_KEEPALIVE (UH_FL_KAMETHOD | UH_FL_KAVERSION)
|
|
26
27
|
|
|
27
28
|
struct http_parser {
|
|
@@ -37,7 +38,7 @@ struct http_parser {
|
|
|
37
38
|
size_t field_len; /* only used during header processing */
|
|
38
39
|
size_t dest_offset; /* only used during body processing */
|
|
39
40
|
} s;
|
|
40
|
-
VALUE cont;
|
|
41
|
+
VALUE cont; /* Qfalse: unset, Qnil: ignored header, T_STRING: append */
|
|
41
42
|
union {
|
|
42
43
|
off_t content;
|
|
43
44
|
off_t chunk;
|
|
@@ -52,16 +53,54 @@ static void finalize_header(struct http_parser *hp, VALUE req);
|
|
|
52
53
|
#define PTR_TO(F) (buffer + hp->F)
|
|
53
54
|
#define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
|
|
54
55
|
|
|
56
|
+
#define HP_FL_TEST(hp,fl) ((hp)->flags & (UH_FL_##fl))
|
|
57
|
+
#define HP_FL_SET(hp,fl) ((hp)->flags |= (UH_FL_##fl))
|
|
58
|
+
#define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
|
|
59
|
+
#define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
|
|
60
|
+
|
|
61
|
+
/*
|
|
62
|
+
* handles values of the "Connection:" header, keepalive is implied
|
|
63
|
+
* for HTTP/1.1 but needs to be explicitly enabled with HTTP/1.0
|
|
64
|
+
* Additionally, we require GET/HEAD requests to support keepalive.
|
|
65
|
+
*/
|
|
66
|
+
static void hp_keepalive_connection(struct http_parser *hp, VALUE val)
|
|
67
|
+
{
|
|
68
|
+
/* REQUEST_METHOD is always set before any headers */
|
|
69
|
+
if (HP_FL_TEST(hp, KAMETHOD)) {
|
|
70
|
+
if (STR_CSTR_CASE_EQ(val, "keep-alive")) {
|
|
71
|
+
/* basically have HTTP/1.0 masquerade as HTTP/1.1+ */
|
|
72
|
+
HP_FL_SET(hp, KAVERSION);
|
|
73
|
+
} else if (STR_CSTR_CASE_EQ(val, "close")) {
|
|
74
|
+
/*
|
|
75
|
+
* it doesn't matter what HTTP version or request method we have,
|
|
76
|
+
* if a client says "Connection: close", we disable keepalive
|
|
77
|
+
*/
|
|
78
|
+
HP_FL_UNSET(hp, KEEPALIVE);
|
|
79
|
+
} else {
|
|
80
|
+
/*
|
|
81
|
+
* client could've sent anything, ignore it for now. Maybe
|
|
82
|
+
* "HP_FL_UNSET(hp, KEEPALIVE);" just in case?
|
|
83
|
+
* Raising an exception might be too mean...
|
|
84
|
+
*/
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
55
89
|
static void
|
|
56
90
|
request_method(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
|
|
57
91
|
{
|
|
58
92
|
VALUE v;
|
|
59
93
|
|
|
94
|
+
/*
|
|
95
|
+
* we only support keepalive for GET and HEAD requests for now other
|
|
96
|
+
* methods are too rarely seen to be worth optimizing. POST is unsafe
|
|
97
|
+
* since some clients send extra bytes after POST bodies.
|
|
98
|
+
*/
|
|
60
99
|
if (CONST_MEM_EQ("GET", ptr, len)) {
|
|
61
|
-
hp
|
|
100
|
+
HP_FL_SET(hp, KAMETHOD);
|
|
62
101
|
v = g_GET;
|
|
63
102
|
} else if (CONST_MEM_EQ("HEAD", ptr, len)) {
|
|
64
|
-
hp
|
|
103
|
+
HP_FL_SET(hp, KAMETHOD);
|
|
65
104
|
v = g_HEAD;
|
|
66
105
|
} else {
|
|
67
106
|
v = rb_str_new(ptr, len);
|
|
@@ -74,10 +113,11 @@ http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
|
|
|
74
113
|
{
|
|
75
114
|
VALUE v;
|
|
76
115
|
|
|
77
|
-
hp
|
|
116
|
+
HP_FL_SET(hp, HASHEADER);
|
|
78
117
|
|
|
79
118
|
if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
|
|
80
|
-
|
|
119
|
+
/* HTTP/1.1 implies keepalive unless "Connection: close" is set */
|
|
120
|
+
HP_FL_SET(hp, KAVERSION);
|
|
81
121
|
v = g_http_11;
|
|
82
122
|
} else if (CONST_MEM_EQ("HTTP/1.0", ptr, len)) {
|
|
83
123
|
v = g_http_10;
|
|
@@ -88,9 +128,9 @@ http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
|
|
|
88
128
|
rb_hash_aset(req, g_http_version, v);
|
|
89
129
|
}
|
|
90
130
|
|
|
91
|
-
static void
|
|
131
|
+
static inline void hp_invalid_if_trailer(struct http_parser *hp)
|
|
92
132
|
{
|
|
93
|
-
if (
|
|
133
|
+
if (HP_FL_TEST(hp, INTRAILER))
|
|
94
134
|
rb_raise(eHttpParserError, "invalid Trailer");
|
|
95
135
|
}
|
|
96
136
|
|
|
@@ -99,10 +139,13 @@ static void write_cont_value(struct http_parser *hp,
|
|
|
99
139
|
{
|
|
100
140
|
char *vptr;
|
|
101
141
|
|
|
102
|
-
if (
|
|
103
|
-
|
|
142
|
+
if (hp->cont == Qfalse)
|
|
143
|
+
rb_raise(eHttpParserError, "invalid continuation line");
|
|
144
|
+
if (NIL_P(hp->cont))
|
|
145
|
+
return; /* we're ignoring this header (probably Host:) */
|
|
104
146
|
|
|
105
|
-
assert(hp->
|
|
147
|
+
assert(TYPE(hp->cont) == T_STRING && "continuation line is not a string");
|
|
148
|
+
assert(hp->mark > 0 && "impossible continuation line offset");
|
|
106
149
|
|
|
107
150
|
if (LEN(mark, p) == 0)
|
|
108
151
|
return;
|
|
@@ -113,7 +156,7 @@ static void write_cont_value(struct http_parser *hp,
|
|
|
113
156
|
vptr = (char *)PTR_TO(mark);
|
|
114
157
|
|
|
115
158
|
if (RSTRING_LEN(hp->cont) > 0) {
|
|
116
|
-
assert(' ' == *vptr || '\t' == *vptr);
|
|
159
|
+
assert((' ' == *vptr || '\t' == *vptr) && "invalid leading white space");
|
|
117
160
|
*vptr = ' ';
|
|
118
161
|
}
|
|
119
162
|
rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
|
|
@@ -127,37 +170,42 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
|
127
170
|
VALUE e;
|
|
128
171
|
|
|
129
172
|
VALIDATE_MAX_LENGTH(LEN(mark, p), FIELD_VALUE);
|
|
130
|
-
v = STR_NEW(mark, p);
|
|
131
|
-
if (f
|
|
173
|
+
v = LEN(mark, p) == 0 ? rb_str_buf_new(128) : STR_NEW(mark, p);
|
|
174
|
+
if (NIL_P(f)) {
|
|
132
175
|
VALIDATE_MAX_LENGTH(hp->s.field_len, FIELD_NAME);
|
|
133
176
|
f = uncommon_field(PTR_TO(start.field), hp->s.field_len);
|
|
134
177
|
} else if (f == g_http_connection) {
|
|
135
|
-
|
|
136
|
-
if (STR_CSTR_CASE_EQ(v, "keep-alive"))
|
|
137
|
-
hp->flags |= UH_FL_KAVERSION;
|
|
138
|
-
else if (STR_CSTR_CASE_EQ(v, "close"))
|
|
139
|
-
hp->flags &= ~UH_FL_KEEPALIVE;
|
|
140
|
-
}
|
|
178
|
+
hp_keepalive_connection(hp, v);
|
|
141
179
|
} else if (f == g_content_length) {
|
|
142
180
|
hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
|
|
143
181
|
if (hp->len.content < 0)
|
|
144
182
|
rb_raise(eHttpParserError, "invalid Content-Length");
|
|
145
|
-
hp
|
|
146
|
-
|
|
183
|
+
HP_FL_SET(hp, HASBODY);
|
|
184
|
+
hp_invalid_if_trailer(hp);
|
|
147
185
|
} else if (f == g_http_transfer_encoding) {
|
|
148
|
-
if (STR_CSTR_CASE_EQ(v, "chunked"))
|
|
149
|
-
hp
|
|
150
|
-
|
|
186
|
+
if (STR_CSTR_CASE_EQ(v, "chunked")) {
|
|
187
|
+
HP_FL_SET(hp, CHUNKED);
|
|
188
|
+
HP_FL_SET(hp, HASBODY);
|
|
189
|
+
}
|
|
190
|
+
hp_invalid_if_trailer(hp);
|
|
151
191
|
} else if (f == g_http_trailer) {
|
|
152
|
-
hp
|
|
153
|
-
|
|
192
|
+
HP_FL_SET(hp, HASTRAILER);
|
|
193
|
+
hp_invalid_if_trailer(hp);
|
|
194
|
+
} else {
|
|
195
|
+
assert(TYPE(f) == T_STRING && "memoized object is not a string");
|
|
196
|
+
assert_frozen(f);
|
|
154
197
|
}
|
|
155
198
|
|
|
156
199
|
e = rb_hash_aref(req, f);
|
|
157
|
-
if (e
|
|
200
|
+
if (NIL_P(e)) {
|
|
158
201
|
hp->cont = rb_hash_aset(req, f, v);
|
|
159
|
-
} else if (f
|
|
160
|
-
/*
|
|
202
|
+
} else if (f == g_http_host) {
|
|
203
|
+
/*
|
|
204
|
+
* ignored, absolute URLs in REQUEST_URI take precedence over
|
|
205
|
+
* the Host: header (ref: rfc 2616, section 5.2.1)
|
|
206
|
+
*/
|
|
207
|
+
hp->cont = Qnil;
|
|
208
|
+
} else {
|
|
161
209
|
rb_str_buf_cat(e, ",", 1);
|
|
162
210
|
hp->cont = rb_str_buf_append(e, v);
|
|
163
211
|
}
|
|
@@ -232,12 +280,12 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
|
232
280
|
finalize_header(hp, req);
|
|
233
281
|
|
|
234
282
|
cs = http_parser_first_final;
|
|
235
|
-
if (hp
|
|
236
|
-
hp
|
|
237
|
-
if (hp
|
|
283
|
+
if (HP_FL_TEST(hp, HASBODY)) {
|
|
284
|
+
HP_FL_SET(hp, INBODY);
|
|
285
|
+
if (HP_FL_TEST(hp, CHUNKED))
|
|
238
286
|
cs = http_parser_en_ChunkedBody;
|
|
239
287
|
} else {
|
|
240
|
-
assert(!(hp
|
|
288
|
+
assert(!HP_FL_TEST(hp, CHUNKED) && "chunked encoding without body!");
|
|
241
289
|
}
|
|
242
290
|
/*
|
|
243
291
|
* go back to Ruby so we can call the Rack application, we'll reenter
|
|
@@ -252,13 +300,14 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
|
252
300
|
}
|
|
253
301
|
|
|
254
302
|
action end_chunked_body {
|
|
255
|
-
if (hp
|
|
256
|
-
hp
|
|
303
|
+
if (HP_FL_TEST(hp, HASTRAILER)) {
|
|
304
|
+
HP_FL_SET(hp, INTRAILER);
|
|
257
305
|
cs = http_parser_en_Trailers;
|
|
258
306
|
} else {
|
|
259
307
|
cs = http_parser_first_final;
|
|
260
308
|
}
|
|
261
309
|
++p;
|
|
310
|
+
assert(p <= pe && "buffer overflow after chunked body");
|
|
262
311
|
goto post_exec;
|
|
263
312
|
}
|
|
264
313
|
|
|
@@ -269,9 +318,9 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
|
269
318
|
hp->s.dest_offset += nr;
|
|
270
319
|
hp->len.chunk -= nr;
|
|
271
320
|
p += nr;
|
|
272
|
-
assert(hp->len.chunk >= 0);
|
|
321
|
+
assert(hp->len.chunk >= 0 && "negative chunk length");
|
|
273
322
|
if (hp->len.chunk > REMAINING) {
|
|
274
|
-
hp
|
|
323
|
+
HP_FL_SET(hp, INCHUNK);
|
|
275
324
|
goto post_exec;
|
|
276
325
|
} else {
|
|
277
326
|
fhold;
|
|
@@ -289,6 +338,7 @@ static void http_parser_init(struct http_parser *hp)
|
|
|
289
338
|
{
|
|
290
339
|
int cs = 0;
|
|
291
340
|
memset(hp, 0, sizeof(struct http_parser));
|
|
341
|
+
hp->cont = Qfalse; /* zero on MRI, should be optimized away by above */
|
|
292
342
|
%% write init;
|
|
293
343
|
hp->cs = cs;
|
|
294
344
|
}
|
|
@@ -311,8 +361,8 @@ static void http_parser_execute(struct http_parser *hp,
|
|
|
311
361
|
|
|
312
362
|
assert(pe - p == len - off && "pointers aren't same distance");
|
|
313
363
|
|
|
314
|
-
if (hp
|
|
315
|
-
hp
|
|
364
|
+
if (HP_FL_TEST(hp, INCHUNK)) {
|
|
365
|
+
HP_FL_UNSET(hp, INCHUNK);
|
|
316
366
|
goto skip_chunk_data_hack;
|
|
317
367
|
}
|
|
318
368
|
%% write exec;
|
|
@@ -330,7 +380,7 @@ static struct http_parser *data_get(VALUE self)
|
|
|
330
380
|
struct http_parser *hp;
|
|
331
381
|
|
|
332
382
|
Data_Get_Struct(self, struct http_parser, hp);
|
|
333
|
-
assert(hp);
|
|
383
|
+
assert(hp && "failed to extract http_parser struct");
|
|
334
384
|
return hp;
|
|
335
385
|
}
|
|
336
386
|
|
|
@@ -341,20 +391,22 @@ static void finalize_header(struct http_parser *hp, VALUE req)
|
|
|
341
391
|
VALUE server_port = g_port_80;
|
|
342
392
|
|
|
343
393
|
/* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
|
|
344
|
-
if (temp
|
|
394
|
+
if (NIL_P(temp)) {
|
|
345
395
|
temp = rb_hash_aref(req, g_http_x_forwarded_proto);
|
|
346
|
-
if (temp
|
|
396
|
+
if (!NIL_P(temp) && STR_CSTR_EQ(temp, "https"))
|
|
347
397
|
server_port = g_port_443;
|
|
348
398
|
else
|
|
349
399
|
temp = g_http;
|
|
350
400
|
rb_hash_aset(req, g_rack_url_scheme, temp);
|
|
351
401
|
} else if (STR_CSTR_EQ(temp, "https")) {
|
|
352
402
|
server_port = g_port_443;
|
|
403
|
+
} else {
|
|
404
|
+
assert(server_port == g_port_80 && "server_port not set");
|
|
353
405
|
}
|
|
354
406
|
|
|
355
407
|
/* parse and set the SERVER_NAME and SERVER_PORT variables */
|
|
356
408
|
temp = rb_hash_aref(req, g_http_host);
|
|
357
|
-
if (temp
|
|
409
|
+
if (!NIL_P(temp)) {
|
|
358
410
|
char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
|
359
411
|
if (colon) {
|
|
360
412
|
long port_start = colon - RSTRING_PTR(temp) + 1;
|
|
@@ -368,11 +420,11 @@ static void finalize_header(struct http_parser *hp, VALUE req)
|
|
|
368
420
|
}
|
|
369
421
|
rb_hash_aset(req, g_server_name, server_name);
|
|
370
422
|
rb_hash_aset(req, g_server_port, server_port);
|
|
371
|
-
if (!(hp
|
|
423
|
+
if (!HP_FL_TEST(hp, HASHEADER))
|
|
372
424
|
rb_hash_aset(req, g_server_protocol, g_http_09);
|
|
373
425
|
|
|
374
426
|
/* rack requires QUERY_STRING */
|
|
375
|
-
if (rb_hash_aref(req, g_query_string)
|
|
427
|
+
if (NIL_P(rb_hash_aref(req, g_query_string)))
|
|
376
428
|
rb_hash_aset(req, g_query_string, rb_str_new(NULL, 0));
|
|
377
429
|
}
|
|
378
430
|
|
|
@@ -380,8 +432,7 @@ static void hp_mark(void *ptr)
|
|
|
380
432
|
{
|
|
381
433
|
struct http_parser *hp = ptr;
|
|
382
434
|
|
|
383
|
-
|
|
384
|
-
rb_gc_mark(hp->cont);
|
|
435
|
+
rb_gc_mark(hp->cont);
|
|
385
436
|
}
|
|
386
437
|
|
|
387
438
|
static VALUE HttpParser_alloc(VALUE klass)
|
|
@@ -427,7 +478,7 @@ static void advance_str(VALUE str, off_t nr)
|
|
|
427
478
|
|
|
428
479
|
rb_str_modify(str);
|
|
429
480
|
|
|
430
|
-
assert(nr <= len);
|
|
481
|
+
assert(nr <= len && "trying to advance past end of buffer");
|
|
431
482
|
len -= nr;
|
|
432
483
|
if (len > 0) /* unlikely, len is usually 0 */
|
|
433
484
|
memmove(RSTRING_PTR(str), RSTRING_PTR(str) + nr, len);
|
|
@@ -450,7 +501,7 @@ static VALUE HttpParser_content_length(VALUE self)
|
|
|
450
501
|
{
|
|
451
502
|
struct http_parser *hp = data_get(self);
|
|
452
503
|
|
|
453
|
-
return (hp
|
|
504
|
+
return HP_FL_TEST(hp, CHUNKED) ? Qnil : OFFT2NUM(hp->len.content);
|
|
454
505
|
}
|
|
455
506
|
|
|
456
507
|
/**
|
|
@@ -477,6 +528,8 @@ static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
|
|
|
477
528
|
{
|
|
478
529
|
struct http_parser *hp = data_get(self);
|
|
479
530
|
|
|
531
|
+
rb_str_update(data);
|
|
532
|
+
|
|
480
533
|
http_parser_execute(hp, req, RSTRING_PTR(data), RSTRING_LEN(data));
|
|
481
534
|
VALIDATE_MAX_LENGTH(hp->start.offset, HEADER);
|
|
482
535
|
|
|
@@ -496,8 +549,7 @@ static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
|
|
|
496
549
|
|
|
497
550
|
static int chunked_eof(struct http_parser *hp)
|
|
498
551
|
{
|
|
499
|
-
return ((hp->cs == http_parser_first_final) ||
|
|
500
|
-
(hp->flags & UH_FL_INTRAILER));
|
|
552
|
+
return ((hp->cs == http_parser_first_final) || HP_FL_TEST(hp, INTRAILER));
|
|
501
553
|
}
|
|
502
554
|
|
|
503
555
|
/**
|
|
@@ -511,7 +563,7 @@ static VALUE HttpParser_body_eof(VALUE self)
|
|
|
511
563
|
{
|
|
512
564
|
struct http_parser *hp = data_get(self);
|
|
513
565
|
|
|
514
|
-
if (hp
|
|
566
|
+
if (HP_FL_TEST(hp, CHUNKED))
|
|
515
567
|
return chunked_eof(hp) ? Qtrue : Qfalse;
|
|
516
568
|
|
|
517
569
|
return hp->len.content == 0 ? Qtrue : Qfalse;
|
|
@@ -532,7 +584,7 @@ static VALUE HttpParser_keepalive(VALUE self)
|
|
|
532
584
|
{
|
|
533
585
|
struct http_parser *hp = data_get(self);
|
|
534
586
|
|
|
535
|
-
return (hp
|
|
587
|
+
return HP_FL_ALL(hp, KEEPALIVE) ? Qtrue : Qfalse;
|
|
536
588
|
}
|
|
537
589
|
|
|
538
590
|
/**
|
|
@@ -547,7 +599,7 @@ static VALUE HttpParser_has_headers(VALUE self)
|
|
|
547
599
|
{
|
|
548
600
|
struct http_parser *hp = data_get(self);
|
|
549
601
|
|
|
550
|
-
return (hp
|
|
602
|
+
return HP_FL_TEST(hp, HASHEADER) ? Qtrue : Qfalse;
|
|
551
603
|
}
|
|
552
604
|
|
|
553
605
|
/**
|
|
@@ -567,34 +619,38 @@ static VALUE HttpParser_has_headers(VALUE self)
|
|
|
567
619
|
static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
|
|
568
620
|
{
|
|
569
621
|
struct http_parser *hp = data_get(self);
|
|
570
|
-
char *dptr
|
|
571
|
-
long dlen
|
|
622
|
+
char *dptr;
|
|
623
|
+
long dlen;
|
|
624
|
+
|
|
625
|
+
rb_str_update(data);
|
|
626
|
+
dptr = RSTRING_PTR(data);
|
|
627
|
+
dlen = RSTRING_LEN(data);
|
|
572
628
|
|
|
573
629
|
StringValue(buf);
|
|
574
630
|
rb_str_resize(buf, dlen); /* we can never copy more than dlen bytes */
|
|
575
631
|
OBJ_TAINT(buf); /* keep weirdo $SAFE users happy */
|
|
576
632
|
|
|
577
|
-
if (hp
|
|
578
|
-
if (chunked_eof(hp))
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
633
|
+
if (HP_FL_TEST(hp, CHUNKED)) {
|
|
634
|
+
if (!chunked_eof(hp)) {
|
|
635
|
+
hp->s.dest_offset = 0;
|
|
636
|
+
http_parser_execute(hp, buf, dptr, dlen);
|
|
637
|
+
if (hp->cs == http_parser_error)
|
|
638
|
+
rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
|
|
639
|
+
|
|
640
|
+
assert(hp->s.dest_offset <= hp->start.offset &&
|
|
641
|
+
"destination buffer overflow");
|
|
642
|
+
advance_str(data, hp->start.offset);
|
|
643
|
+
rb_str_set_len(buf, hp->s.dest_offset);
|
|
644
|
+
|
|
645
|
+
if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
|
|
646
|
+
assert(hp->len.chunk == 0 && "chunk at EOF but more to parse");
|
|
647
|
+
} else {
|
|
648
|
+
data = Qnil;
|
|
649
|
+
}
|
|
594
650
|
}
|
|
595
651
|
} else {
|
|
596
652
|
/* no need to enter the Ragel machine for unchunked transfers */
|
|
597
|
-
assert(hp->len.content >= 0);
|
|
653
|
+
assert(hp->len.content >= 0 && "negative Content-Length");
|
|
598
654
|
if (hp->len.content > 0) {
|
|
599
655
|
long nr = MIN(dlen, hp->len.content);
|
|
600
656
|
|
|
@@ -607,14 +663,13 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
|
|
|
607
663
|
data = Qnil;
|
|
608
664
|
}
|
|
609
665
|
}
|
|
610
|
-
end_of_body:
|
|
611
666
|
hp->start.offset = 0; /* for trailer parsing */
|
|
612
667
|
return data;
|
|
613
668
|
}
|
|
614
669
|
|
|
615
670
|
#define SET_GLOBAL(var,str) do { \
|
|
616
671
|
var = find_common_field(str, sizeof(str) - 1); \
|
|
617
|
-
assert(var
|
|
672
|
+
assert(!NIL_P(var) && "missed global field"); \
|
|
618
673
|
} while (0)
|
|
619
674
|
|
|
620
675
|
void Init_unicorn_http(void)
|
data/lib/unicorn.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# -*- encoding: binary -*-
|
|
2
|
+
|
|
1
3
|
require 'fcntl'
|
|
2
4
|
require 'unicorn/socket_helper'
|
|
3
5
|
autoload :Rack, 'rack'
|
|
@@ -108,6 +110,8 @@ module Unicorn
|
|
|
108
110
|
config_listeners -= listener_names
|
|
109
111
|
if config_listeners.empty? && LISTENERS.empty?
|
|
110
112
|
config_listeners << Unicorn::Const::DEFAULT_LISTEN
|
|
113
|
+
init_listeners << Unicorn::Const::DEFAULT_LISTEN
|
|
114
|
+
START_CTX[:argv] << "-l#{Unicorn::Const::DEFAULT_LISTEN}"
|
|
111
115
|
end
|
|
112
116
|
config_listeners.each { |addr| listen(addr) }
|
|
113
117
|
raise ArgumentError, "no listeners" if LISTENERS.empty?
|
|
@@ -283,7 +287,7 @@ module Unicorn
|
|
|
283
287
|
|
|
284
288
|
# list of signals we care about and trap in master.
|
|
285
289
|
QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP,
|
|
286
|
-
:TTIN, :TTOU ]
|
|
290
|
+
:TTIN, :TTOU ]
|
|
287
291
|
|
|
288
292
|
# defer a signal for later processing in #join (master process)
|
|
289
293
|
def trap_deferred(signal)
|
|
@@ -535,7 +539,7 @@ module Unicorn
|
|
|
535
539
|
|
|
536
540
|
# make the following bet: if we accepted clients this round,
|
|
537
541
|
# we're probably reasonably busy, so avoid calling select()
|
|
538
|
-
# and do a speculative accept_nonblock on
|
|
542
|
+
# and do a speculative accept_nonblock on ready listeners
|
|
539
543
|
# before we sleep again in select().
|
|
540
544
|
redo unless nr == 0 # (nr < 0) => reopen logs
|
|
541
545
|
|