unicorn 2.0.0pre1 → 2.0.0pre2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|