unicorn 2.0.0pre1 → 2.0.0pre2
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/GIT-VERSION-GEN +1 -1
- data/Rakefile +1 -1
- data/bin/unicorn +2 -2
- data/ext/unicorn_http/ext_help.h +0 -4
- data/ext/unicorn_http/unicorn_http.rl +103 -61
- data/lib/unicorn.rb +1 -1
- data/lib/unicorn/const.rb +2 -2
- data/lib/unicorn/http_request.rb +14 -23
- data/lib/unicorn/http_server.rb +3 -2
- data/lib/unicorn/tee_input.rb +52 -52
- data/test/unit/test_http_parser_ng.rb +2 -0
- data/test/unit/test_tee_input.rb +4 -5
- metadata +4 -4
data/GIT-VERSION-GEN
CHANGED
data/Rakefile
CHANGED
@@ -15,7 +15,7 @@ def tags
|
|
15
15
|
timefmt = '%Y-%m-%dT%H:%M:%SZ'
|
16
16
|
@tags ||= `git tag -l`.split(/\n/).map do |tag|
|
17
17
|
next if tag == "v0.0.0"
|
18
|
-
if %r{\Av[\d\.]
|
18
|
+
if %r{\Av[\d\.]+} =~ tag
|
19
19
|
header, subject, body = `git cat-file tag #{tag}`.split(/\n\n/, 3)
|
20
20
|
header = header.split(/\n/)
|
21
21
|
tagger = header.grep(/\Atagger /).first
|
data/bin/unicorn
CHANGED
@@ -53,8 +53,8 @@ opts = OptionParser.new("", 24, ' ') do |opts|
|
|
53
53
|
rackup_opts[:set_listener] = true
|
54
54
|
end
|
55
55
|
|
56
|
-
opts.on("-E", "--env
|
57
|
-
"use
|
56
|
+
opts.on("-E", "--env RACK_ENV",
|
57
|
+
"use RACK_ENV for defaults (default: development)") do |e|
|
58
58
|
ENV["RACK_ENV"] = e
|
59
59
|
end
|
60
60
|
|
data/ext/unicorn_http/ext_help.h
CHANGED
@@ -8,10 +8,6 @@
|
|
8
8
|
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
9
9
|
#endif /* !defined(RSTRING_LEN) */
|
10
10
|
|
11
|
-
#ifndef RUBINIUS
|
12
|
-
# define rb_str_update(x) do {} while (0)
|
13
|
-
#endif /* !RUBINIUS */
|
14
|
-
|
15
11
|
#ifndef HAVE_RB_STR_SET_LEN
|
16
12
|
# ifdef RUBINIUS
|
17
13
|
# error we should never get here with current Rubinius (1.x)
|
@@ -39,6 +39,8 @@ struct http_parser {
|
|
39
39
|
size_t field_len; /* only used during header processing */
|
40
40
|
size_t dest_offset; /* only used during body processing */
|
41
41
|
} s;
|
42
|
+
VALUE buf;
|
43
|
+
VALUE env;
|
42
44
|
VALUE cont; /* Qfalse: unset, Qnil: ignored header, T_STRING: append */
|
43
45
|
union {
|
44
46
|
off_t content;
|
@@ -46,7 +48,9 @@ struct http_parser {
|
|
46
48
|
} len;
|
47
49
|
};
|
48
50
|
|
49
|
-
static
|
51
|
+
static ID id_clear;
|
52
|
+
|
53
|
+
static void finalize_header(struct http_parser *hp);
|
50
54
|
|
51
55
|
static void parser_error(const char *msg)
|
52
56
|
{
|
@@ -97,7 +101,7 @@ static void hp_keepalive_connection(struct http_parser *hp, VALUE val)
|
|
97
101
|
}
|
98
102
|
|
99
103
|
static void
|
100
|
-
request_method(struct http_parser *hp,
|
104
|
+
request_method(struct http_parser *hp, const char *ptr, size_t len)
|
101
105
|
{
|
102
106
|
VALUE v;
|
103
107
|
|
@@ -115,11 +119,11 @@ request_method(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
|
|
115
119
|
} else {
|
116
120
|
v = rb_str_new(ptr, len);
|
117
121
|
}
|
118
|
-
rb_hash_aset(
|
122
|
+
rb_hash_aset(hp->env, g_request_method, v);
|
119
123
|
}
|
120
124
|
|
121
125
|
static void
|
122
|
-
http_version(struct http_parser *hp,
|
126
|
+
http_version(struct http_parser *hp, const char *ptr, size_t len)
|
123
127
|
{
|
124
128
|
VALUE v;
|
125
129
|
|
@@ -134,8 +138,8 @@ http_version(struct http_parser *hp, VALUE req, const char *ptr, size_t len)
|
|
134
138
|
} else {
|
135
139
|
v = rb_str_new(ptr, len);
|
136
140
|
}
|
137
|
-
rb_hash_aset(
|
138
|
-
rb_hash_aset(
|
141
|
+
rb_hash_aset(hp->env, g_server_protocol, v);
|
142
|
+
rb_hash_aset(hp->env, g_http_version, v);
|
139
143
|
}
|
140
144
|
|
141
145
|
static inline void hp_invalid_if_trailer(struct http_parser *hp)
|
@@ -172,7 +176,7 @@ static void write_cont_value(struct http_parser *hp,
|
|
172
176
|
rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
|
173
177
|
}
|
174
178
|
|
175
|
-
static void write_value(
|
179
|
+
static void write_value(struct http_parser *hp,
|
176
180
|
const char *buffer, const char *p)
|
177
181
|
{
|
178
182
|
VALUE f = find_common_field(PTR_TO(start.field), hp->s.field_len);
|
@@ -218,9 +222,9 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
218
222
|
assert_frozen(f);
|
219
223
|
}
|
220
224
|
|
221
|
-
e = rb_hash_aref(
|
225
|
+
e = rb_hash_aref(hp->env, f);
|
222
226
|
if (NIL_P(e)) {
|
223
|
-
hp->cont = rb_hash_aset(
|
227
|
+
hp->cont = rb_hash_aset(hp->env, f, v);
|
224
228
|
} else if (f == g_http_host) {
|
225
229
|
/*
|
226
230
|
* ignored, absolute URLs in REQUEST_URI take precedence over
|
@@ -245,51 +249,47 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
245
249
|
action downcase_char { downcase_char(deconst(fpc)); }
|
246
250
|
action write_field { hp->s.field_len = LEN(start.field, fpc); }
|
247
251
|
action start_value { MARK(mark, fpc); }
|
248
|
-
action write_value { write_value(
|
252
|
+
action write_value { write_value(hp, buffer, fpc); }
|
249
253
|
action write_cont_value { write_cont_value(hp, buffer, fpc); }
|
250
|
-
action request_method {
|
251
|
-
request_method(hp, req, PTR_TO(mark), LEN(mark, fpc));
|
252
|
-
}
|
254
|
+
action request_method { request_method(hp, PTR_TO(mark), LEN(mark, fpc)); }
|
253
255
|
action scheme {
|
254
|
-
rb_hash_aset(
|
255
|
-
}
|
256
|
-
action host {
|
257
|
-
rb_hash_aset(req, g_http_host, STR_NEW(mark, fpc));
|
256
|
+
rb_hash_aset(hp->env, g_rack_url_scheme, STR_NEW(mark, fpc));
|
258
257
|
}
|
258
|
+
action host { rb_hash_aset(hp->env, g_http_host, STR_NEW(mark, fpc)); }
|
259
259
|
action request_uri {
|
260
260
|
VALUE str;
|
261
261
|
|
262
262
|
VALIDATE_MAX_LENGTH(LEN(mark, fpc), REQUEST_URI);
|
263
|
-
str = rb_hash_aset(
|
263
|
+
str = rb_hash_aset(hp->env, g_request_uri, STR_NEW(mark, fpc));
|
264
264
|
/*
|
265
265
|
* "OPTIONS * HTTP/1.1\r\n" is a valid request, but we can't have '*'
|
266
266
|
* in REQUEST_PATH or PATH_INFO or else Rack::Lint will complain
|
267
267
|
*/
|
268
268
|
if (STR_CSTR_EQ(str, "*")) {
|
269
269
|
str = rb_str_new(NULL, 0);
|
270
|
-
rb_hash_aset(
|
271
|
-
rb_hash_aset(
|
270
|
+
rb_hash_aset(hp->env, g_path_info, str);
|
271
|
+
rb_hash_aset(hp->env, g_request_path, str);
|
272
272
|
}
|
273
273
|
}
|
274
274
|
action fragment {
|
275
275
|
VALIDATE_MAX_LENGTH(LEN(mark, fpc), FRAGMENT);
|
276
|
-
rb_hash_aset(
|
276
|
+
rb_hash_aset(hp->env, g_fragment, STR_NEW(mark, fpc));
|
277
277
|
}
|
278
278
|
action start_query {MARK(start.query, fpc); }
|
279
279
|
action query_string {
|
280
280
|
VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
|
281
|
-
rb_hash_aset(
|
281
|
+
rb_hash_aset(hp->env, g_query_string, STR_NEW(start.query, fpc));
|
282
282
|
}
|
283
|
-
action http_version { http_version(hp,
|
283
|
+
action http_version { http_version(hp, PTR_TO(mark), LEN(mark, fpc)); }
|
284
284
|
action request_path {
|
285
285
|
VALUE val;
|
286
286
|
|
287
287
|
VALIDATE_MAX_LENGTH(LEN(mark, fpc), REQUEST_PATH);
|
288
|
-
val = rb_hash_aset(
|
288
|
+
val = rb_hash_aset(hp->env, g_request_path, STR_NEW(mark, fpc));
|
289
289
|
|
290
290
|
/* rack says PATH_INFO must start with "/" or be empty */
|
291
291
|
if (!STR_CSTR_EQ(val, "*"))
|
292
|
-
rb_hash_aset(
|
292
|
+
rb_hash_aset(hp->env, g_path_info, val);
|
293
293
|
}
|
294
294
|
action add_to_chunk_size {
|
295
295
|
hp->len.chunk = step_incr(hp->len.chunk, fc, 16);
|
@@ -297,7 +297,7 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
297
297
|
parser_error("invalid chunk size");
|
298
298
|
}
|
299
299
|
action header_done {
|
300
|
-
finalize_header(hp
|
300
|
+
finalize_header(hp);
|
301
301
|
|
302
302
|
cs = http_parser_first_final;
|
303
303
|
if (HP_FL_TEST(hp, HASBODY)) {
|
@@ -330,7 +330,7 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
330
330
|
action skip_chunk_data {
|
331
331
|
skip_chunk_data_hack: {
|
332
332
|
size_t nr = MIN((size_t)hp->len.chunk, REMAINING);
|
333
|
-
memcpy(RSTRING_PTR(
|
333
|
+
memcpy(RSTRING_PTR(hp->cont) + hp->s.dest_offset, fpc, nr);
|
334
334
|
hp->s.dest_offset += nr;
|
335
335
|
hp->len.chunk -= nr;
|
336
336
|
p += nr;
|
@@ -353,15 +353,20 @@ static void write_value(VALUE req, struct http_parser *hp,
|
|
353
353
|
static void http_parser_init(struct http_parser *hp)
|
354
354
|
{
|
355
355
|
int cs = 0;
|
356
|
-
|
356
|
+
hp->flags = 0;
|
357
|
+
hp->mark = 0;
|
358
|
+
hp->offset = 0;
|
359
|
+
hp->start.field = 0;
|
360
|
+
hp->s.field_len = 0;
|
361
|
+
hp->len.content = 0;
|
357
362
|
hp->cont = Qfalse; /* zero on MRI, should be optimized away by above */
|
358
363
|
%% write init;
|
359
364
|
hp->cs = cs;
|
360
365
|
}
|
361
366
|
|
362
367
|
/** exec **/
|
363
|
-
static void
|
364
|
-
|
368
|
+
static void
|
369
|
+
http_parser_execute(struct http_parser *hp, char *buffer, size_t len)
|
365
370
|
{
|
366
371
|
const char *p, *pe;
|
367
372
|
int cs = hp->cs;
|
@@ -401,20 +406,20 @@ static struct http_parser *data_get(VALUE self)
|
|
401
406
|
return hp;
|
402
407
|
}
|
403
408
|
|
404
|
-
static void finalize_header(struct http_parser *hp
|
409
|
+
static void finalize_header(struct http_parser *hp)
|
405
410
|
{
|
406
|
-
VALUE temp = rb_hash_aref(
|
411
|
+
VALUE temp = rb_hash_aref(hp->env, g_rack_url_scheme);
|
407
412
|
VALUE server_name = g_localhost;
|
408
413
|
VALUE server_port = g_port_80;
|
409
414
|
|
410
415
|
/* set rack.url_scheme to "https" or "http", no others are allowed by Rack */
|
411
416
|
if (NIL_P(temp)) {
|
412
|
-
temp = rb_hash_aref(
|
417
|
+
temp = rb_hash_aref(hp->env, g_http_x_forwarded_proto);
|
413
418
|
if (!NIL_P(temp) && STR_CSTR_EQ(temp, "https"))
|
414
419
|
server_port = g_port_443;
|
415
420
|
else
|
416
421
|
temp = g_http;
|
417
|
-
rb_hash_aset(
|
422
|
+
rb_hash_aset(hp->env, g_rack_url_scheme, temp);
|
418
423
|
} else if (STR_CSTR_EQ(temp, "https")) {
|
419
424
|
server_port = g_port_443;
|
420
425
|
} else {
|
@@ -422,7 +427,7 @@ static void finalize_header(struct http_parser *hp, VALUE req)
|
|
422
427
|
}
|
423
428
|
|
424
429
|
/* parse and set the SERVER_NAME and SERVER_PORT variables */
|
425
|
-
temp = rb_hash_aref(
|
430
|
+
temp = rb_hash_aref(hp->env, g_http_host);
|
426
431
|
if (!NIL_P(temp)) {
|
427
432
|
char *colon = memchr(RSTRING_PTR(temp), ':', RSTRING_LEN(temp));
|
428
433
|
if (colon) {
|
@@ -435,20 +440,22 @@ static void finalize_header(struct http_parser *hp, VALUE req)
|
|
435
440
|
server_name = temp;
|
436
441
|
}
|
437
442
|
}
|
438
|
-
rb_hash_aset(
|
439
|
-
rb_hash_aset(
|
443
|
+
rb_hash_aset(hp->env, g_server_name, server_name);
|
444
|
+
rb_hash_aset(hp->env, g_server_port, server_port);
|
440
445
|
if (!HP_FL_TEST(hp, HASHEADER))
|
441
|
-
rb_hash_aset(
|
446
|
+
rb_hash_aset(hp->env, g_server_protocol, g_http_09);
|
442
447
|
|
443
448
|
/* rack requires QUERY_STRING */
|
444
|
-
if (NIL_P(rb_hash_aref(
|
445
|
-
rb_hash_aset(
|
449
|
+
if (NIL_P(rb_hash_aref(hp->env, g_query_string)))
|
450
|
+
rb_hash_aset(hp->env, g_query_string, rb_str_new(NULL, 0));
|
446
451
|
}
|
447
452
|
|
448
453
|
static void hp_mark(void *ptr)
|
449
454
|
{
|
450
455
|
struct http_parser *hp = ptr;
|
451
456
|
|
457
|
+
rb_gc_mark(hp->buf);
|
458
|
+
rb_gc_mark(hp->env);
|
452
459
|
rb_gc_mark(hp->cont);
|
453
460
|
}
|
454
461
|
|
@@ -467,7 +474,11 @@ static VALUE HttpParser_alloc(VALUE klass)
|
|
467
474
|
*/
|
468
475
|
static VALUE HttpParser_init(VALUE self)
|
469
476
|
{
|
470
|
-
|
477
|
+
struct http_parser *hp = data_get(self);
|
478
|
+
|
479
|
+
http_parser_init(hp);
|
480
|
+
hp->buf = rb_str_new(NULL, 0);
|
481
|
+
hp->env = rb_hash_new();
|
471
482
|
|
472
483
|
return self;
|
473
484
|
}
|
@@ -481,7 +492,11 @@ static VALUE HttpParser_init(VALUE self)
|
|
481
492
|
*/
|
482
493
|
static VALUE HttpParser_reset(VALUE self)
|
483
494
|
{
|
484
|
-
|
495
|
+
struct http_parser *hp = data_get(self);
|
496
|
+
|
497
|
+
http_parser_init(hp);
|
498
|
+
rb_funcall(hp->env, id_clear, 0);
|
499
|
+
rb_str_set_len(hp->buf, 0);
|
485
500
|
|
486
501
|
return Qnil;
|
487
502
|
}
|
@@ -522,32 +537,23 @@ static VALUE HttpParser_content_length(VALUE self)
|
|
522
537
|
}
|
523
538
|
|
524
539
|
/**
|
525
|
-
* Document-method:
|
526
|
-
* call-seq:
|
527
|
-
* parser.trailers(req, data) => req or nil
|
528
|
-
*
|
529
|
-
* This is an alias for HttpParser#headers
|
530
|
-
*/
|
531
|
-
|
532
|
-
/**
|
533
|
-
* Document-method: headers
|
540
|
+
* Document-method: parse
|
534
541
|
* call-seq:
|
535
|
-
* parser.
|
542
|
+
* parser.parse => env or nil
|
536
543
|
*
|
537
544
|
* Takes a Hash and a String of data, parses the String of data filling
|
538
545
|
* in the Hash returning the Hash if parsing is finished, nil otherwise
|
539
|
-
* When returning the
|
546
|
+
* When returning the env Hash, it may modify data to point to where
|
540
547
|
* body processing should begin.
|
541
548
|
*
|
542
549
|
* Raises HttpParserError if there are parsing errors.
|
543
550
|
*/
|
544
|
-
static VALUE
|
551
|
+
static VALUE HttpParser_parse(VALUE self)
|
545
552
|
{
|
546
553
|
struct http_parser *hp = data_get(self);
|
554
|
+
VALUE data = hp->buf;
|
547
555
|
|
548
|
-
|
549
|
-
|
550
|
-
http_parser_execute(hp, req, RSTRING_PTR(data), RSTRING_LEN(data));
|
556
|
+
http_parser_execute(hp, RSTRING_PTR(data), RSTRING_LEN(data));
|
551
557
|
VALIDATE_MAX_LENGTH(hp->offset, HEADER);
|
552
558
|
|
553
559
|
if (hp->cs == http_parser_first_final ||
|
@@ -555,7 +561,7 @@ static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
|
|
555
561
|
advance_str(data, hp->offset + 1);
|
556
562
|
hp->offset = 0;
|
557
563
|
|
558
|
-
return
|
564
|
+
return hp->env;
|
559
565
|
}
|
560
566
|
|
561
567
|
if (hp->cs == http_parser_error)
|
@@ -564,6 +570,27 @@ static VALUE HttpParser_headers(VALUE self, VALUE req, VALUE data)
|
|
564
570
|
return Qnil;
|
565
571
|
}
|
566
572
|
|
573
|
+
/**
|
574
|
+
* Document-method: trailers
|
575
|
+
* call-seq:
|
576
|
+
* parser.trailers(req, data) => req or nil
|
577
|
+
*
|
578
|
+
* This is an alias for HttpParser#headers
|
579
|
+
*/
|
580
|
+
|
581
|
+
/**
|
582
|
+
* Document-method: headers
|
583
|
+
*/
|
584
|
+
static VALUE HttpParser_headers(VALUE self, VALUE env, VALUE buf)
|
585
|
+
{
|
586
|
+
struct http_parser *hp = data_get(self);
|
587
|
+
|
588
|
+
hp->env = env;
|
589
|
+
hp->buf = buf;
|
590
|
+
|
591
|
+
return HttpParser_parse(self);
|
592
|
+
}
|
593
|
+
|
567
594
|
static int chunked_eof(struct http_parser *hp)
|
568
595
|
{
|
569
596
|
return ((hp->cs == http_parser_first_final) || HP_FL_TEST(hp, INTRAILER));
|
@@ -619,6 +646,16 @@ static VALUE HttpParser_has_headers(VALUE self)
|
|
619
646
|
return HP_FL_TEST(hp, HASHEADER) ? Qtrue : Qfalse;
|
620
647
|
}
|
621
648
|
|
649
|
+
static VALUE HttpParser_buf(VALUE self)
|
650
|
+
{
|
651
|
+
return data_get(self)->buf;
|
652
|
+
}
|
653
|
+
|
654
|
+
static VALUE HttpParser_env(VALUE self)
|
655
|
+
{
|
656
|
+
return data_get(self)->env;
|
657
|
+
}
|
658
|
+
|
622
659
|
/**
|
623
660
|
* call-seq:
|
624
661
|
* parser.filter_body(buf, data) => nil/data
|
@@ -639,7 +676,6 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
|
|
639
676
|
char *dptr;
|
640
677
|
long dlen;
|
641
678
|
|
642
|
-
rb_str_update(data);
|
643
679
|
dptr = RSTRING_PTR(data);
|
644
680
|
dlen = RSTRING_LEN(data);
|
645
681
|
|
@@ -650,7 +686,9 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
|
|
650
686
|
if (HP_FL_TEST(hp, CHUNKED)) {
|
651
687
|
if (!chunked_eof(hp)) {
|
652
688
|
hp->s.dest_offset = 0;
|
653
|
-
|
689
|
+
hp->cont = buf;
|
690
|
+
hp->buf = data;
|
691
|
+
http_parser_execute(hp, dptr, dlen);
|
654
692
|
if (hp->cs == http_parser_error)
|
655
693
|
parser_error("Invalid HTTP format, parsing fails.");
|
656
694
|
|
@@ -702,13 +740,16 @@ void Init_unicorn_http(void)
|
|
702
740
|
rb_define_alloc_func(cHttpParser, HttpParser_alloc);
|
703
741
|
rb_define_method(cHttpParser, "initialize", HttpParser_init,0);
|
704
742
|
rb_define_method(cHttpParser, "reset", HttpParser_reset,0);
|
743
|
+
rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
|
705
744
|
rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
|
706
|
-
rb_define_method(cHttpParser, "filter_body", HttpParser_filter_body, 2);
|
707
745
|
rb_define_method(cHttpParser, "trailers", HttpParser_headers, 2);
|
746
|
+
rb_define_method(cHttpParser, "filter_body", HttpParser_filter_body, 2);
|
708
747
|
rb_define_method(cHttpParser, "content_length", HttpParser_content_length, 0);
|
709
748
|
rb_define_method(cHttpParser, "body_eof?", HttpParser_body_eof, 0);
|
710
749
|
rb_define_method(cHttpParser, "keepalive?", HttpParser_keepalive, 0);
|
711
750
|
rb_define_method(cHttpParser, "headers?", HttpParser_has_headers, 0);
|
751
|
+
rb_define_method(cHttpParser, "buf", HttpParser_buf, 0);
|
752
|
+
rb_define_method(cHttpParser, "env", HttpParser_env, 0);
|
712
753
|
|
713
754
|
/*
|
714
755
|
* The maximum size a single chunk when using chunked transfer encoding.
|
@@ -731,5 +772,6 @@ void Init_unicorn_http(void)
|
|
731
772
|
SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
|
732
773
|
SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
|
733
774
|
SET_GLOBAL(g_http_connection, "CONNECTION");
|
775
|
+
id_clear = rb_intern("clear");
|
734
776
|
}
|
735
777
|
#undef SET_GLOBAL
|
data/lib/unicorn.rb
CHANGED
@@ -73,11 +73,11 @@ class Unicorn::ClientShutdown < EOFError; end
|
|
73
73
|
|
74
74
|
require 'unicorn/const'
|
75
75
|
require 'unicorn/socket_helper'
|
76
|
+
require 'unicorn/tee_input'
|
76
77
|
require 'unicorn/http_request'
|
77
78
|
require 'unicorn/configurator'
|
78
79
|
require 'unicorn/tmpio'
|
79
80
|
require 'unicorn/util'
|
80
|
-
require 'unicorn/tee_input'
|
81
81
|
require 'unicorn/http_response'
|
82
82
|
require 'unicorn/worker'
|
83
83
|
require 'unicorn/http_server'
|
data/lib/unicorn/const.rb
CHANGED
@@ -7,8 +7,8 @@
|
|
7
7
|
# improve things much compared to constants.
|
8
8
|
module Unicorn::Const
|
9
9
|
|
10
|
-
# The current version of Unicorn, currently 2.0.
|
11
|
-
UNICORN_VERSION = "2.0.
|
10
|
+
# The current version of Unicorn, currently 2.0.0pre2
|
11
|
+
UNICORN_VERSION = "2.0.0pre2"
|
12
12
|
|
13
13
|
# default TCP listen host address (0.0.0.0, all interfaces)
|
14
14
|
DEFAULT_HOST = "0.0.0.0"
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
require 'unicorn_http'
|
4
4
|
|
5
|
-
|
5
|
+
# TODO: remove redundant names
|
6
|
+
Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
|
7
|
+
class Unicorn::HttpParser
|
6
8
|
|
7
9
|
# default parameters we merge into the request env for Rack handlers
|
8
10
|
DEFAULTS = {
|
@@ -23,20 +25,9 @@ class Unicorn::HttpRequest
|
|
23
25
|
# A frozen format for this is about 15% faster
|
24
26
|
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
25
27
|
RACK_INPUT = 'rack.input'.freeze
|
28
|
+
TeeInput = Unicorn::TeeInput
|
26
29
|
# :startdoc:
|
27
30
|
|
28
|
-
attr_reader :env, :parser, :buf
|
29
|
-
|
30
|
-
def initialize
|
31
|
-
@parser = Unicorn::HttpParser.new
|
32
|
-
@buf = ""
|
33
|
-
@env = {}
|
34
|
-
end
|
35
|
-
|
36
|
-
def response_headers?
|
37
|
-
@parser.headers?
|
38
|
-
end
|
39
|
-
|
40
31
|
# Does the majority of the IO processing. It has been written in
|
41
32
|
# Ruby using about 8 different IO processing strategies.
|
42
33
|
#
|
@@ -51,8 +42,8 @@ class Unicorn::HttpRequest
|
|
51
42
|
# This does minimal exception trapping and it is up to the caller
|
52
43
|
# to handle any socket errors (e.g. user aborted upload).
|
53
44
|
def read(socket)
|
54
|
-
|
55
|
-
|
45
|
+
reset
|
46
|
+
e = env
|
56
47
|
|
57
48
|
# From http://www.ietf.org/rfc/rfc3875:
|
58
49
|
# "Script authors should be aware that the REMOTE_ADDR and
|
@@ -61,18 +52,18 @@ class Unicorn::HttpRequest
|
|
61
52
|
# identify the client for the immediate request to the server;
|
62
53
|
# that client may be a proxy, gateway, or other intermediary
|
63
54
|
# acting on behalf of the actual source client."
|
64
|
-
|
55
|
+
e[REMOTE_ADDR] = socket.kgio_addr
|
65
56
|
|
66
57
|
# short circuit the common case with small GET requests first
|
67
|
-
|
58
|
+
socket.kgio_read!(16384, buf)
|
59
|
+
if parse.nil?
|
68
60
|
# Parser is not done, queue up more data to read and continue parsing
|
69
|
-
# an Exception thrown from the
|
61
|
+
# an Exception thrown from the parser will throw us out of the loop
|
70
62
|
begin
|
71
|
-
|
72
|
-
end while
|
63
|
+
buf << socket.kgio_read!(16384)
|
64
|
+
end while parse.nil?
|
73
65
|
end
|
74
|
-
|
75
|
-
|
76
|
-
@env.merge!(DEFAULTS)
|
66
|
+
e[RACK_INPUT] = 0 == content_length ? NULL_IO : TeeInput.new(socket, self)
|
67
|
+
e.merge!(DEFAULTS)
|
77
68
|
end
|
78
69
|
end
|
data/lib/unicorn/http_server.rb
CHANGED
@@ -7,9 +7,10 @@
|
|
7
7
|
class Unicorn::HttpServer
|
8
8
|
attr_accessor :app, :request, :timeout, :worker_processes,
|
9
9
|
:before_fork, :after_fork, :before_exec,
|
10
|
-
:
|
10
|
+
:listener_opts, :preload_app,
|
11
11
|
:reexec_pid, :orig_app, :init_listeners,
|
12
12
|
:master_pid, :config, :ready_pipe, :user
|
13
|
+
attr_reader :pid, :logger
|
13
14
|
|
14
15
|
# :stopdoc:
|
15
16
|
include Unicorn::SocketHelper
|
@@ -520,7 +521,7 @@ class Unicorn::HttpServer
|
|
520
521
|
r = @app.call(env)
|
521
522
|
end
|
522
523
|
# r may be frozen or const, so don't modify it
|
523
|
-
@request.
|
524
|
+
@request.headers? or r = [ r[0], nil, r[2] ]
|
524
525
|
http_response_write(client, r)
|
525
526
|
rescue => e
|
526
527
|
handle_error(client, e)
|
data/lib/unicorn/tee_input.rb
CHANGED
@@ -11,8 +11,8 @@
|
|
11
11
|
#
|
12
12
|
# When processing uploads, Unicorn exposes a TeeInput object under
|
13
13
|
# "rack.input" of the Rack environment.
|
14
|
-
class Unicorn::TeeInput
|
15
|
-
|
14
|
+
class Unicorn::TeeInput
|
15
|
+
attr_accessor :tmp, :socket, :parser, :env, :buf, :len, :buf2
|
16
16
|
|
17
17
|
# The maximum size (in +bytes+) to buffer in memory before
|
18
18
|
# resorting to a temporary file. Default is 112 kilobytes.
|
@@ -26,18 +26,18 @@ class Unicorn::TeeInput < Struct.new(:socket, :req, :parser,
|
|
26
26
|
# Initializes a new TeeInput object. You normally do not have to call
|
27
27
|
# this unless you are writing an HTTP server.
|
28
28
|
def initialize(socket, request)
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if buf.size > 0
|
38
|
-
parser.filter_body(buf2, buf) and finalize_input
|
39
|
-
tmp.write(buf2)
|
40
|
-
tmp.rewind
|
29
|
+
@socket = socket
|
30
|
+
@parser = request
|
31
|
+
@buf = request.buf
|
32
|
+
@env = request.env
|
33
|
+
@len = request.content_length
|
34
|
+
@tmp = @len && @len < @@client_body_buffer_size ?
|
35
|
+
StringIO.new("") : Unicorn::TmpIO.new
|
36
|
+
@buf2 = ""
|
37
|
+
if @buf.size > 0
|
38
|
+
@parser.filter_body(@buf2, @buf) and finalize_input
|
39
|
+
@tmp.write(@buf2)
|
40
|
+
@tmp.rewind
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -58,16 +58,16 @@ class Unicorn::TeeInput < Struct.new(:socket, :req, :parser,
|
|
58
58
|
# earlier. Most applications should only need to call +read+ with a
|
59
59
|
# specified +length+ in a loop until it returns +nil+.
|
60
60
|
def size
|
61
|
-
len and return len
|
61
|
+
@len and return @len
|
62
62
|
|
63
63
|
if socket
|
64
|
-
pos = tmp.pos
|
65
|
-
while tee(@@io_chunk_size, buf2)
|
64
|
+
pos = @tmp.pos
|
65
|
+
while tee(@@io_chunk_size, @buf2)
|
66
66
|
end
|
67
|
-
tmp.seek(pos)
|
67
|
+
@tmp.seek(pos)
|
68
68
|
end
|
69
69
|
|
70
|
-
|
70
|
+
@len = @tmp.size
|
71
71
|
end
|
72
72
|
|
73
73
|
# :call-seq:
|
@@ -90,22 +90,22 @@ class Unicorn::TeeInput < Struct.new(:socket, :req, :parser,
|
|
90
90
|
# any data and only block when nothing is available (providing
|
91
91
|
# IO#readpartial semantics).
|
92
92
|
def read(*args)
|
93
|
-
socket or return tmp.read(*args)
|
93
|
+
@socket or return @tmp.read(*args)
|
94
94
|
|
95
95
|
length = args.shift
|
96
96
|
if nil == length
|
97
|
-
rv = tmp.read || ""
|
98
|
-
while tee(@@io_chunk_size, buf2)
|
99
|
-
rv << buf2
|
97
|
+
rv = @tmp.read || ""
|
98
|
+
while tee(@@io_chunk_size, @buf2)
|
99
|
+
rv << @buf2
|
100
100
|
end
|
101
101
|
rv
|
102
102
|
else
|
103
103
|
rv = args.shift || ""
|
104
|
-
diff = tmp.size - tmp.pos
|
104
|
+
diff = @tmp.size - @tmp.pos
|
105
105
|
if 0 == diff
|
106
106
|
ensure_length(tee(length, rv), length)
|
107
107
|
else
|
108
|
-
ensure_length(tmp.read(diff > length ? length : diff, rv), length)
|
108
|
+
ensure_length(@tmp.read(diff > length ? length : diff, rv), length)
|
109
109
|
end
|
110
110
|
end
|
111
111
|
end
|
@@ -120,27 +120,27 @@ class Unicorn::TeeInput < Struct.new(:socket, :req, :parser,
|
|
120
120
|
# This takes zero arguments for strict Rack::Lint compatibility,
|
121
121
|
# unlike IO#gets.
|
122
122
|
def gets
|
123
|
-
socket or return tmp.gets
|
123
|
+
@socket or return @tmp.gets
|
124
124
|
sep = $/ or return read
|
125
125
|
|
126
|
-
orig_size = tmp.size
|
127
|
-
if tmp.pos == orig_size
|
128
|
-
tee(@@io_chunk_size, buf2) or return nil
|
129
|
-
tmp.seek(orig_size)
|
126
|
+
orig_size = @tmp.size
|
127
|
+
if @tmp.pos == orig_size
|
128
|
+
tee(@@io_chunk_size, @buf2) or return nil
|
129
|
+
@tmp.seek(orig_size)
|
130
130
|
end
|
131
131
|
|
132
132
|
sep_size = Rack::Utils.bytesize(sep)
|
133
|
-
line = tmp.gets # cannot be nil here since size > pos
|
133
|
+
line = @tmp.gets # cannot be nil here since size > pos
|
134
134
|
sep == line[-sep_size, sep_size] and return line
|
135
135
|
|
136
|
-
# unlikely, if we got here, then tmp is at EOF
|
136
|
+
# unlikely, if we got here, then @tmp is at EOF
|
137
137
|
begin
|
138
|
-
orig_size = tmp.pos
|
139
|
-
tee(@@io_chunk_size, buf2) or break
|
140
|
-
tmp.seek(orig_size)
|
141
|
-
line << tmp.gets
|
138
|
+
orig_size = @tmp.pos
|
139
|
+
tee(@@io_chunk_size, @buf2) or break
|
140
|
+
@tmp.seek(orig_size)
|
141
|
+
line << @tmp.gets
|
142
142
|
sep == line[-sep_size, sep_size] and return line
|
143
|
-
# tmp is at EOF again here, retry the loop
|
143
|
+
# @tmp is at EOF again here, retry the loop
|
144
144
|
end while true
|
145
145
|
|
146
146
|
line
|
@@ -166,7 +166,7 @@ class Unicorn::TeeInput < Struct.new(:socket, :req, :parser,
|
|
166
166
|
# the offset (zero) of the +ios+ pointer. Subsequent reads will
|
167
167
|
# start from the beginning of the previously-buffered input.
|
168
168
|
def rewind
|
169
|
-
tmp.rewind # Rack does not specify what the return value is here
|
169
|
+
@tmp.rewind # Rack does not specify what the return value is here
|
170
170
|
end
|
171
171
|
|
172
172
|
private
|
@@ -175,11 +175,11 @@ private
|
|
175
175
|
# backing store as well as returning it. +dst+ must be specified.
|
176
176
|
# returns nil if reading from the input returns nil
|
177
177
|
def tee(length, dst)
|
178
|
-
unless parser.body_eof?
|
179
|
-
r = socket.kgio_read(length, buf) or eof!
|
180
|
-
unless parser.filter_body(dst,
|
181
|
-
tmp.write(dst)
|
182
|
-
tmp.seek(0, IO::SEEK_END) # workaround FreeBSD/OSX + MRI 1.8.x bug
|
178
|
+
unless @parser.body_eof?
|
179
|
+
r = @socket.kgio_read(length, @buf) or eof!
|
180
|
+
unless @parser.filter_body(dst, @buf)
|
181
|
+
@tmp.write(dst)
|
182
|
+
@tmp.seek(0, IO::SEEK_END) # workaround FreeBSD/OSX + MRI 1.8.x bug
|
183
183
|
return dst
|
184
184
|
end
|
185
185
|
end
|
@@ -187,11 +187,11 @@ private
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def finalize_input
|
190
|
-
while parser.trailers(
|
191
|
-
r = socket.kgio_read(@@io_chunk_size) or eof!
|
192
|
-
buf << r
|
190
|
+
while @parser.trailers(@env, @buf).nil?
|
191
|
+
r = @socket.kgio_read(@@io_chunk_size) or eof!
|
192
|
+
@buf << r
|
193
193
|
end
|
194
|
-
|
194
|
+
@socket = nil
|
195
195
|
end
|
196
196
|
|
197
197
|
# tee()s into +dst+ until it is of +length+ bytes (or until
|
@@ -204,10 +204,10 @@ private
|
|
204
204
|
# len is nil for chunked bodies, so we can't ensure length for those
|
205
205
|
# since they could be streaming bidirectionally and we don't want to
|
206
206
|
# block the caller in that case.
|
207
|
-
return dst if dst.nil? || len.nil?
|
207
|
+
return dst if dst.nil? || @len.nil?
|
208
208
|
|
209
|
-
while dst.size < length && tee(length - dst.size, buf2)
|
210
|
-
dst << buf2
|
209
|
+
while dst.size < length && tee(length - dst.size, @buf2)
|
210
|
+
dst << @buf2
|
211
211
|
end
|
212
212
|
|
213
213
|
dst
|
@@ -218,7 +218,7 @@ private
|
|
218
218
|
# we do support clients that shutdown(SHUT_WR) after the
|
219
219
|
# _entire_ request has been sent, and those will not have
|
220
220
|
# raised EOFError on us.
|
221
|
-
socket.close if socket
|
222
|
-
raise Unicorn::ClientShutdown, "bytes_read=#{tmp.size}", []
|
221
|
+
@socket.close if @socket
|
222
|
+
raise Unicorn::ClientShutdown, "bytes_read=#{@tmp.size}", []
|
223
223
|
end
|
224
224
|
end
|
@@ -388,6 +388,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
388
388
|
"*" => { qs => "", pi => "" },
|
389
389
|
}.each do |uri,expect|
|
390
390
|
assert_equal req, @parser.headers(req.clear, str % [ uri ])
|
391
|
+
req = req.dup
|
391
392
|
@parser.reset
|
392
393
|
assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
|
393
394
|
assert_equal expect[qs], req[qs], "#{qs} mismatch"
|
@@ -412,6 +413,7 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
412
413
|
"/1?a=b;c=d&e=f" => { qs => "a=b;c=d&e=f", pi => "/1" },
|
413
414
|
}.each do |uri,expect|
|
414
415
|
assert_equal req, @parser.headers(req.clear, str % [ uri ])
|
416
|
+
req = req.dup
|
415
417
|
@parser.reset
|
416
418
|
assert_equal uri, req["REQUEST_URI"], "REQUEST_URI mismatch"
|
417
419
|
assert_equal "example.com", req["HTTP_HOST"], "Host: mismatch"
|
data/test/unit/test_tee_input.rb
CHANGED
@@ -5,7 +5,6 @@ require 'digest/sha1'
|
|
5
5
|
require 'unicorn'
|
6
6
|
|
7
7
|
class TestTeeInput < Test::Unit::TestCase
|
8
|
-
MockRequest = Struct.new(:env, :parser, :buf)
|
9
8
|
|
10
9
|
def setup
|
11
10
|
@rs = $/
|
@@ -164,7 +163,7 @@ class TestTeeInput < Test::Unit::TestCase
|
|
164
163
|
@wr.write("0\r\n\r\n")
|
165
164
|
}
|
166
165
|
@wr.close
|
167
|
-
ti = Unicorn::TeeInput.new(@rd,
|
166
|
+
ti = Unicorn::TeeInput.new(@rd, @parser)
|
168
167
|
assert_nil @parser.content_length
|
169
168
|
assert_nil ti.len
|
170
169
|
assert ! @parser.body_eof?
|
@@ -202,7 +201,7 @@ class TestTeeInput < Test::Unit::TestCase
|
|
202
201
|
end
|
203
202
|
@wr.write("0\r\n\r\n")
|
204
203
|
}
|
205
|
-
ti = Unicorn::TeeInput.new(@rd,
|
204
|
+
ti = Unicorn::TeeInput.new(@rd, @parser)
|
206
205
|
assert_nil @parser.content_length
|
207
206
|
assert_nil ti.len
|
208
207
|
assert ! @parser.body_eof?
|
@@ -231,7 +230,7 @@ class TestTeeInput < Test::Unit::TestCase
|
|
231
230
|
@wr.write("Hello: World\r\n\r\n")
|
232
231
|
}
|
233
232
|
@wr.close
|
234
|
-
ti = Unicorn::TeeInput.new(@rd,
|
233
|
+
ti = Unicorn::TeeInput.new(@rd, @parser)
|
235
234
|
assert_nil @parser.content_length
|
236
235
|
assert_nil ti.len
|
237
236
|
assert ! @parser.body_eof?
|
@@ -253,7 +252,7 @@ private
|
|
253
252
|
"\r\n#{body}"
|
254
253
|
assert_equal @env, @parser.headers(@env, @buf)
|
255
254
|
assert_equal body, @buf
|
256
|
-
|
255
|
+
@parser
|
257
256
|
end
|
258
257
|
|
259
258
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unicorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: -
|
4
|
+
hash: -766259858
|
5
5
|
prerelease: true
|
6
6
|
segments:
|
7
7
|
- 2
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 2.0.
|
9
|
+
- 0pre2
|
10
|
+
version: 2.0.0pre2
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Unicorn hackers
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-07 00:00:00 +00:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|