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.
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