unicorn 0.91.0 → 0.92.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. data/{CHANGELOG → .CHANGELOG.old} +0 -0
  2. data/.document +4 -0
  3. data/.gitignore +5 -0
  4. data/.mailmap +26 -0
  5. data/CONTRIBUTORS +1 -1
  6. data/Documentation/.gitignore +5 -0
  7. data/Documentation/GNUmakefile +30 -0
  8. data/Documentation/unicorn.1.txt +158 -0
  9. data/Documentation/unicorn_rails.1.txt +150 -0
  10. data/GIT-VERSION-GEN +40 -0
  11. data/GNUmakefile +102 -14
  12. data/README +3 -2
  13. data/Rakefile +104 -32
  14. data/SIGNALS +2 -4
  15. data/TODO +3 -9
  16. data/bin/unicorn +5 -2
  17. data/bin/unicorn_rails +5 -3
  18. data/ext/unicorn_http/c_util.h +2 -2
  19. data/ext/unicorn_http/common_field_optimization.h +2 -1
  20. data/ext/unicorn_http/ext_help.h +29 -4
  21. data/ext/unicorn_http/extconf.rb +5 -0
  22. data/ext/unicorn_http/unicorn_http.rl +131 -76
  23. data/lib/unicorn.rb +6 -2
  24. data/lib/unicorn/app/exec_cgi.rb +3 -1
  25. data/lib/unicorn/app/inetd.rb +2 -0
  26. data/lib/unicorn/app/old_rails.rb +2 -0
  27. data/lib/unicorn/app/old_rails/static.rb +3 -1
  28. data/lib/unicorn/cgi_wrapper.rb +3 -1
  29. data/lib/unicorn/configurator.rb +2 -0
  30. data/lib/unicorn/const.rb +8 -6
  31. data/lib/unicorn/http_request.rb +6 -5
  32. data/lib/unicorn/http_response.rb +4 -2
  33. data/lib/unicorn/launcher.rb +6 -0
  34. data/lib/unicorn/socket_helper.rb +5 -5
  35. data/lib/unicorn/tee_input.rb +2 -0
  36. data/lib/unicorn/util.rb +2 -0
  37. data/local.mk.sample +4 -2
  38. data/setup.rb +1 -0
  39. data/test/aggregate.rb +2 -0
  40. data/test/exec/test_exec.rb +157 -0
  41. data/test/rails/app-1.2.3/app/controllers/application.rb +2 -0
  42. data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +2 -0
  43. data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
  44. data/test/rails/app-1.2.3/config/boot.rb +2 -0
  45. data/test/rails/app-1.2.3/config/environment.rb +2 -0
  46. data/test/rails/app-1.2.3/config/environments/development.rb +2 -0
  47. data/test/rails/app-1.2.3/config/environments/production.rb +2 -0
  48. data/test/rails/app-1.2.3/config/routes.rb +2 -0
  49. data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
  50. data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +2 -0
  51. data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
  52. data/test/rails/app-2.0.2/config/boot.rb +2 -0
  53. data/test/rails/app-2.0.2/config/environment.rb +2 -0
  54. data/test/rails/app-2.0.2/config/environments/development.rb +2 -0
  55. data/test/rails/app-2.0.2/config/environments/production.rb +2 -0
  56. data/test/rails/app-2.0.2/config/routes.rb +2 -0
  57. data/test/rails/app-2.1.2/app/controllers/application.rb +2 -0
  58. data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +2 -0
  59. data/test/rails/app-2.1.2/app/helpers/application_helper.rb +2 -0
  60. data/test/rails/app-2.1.2/config/boot.rb +2 -0
  61. data/test/rails/app-2.1.2/config/environment.rb +2 -0
  62. data/test/rails/app-2.1.2/config/environments/development.rb +2 -0
  63. data/test/rails/app-2.1.2/config/environments/production.rb +2 -0
  64. data/test/rails/app-2.1.2/config/routes.rb +2 -0
  65. data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
  66. data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +2 -0
  67. data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
  68. data/test/rails/app-2.2.2/config/boot.rb +2 -0
  69. data/test/rails/app-2.2.2/config/environment.rb +2 -0
  70. data/test/rails/app-2.2.2/config/environments/development.rb +2 -0
  71. data/test/rails/app-2.2.2/config/environments/production.rb +2 -0
  72. data/test/rails/app-2.2.2/config/routes.rb +2 -0
  73. data/test/rails/app-2.3.3.1/app/controllers/application_controller.rb +2 -0
  74. data/test/rails/app-2.3.3.1/app/controllers/foo_controller.rb +2 -0
  75. data/test/rails/app-2.3.3.1/app/helpers/application_helper.rb +2 -0
  76. data/test/rails/app-2.3.3.1/config/boot.rb +2 -0
  77. data/test/rails/app-2.3.3.1/config/environment.rb +2 -0
  78. data/test/rails/app-2.3.3.1/config/environments/development.rb +2 -0
  79. data/test/rails/app-2.3.3.1/config/environments/production.rb +2 -0
  80. data/test/rails/app-2.3.3.1/config/routes.rb +2 -0
  81. data/test/rails/test_rails.rb +2 -0
  82. data/test/test_helper.rb +8 -0
  83. data/test/unit/test_configurator.rb +2 -0
  84. data/test/unit/test_http_parser.rb +13 -0
  85. data/test/unit/test_http_parser_ng.rb +2 -0
  86. data/test/unit/test_request.rb +2 -0
  87. data/test/unit/test_response.rb +2 -0
  88. data/test/unit/test_server.rb +2 -0
  89. data/test/unit/test_signals.rb +2 -0
  90. data/test/unit/test_socket_helper.rb +2 -0
  91. data/test/unit/test_tee_input.rb +2 -1
  92. data/test/unit/test_upload.rb +2 -0
  93. data/test/unit/test_util.rb +2 -0
  94. data/unicorn.gemspec +38 -28
  95. metadata +38 -42
  96. data/Manifest +0 -137
@@ -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->flags |= UH_FL_KAMETHOD;
100
+ HP_FL_SET(hp, KAMETHOD);
62
101
  v = g_GET;
63
102
  } else if (CONST_MEM_EQ("HEAD", ptr, len)) {
64
- hp->flags |= UH_FL_KAMETHOD;
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->flags |= UH_FL_HASHEADER;
116
+ HP_FL_SET(hp, HASHEADER);
78
117
 
79
118
  if (CONST_MEM_EQ("HTTP/1.1", ptr, len)) {
80
- hp->flags |= UH_FL_KAVERSION;
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 invalid_if_trailer(int flags)
131
+ static inline void hp_invalid_if_trailer(struct http_parser *hp)
92
132
  {
93
- if (flags & UH_FL_INTRAILER)
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 (!hp->cont)
103
- rb_raise(eHttpParserError, "invalid continuation line");
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->mark > 0);
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 == Qnil) {
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
- if (hp->flags & UH_FL_KAMETHOD) {
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->flags |= UH_FL_HASBODY;
146
- invalid_if_trailer(hp->flags);
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->flags |= UH_FL_CHUNKED | UH_FL_HASBODY;
150
- invalid_if_trailer(hp->flags);
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->flags |= UH_FL_HASTRAILER;
153
- invalid_if_trailer(hp->flags);
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 == Qnil) {
200
+ if (NIL_P(e)) {
158
201
  hp->cont = rb_hash_aset(req, f, v);
159
- } else if (f != g_http_host) {
160
- /* full URLs in REQUEST_URI take precedence for the Host: header */
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->flags & UH_FL_HASBODY) {
236
- hp->flags |= UH_FL_INBODY;
237
- if (hp->flags & UH_FL_CHUNKED)
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->flags & UH_FL_CHUNKED));
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->flags & UH_FL_HASTRAILER) {
256
- hp->flags |= UH_FL_INTRAILER;
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->flags |= UH_FL_INCHUNK;
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->flags & UH_FL_INCHUNK) {
315
- hp->flags &= ~(UH_FL_INCHUNK);
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 == Qnil) {
394
+ if (NIL_P(temp)) {
345
395
  temp = rb_hash_aref(req, g_http_x_forwarded_proto);
346
- if (temp != Qnil && STR_CSTR_EQ(temp, "https"))
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 != Qnil) {
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->flags & UH_FL_HASHEADER))
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) == Qnil)
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
- if (hp->cont)
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->flags & UH_FL_CHUNKED) ? Qnil : OFFT2NUM(hp->len.content);
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->flags & UH_FL_CHUNKED)
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->flags & UH_FL_KEEPALIVE) == UH_FL_KEEPALIVE ? Qtrue : Qfalse;
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->flags & UH_FL_HASHEADER) ? Qtrue : Qfalse;
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 = RSTRING_PTR(data);
571
- long dlen = RSTRING_LEN(data);
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->flags & UH_FL_CHUNKED) {
578
- if (chunked_eof(hp))
579
- goto end_of_body;
580
-
581
- hp->s.dest_offset = 0;
582
- http_parser_execute(hp, buf, dptr, dlen);
583
- if (hp->cs == http_parser_error)
584
- rb_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
585
-
586
- assert(hp->s.dest_offset <= hp->start.offset);
587
- advance_str(data, hp->start.offset);
588
- rb_str_set_len(buf, hp->s.dest_offset);
589
-
590
- if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
591
- assert(hp->len.chunk == 0);
592
- } else {
593
- data = Qnil;
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 != Qnil); \
672
+ assert(!NIL_P(var) && "missed global field"); \
618
673
  } while (0)
619
674
 
620
675
  void Init_unicorn_http(void)
@@ -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 ].freeze
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 every listener
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