unicorn 4.7.0 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (119) hide show
  1. checksums.yaml +7 -0
  2. data/.document +0 -1
  3. data/.gitattributes +5 -0
  4. data/.gitignore +2 -2
  5. data/.manifest +14 -21
  6. data/.olddoc.yml +22 -0
  7. data/Application_Timeouts +7 -7
  8. data/DESIGN +2 -4
  9. data/Documentation/.gitignore +1 -3
  10. data/Documentation/unicorn.1 +222 -0
  11. data/Documentation/unicorn_rails.1 +207 -0
  12. data/FAQ +23 -6
  13. data/GIT-VERSION-FILE +1 -1
  14. data/GIT-VERSION-GEN +1 -1
  15. data/GNUmakefile +139 -92
  16. data/HACKING +13 -28
  17. data/ISSUES +82 -19
  18. data/KNOWN_ISSUES +18 -18
  19. data/LATEST +22 -44
  20. data/LICENSE +2 -2
  21. data/Links +24 -22
  22. data/NEWS +729 -0
  23. data/PHILOSOPHY +0 -6
  24. data/README +50 -48
  25. data/Rakefile +0 -44
  26. data/SIGNALS +12 -3
  27. data/Sandbox +11 -10
  28. data/TODO +0 -2
  29. data/TUNING +30 -9
  30. data/archive/.gitignore +3 -0
  31. data/archive/slrnpull.conf +4 -0
  32. data/bin/unicorn +4 -2
  33. data/bin/unicorn_rails +3 -3
  34. data/examples/big_app_gc.rb +1 -1
  35. data/examples/init.sh +36 -8
  36. data/examples/logrotate.conf +17 -2
  37. data/examples/nginx.conf +14 -14
  38. data/examples/unicorn.conf.minimal.rb +2 -2
  39. data/examples/unicorn.conf.rb +14 -6
  40. data/examples/unicorn.socket +11 -0
  41. data/examples/unicorn@.service +40 -0
  42. data/ext/unicorn_http/common_field_optimization.h +23 -5
  43. data/ext/unicorn_http/ext_help.h +0 -20
  44. data/ext/unicorn_http/extconf.rb +37 -1
  45. data/ext/unicorn_http/global_variables.h +1 -1
  46. data/ext/unicorn_http/httpdate.c +2 -2
  47. data/ext/unicorn_http/unicorn_http.c +940 -644
  48. data/ext/unicorn_http/unicorn_http.rl +167 -170
  49. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  50. data/lib/unicorn/configurator.rb +110 -46
  51. data/lib/unicorn/const.rb +2 -25
  52. data/lib/unicorn/http_request.rb +110 -31
  53. data/lib/unicorn/http_response.rb +17 -31
  54. data/lib/unicorn/http_server.rb +292 -199
  55. data/lib/unicorn/launcher.rb +1 -1
  56. data/lib/unicorn/oob_gc.rb +16 -6
  57. data/lib/unicorn/socket_helper.rb +58 -78
  58. data/lib/unicorn/stream_input.rb +9 -11
  59. data/lib/unicorn/tee_input.rb +16 -11
  60. data/lib/unicorn/tmpio.rb +10 -6
  61. data/lib/unicorn/util.rb +5 -4
  62. data/lib/unicorn/version.rb +1 -1
  63. data/lib/unicorn/worker.rb +99 -22
  64. data/lib/unicorn.rb +69 -42
  65. data/man/man1/unicorn.1 +124 -122
  66. data/man/man1/unicorn_rails.1 +113 -127
  67. data/t/.gitignore +0 -1
  68. data/t/GNUmakefile +3 -80
  69. data/t/README +4 -4
  70. data/t/t0002-parser-error.sh +3 -3
  71. data/t/t0011-active-unix-socket.sh +1 -1
  72. data/t/t0012-reload-empty-config.sh +2 -1
  73. data/t/t0300-no-default-middleware.sh +6 -1
  74. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  75. data/t/t0301.ru +13 -0
  76. data/t/test-lib.sh +2 -2
  77. data/test/benchmark/README +14 -4
  78. data/test/benchmark/ddstream.ru +50 -0
  79. data/test/benchmark/readinput.ru +40 -0
  80. data/test/benchmark/uconnect.perl +66 -0
  81. data/test/exec/test_exec.rb +74 -20
  82. data/test/test_helper.rb +42 -33
  83. data/test/unit/test_ccc.rb +91 -0
  84. data/test/unit/test_droplet.rb +1 -1
  85. data/test/unit/test_http_parser.rb +49 -19
  86. data/test/unit/test_http_parser_ng.rb +98 -115
  87. data/test/unit/test_request.rb +11 -11
  88. data/test/unit/test_response.rb +31 -19
  89. data/test/unit/test_server.rb +89 -15
  90. data/test/unit/test_signals.rb +9 -9
  91. data/test/unit/test_socket_helper.rb +20 -14
  92. data/test/unit/test_tee_input.rb +10 -0
  93. data/test/unit/test_upload.rb +10 -15
  94. data/test/unit/test_util.rb +28 -3
  95. data/unicorn.gemspec +28 -23
  96. data/unicorn_1 +1 -0
  97. data/unicorn_rails_1 +1 -0
  98. metadata +64 -134
  99. data/.wrongdoc.yml +0 -10
  100. data/ChangeLog +0 -4694
  101. data/Documentation/GNUmakefile +0 -30
  102. data/Documentation/unicorn.1.txt +0 -178
  103. data/Documentation/unicorn_rails.1.txt +0 -175
  104. data/examples/git.ru +0 -13
  105. data/lib/unicorn/app/exec_cgi.rb +0 -154
  106. data/lib/unicorn/app/inetd.rb +0 -109
  107. data/lib/unicorn/ssl_client.rb +0 -11
  108. data/lib/unicorn/ssl_configurator.rb +0 -104
  109. data/lib/unicorn/ssl_server.rb +0 -42
  110. data/local.mk.sample +0 -59
  111. data/script/isolate_for_tests +0 -32
  112. data/t/hijack.ru +0 -42
  113. data/t/sslgen.sh +0 -71
  114. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  115. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  116. data/t/t0200-rack-hijack.sh +0 -27
  117. data/t/t0600-https-server-basic.sh +0 -48
  118. data/test/unit/test_http_parser_xftrust.rb +0 -38
  119. data/test/unit/test_sni_hostnames.rb +0 -47
@@ -25,86 +25,33 @@ void init_unicorn_httpdate(void);
25
25
  #define UH_FL_KAVERSION 0x80
26
26
  #define UH_FL_HASHEADER 0x100
27
27
  #define UH_FL_TO_CLEAR 0x200
28
+ #define UH_FL_RESSTART 0x400 /* for check_client_connection */
29
+ #define UH_FL_HIJACK 0x800
28
30
 
29
31
  /* all of these flags need to be set for keepalive to be supported */
30
32
  #define UH_FL_KEEPALIVE (UH_FL_KAVERSION | UH_FL_REQEOF | UH_FL_HASHEADER)
31
33
 
32
- /*
33
- * whether or not to trust X-Forwarded-Proto and X-Forwarded-SSL when
34
- * setting rack.url_scheme
35
- */
36
- static VALUE trust_x_forward = Qtrue;
37
-
38
- static unsigned long keepalive_requests = 100; /* same as nginx */
39
-
40
- /*
41
- * Returns the maximum number of keepalive requests a client may make
42
- * before the parser refuses to continue.
43
- */
44
- static VALUE ka_req(VALUE self)
45
- {
46
- return ULONG2NUM(keepalive_requests);
47
- }
48
-
49
- /*
50
- * Sets the maximum number of keepalive requests a client may make.
51
- * A special value of +nil+ causes this to be the maximum value
52
- * possible (this is architecture-dependent).
53
- */
54
- static VALUE set_ka_req(VALUE self, VALUE val)
55
- {
56
- keepalive_requests = NIL_P(val) ? ULONG_MAX : NUM2ULONG(val);
57
-
58
- return ka_req(self);
59
- }
60
-
61
- /*
62
- * Sets whether or not the parser will trust X-Forwarded-Proto and
63
- * X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
64
- * Rainbows!/Zbatery installations facing untrusted clients directly
65
- * should set this to +false+
66
- */
67
- static VALUE set_xftrust(VALUE self, VALUE val)
68
- {
69
- if (Qtrue == val || Qfalse == val)
70
- trust_x_forward = val;
71
- else
72
- rb_raise(rb_eTypeError, "must be true or false");
73
-
74
- return val;
75
- }
76
-
77
- /*
78
- * returns whether or not the parser will trust X-Forwarded-Proto and
79
- * X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly
80
- */
81
- static VALUE xftrust(VALUE self)
82
- {
83
- return trust_x_forward;
84
- }
85
-
86
- static size_t MAX_HEADER_LEN = 1024 * (80 + 32); /* same as Mongrel */
34
+ static unsigned int MAX_HEADER_LEN = 1024 * (80 + 32); /* same as Mongrel */
87
35
 
88
36
  /* this is only intended for use with Rainbows! */
89
37
  static VALUE set_maxhdrlen(VALUE self, VALUE len)
90
38
  {
91
- return SIZET2NUM(MAX_HEADER_LEN = NUM2SIZET(len));
39
+ return UINT2NUM(MAX_HEADER_LEN = NUM2UINT(len));
92
40
  }
93
41
 
94
- /* keep this small for Rainbows! since every client has one */
42
+ /* keep this small for other servers (e.g. yahns) since every client has one */
95
43
  struct http_parser {
96
44
  int cs; /* Ragel internal state */
97
45
  unsigned int flags;
98
- unsigned long nr_requests;
99
- size_t mark;
100
- size_t offset;
46
+ unsigned int mark;
47
+ unsigned int offset;
101
48
  union { /* these 2 fields don't nest */
102
- size_t field;
103
- size_t query;
49
+ unsigned int field;
50
+ unsigned int query;
104
51
  } start;
105
52
  union {
106
- size_t field_len; /* only used during header processing */
107
- size_t dest_offset; /* only used during body processing */
53
+ unsigned int field_len; /* only used during header processing */
54
+ unsigned int dest_offset; /* only used during body processing */
108
55
  } s;
109
56
  VALUE buf;
110
57
  VALUE env;
@@ -115,7 +62,20 @@ struct http_parser {
115
62
  } len;
116
63
  };
117
64
 
118
- static ID id_clear, id_set_backtrace, id_response_start_sent;
65
+ static ID id_set_backtrace, id_is_chunked_p;
66
+ static VALUE cHttpParser;
67
+
68
+ #ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
69
+ # define my_hash_clear(h) (void)rb_hash_clear(h)
70
+ #else /* !HAVE_RB_HASH_CLEAR - Ruby <= 1.9.3 */
71
+
72
+ static ID id_clear;
73
+
74
+ static void my_hash_clear(VALUE h)
75
+ {
76
+ rb_funcall(h, id_clear, 0);
77
+ }
78
+ #endif /* HAVE_RB_HASH_CLEAR */
119
79
 
120
80
  static void finalize_header(struct http_parser *hp);
121
81
 
@@ -124,13 +84,25 @@ static void parser_raise(VALUE klass, const char *msg)
124
84
  VALUE exc = rb_exc_new2(klass, msg);
125
85
  VALUE bt = rb_ary_new();
126
86
 
127
- rb_funcall(exc, id_set_backtrace, 1, bt);
128
- rb_exc_raise(exc);
87
+ rb_funcall(exc, id_set_backtrace, 1, bt);
88
+ rb_exc_raise(exc);
89
+ }
90
+
91
+ static inline unsigned int ulong2uint(unsigned long n)
92
+ {
93
+ unsigned int i = (unsigned int)n;
94
+
95
+ if (sizeof(unsigned int) != sizeof(unsigned long)) {
96
+ if ((unsigned long)i != n) {
97
+ rb_raise(rb_eRangeError, "too large to be 32-bit uint: %lu", n);
98
+ }
99
+ }
100
+ return i;
129
101
  }
130
102
 
131
103
  #define REMAINING (unsigned long)(pe - p)
132
- #define LEN(AT, FPC) (FPC - buffer - hp->AT)
133
- #define MARK(M,FPC) (hp->M = (FPC) - buffer)
104
+ #define LEN(AT, FPC) (ulong2uint(FPC - buffer) - hp->AT)
105
+ #define MARK(M,FPC) (hp->M = ulong2uint((FPC) - buffer))
134
106
  #define PTR_TO(F) (buffer + hp->F)
135
107
  #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
136
108
  #define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
@@ -249,6 +221,19 @@ static void write_cont_value(struct http_parser *hp,
249
221
  rb_str_buf_cat(hp->cont, vptr, end + 1);
250
222
  }
251
223
 
224
+ static int is_chunked(VALUE v)
225
+ {
226
+ /* common case first */
227
+ if (STR_CSTR_CASE_EQ(v, "chunked"))
228
+ return 1;
229
+
230
+ /*
231
+ * call Ruby function in unicorn/http_request.rb to deal with unlikely
232
+ * comma-delimited case
233
+ */
234
+ return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
235
+ }
236
+
252
237
  static void write_value(struct http_parser *hp,
253
238
  const char *buffer, const char *p)
254
239
  {
@@ -275,7 +260,9 @@ static void write_value(struct http_parser *hp,
275
260
  f = uncommon_field(field, flen);
276
261
  } else if (f == g_http_connection) {
277
262
  hp_keepalive_connection(hp, v);
278
- } else if (f == g_content_length) {
263
+ } else if (f == g_content_length && !HP_FL_TEST(hp, CHUNKED)) {
264
+ if (hp->len.content)
265
+ parser_raise(eHttpParserError, "Content-Length already set");
279
266
  hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
280
267
  if (hp->len.content < 0)
281
268
  parser_raise(eHttpParserError, "invalid Content-Length");
@@ -283,9 +270,30 @@ static void write_value(struct http_parser *hp,
283
270
  HP_FL_SET(hp, HASBODY);
284
271
  hp_invalid_if_trailer(hp);
285
272
  } else if (f == g_http_transfer_encoding) {
286
- if (STR_CSTR_CASE_EQ(v, "chunked")) {
273
+ if (is_chunked(v)) {
274
+ if (HP_FL_TEST(hp, CHUNKED))
275
+ /*
276
+ * RFC 7230 3.3.1:
277
+ * A sender MUST NOT apply chunked more than once to a message body
278
+ * (i.e., chunking an already chunked message is not allowed).
279
+ */
280
+ parser_raise(eHttpParserError, "Transfer-Encoding double chunked");
281
+
287
282
  HP_FL_SET(hp, CHUNKED);
288
283
  HP_FL_SET(hp, HASBODY);
284
+
285
+ /* RFC 7230 3.3.3, 3: favor chunked if Content-Length exists */
286
+ hp->len.content = 0;
287
+ } else if (HP_FL_TEST(hp, CHUNKED)) {
288
+ /*
289
+ * RFC 7230 3.3.3, point 3 states:
290
+ * If a Transfer-Encoding header field is present in a request and
291
+ * the chunked transfer coding is not the final encoding, the
292
+ * message body length cannot be determined reliably; the server
293
+ * MUST respond with the 400 (Bad Request) status code and then
294
+ * close the connection.
295
+ */
296
+ parser_raise(eHttpParserError, "invalid Transfer-Encoding");
289
297
  }
290
298
  hp_invalid_if_trailer(hp);
291
299
  } else if (f == g_http_trailer) {
@@ -466,17 +474,37 @@ http_parser_execute(struct http_parser *hp, char *buffer, size_t len)
466
474
  post_exec: /* "_out:" also goes here */
467
475
  if (hp->cs != http_parser_error)
468
476
  hp->cs = cs;
469
- hp->offset = p - buffer;
477
+ hp->offset = ulong2uint(p - buffer);
470
478
 
471
479
  assert(p <= pe && "buffer overflow after parsing execute");
472
480
  assert(hp->offset <= len && "offset longer than length");
473
481
  }
474
482
 
483
+ static void hp_mark(void *ptr)
484
+ {
485
+ struct http_parser *hp = ptr;
486
+
487
+ rb_gc_mark(hp->buf);
488
+ rb_gc_mark(hp->env);
489
+ rb_gc_mark(hp->cont);
490
+ }
491
+
492
+ static size_t hp_memsize(const void *ptr)
493
+ {
494
+ return sizeof(struct http_parser);
495
+ }
496
+
497
+ static const rb_data_type_t hp_type = {
498
+ "unicorn_http",
499
+ { hp_mark, RUBY_TYPED_DEFAULT_FREE, hp_memsize, /* reserved */ },
500
+ /* parent, data, [ flags ] */
501
+ };
502
+
475
503
  static struct http_parser *data_get(VALUE self)
476
504
  {
477
505
  struct http_parser *hp;
478
506
 
479
- Data_Get_Struct(self, struct http_parser, hp);
507
+ TypedData_Get_Struct(self, struct http_parser, &hp_type, hp);
480
508
  assert(hp && "failed to extract http_parser struct");
481
509
  return hp;
482
510
  }
@@ -491,26 +519,29 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
491
519
  VALUE scheme = rb_hash_aref(env, g_rack_url_scheme);
492
520
 
493
521
  if (NIL_P(scheme)) {
494
- if (trust_x_forward == Qfalse) {
495
- scheme = g_http;
522
+ /*
523
+ * would anybody be horribly opposed to removing the X-Forwarded-SSL
524
+ * and X-Forwarded-Proto handling from this parser? We've had it
525
+ * forever and nobody has said anything against it, either.
526
+ * Anyways, please send comments to our public mailing list:
527
+ * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
528
+ */
529
+ scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
530
+ if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
531
+ *server_port = g_port_443;
532
+ scheme = g_https;
496
533
  } else {
497
- scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
498
- if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
499
- *server_port = g_port_443;
500
- scheme = g_https;
534
+ scheme = rb_hash_aref(env, g_http_x_forwarded_proto);
535
+ if (NIL_P(scheme)) {
536
+ scheme = g_http;
501
537
  } else {
502
- scheme = rb_hash_aref(env, g_http_x_forwarded_proto);
503
- if (NIL_P(scheme)) {
504
- scheme = g_http;
538
+ long len = RSTRING_LEN(scheme);
539
+ if (len >= 5 && !memcmp(RSTRING_PTR(scheme), "https", 5)) {
540
+ if (len != 5)
541
+ scheme = g_https;
542
+ *server_port = g_port_443;
505
543
  } else {
506
- long len = RSTRING_LEN(scheme);
507
- if (len >= 5 && !memcmp(RSTRING_PTR(scheme), "https", 5)) {
508
- if (len != 5)
509
- scheme = g_https;
510
- *server_port = g_port_443;
511
- } else {
512
- scheme = g_http;
513
- }
544
+ scheme = g_http;
514
545
  }
515
546
  }
516
547
  }
@@ -579,21 +610,12 @@ static void finalize_header(struct http_parser *hp)
579
610
  rb_hash_aset(hp->env, g_query_string, rb_str_new(NULL, 0));
580
611
  }
581
612
 
582
- static void hp_mark(void *ptr)
583
- {
584
- struct http_parser *hp = ptr;
585
-
586
- rb_gc_mark(hp->buf);
587
- rb_gc_mark(hp->env);
588
- rb_gc_mark(hp->cont);
589
- }
590
-
591
613
  static VALUE HttpParser_alloc(VALUE klass)
592
614
  {
593
615
  struct http_parser *hp;
594
- return Data_Make_Struct(klass, struct http_parser, hp_mark, -1, hp);
595
- }
596
616
 
617
+ return TypedData_Make_Struct(klass, struct http_parser, &hp_type, hp);
618
+ }
597
619
 
598
620
  /**
599
621
  * call-seq:
@@ -608,7 +630,6 @@ static VALUE HttpParser_init(VALUE self)
608
630
  http_parser_init(hp);
609
631
  hp->buf = rb_str_new(NULL, 0);
610
632
  hp->env = rb_hash_new();
611
- hp->nr_requests = keepalive_requests;
612
633
 
613
634
  return self;
614
635
  }
@@ -624,62 +645,16 @@ static VALUE HttpParser_clear(VALUE self)
624
645
  {
625
646
  struct http_parser *hp = data_get(self);
626
647
 
627
- http_parser_init(hp);
628
- rb_funcall(hp->env, id_clear, 0);
629
- rb_ivar_set(self, id_response_start_sent, Qfalse);
630
-
631
- return self;
632
- }
633
-
634
- /**
635
- * call-seq:
636
- * parser.dechunk! => parser
637
- *
638
- * Resets the parser to a state suitable for dechunking response bodies
639
- *
640
- */
641
- static VALUE HttpParser_dechunk_bang(VALUE self)
642
- {
643
- struct http_parser *hp = data_get(self);
648
+ /* we can't safely reuse .buf and .env if hijacked */
649
+ if (HP_FL_TEST(hp, HIJACK))
650
+ return HttpParser_init(self);
644
651
 
645
652
  http_parser_init(hp);
646
-
647
- /*
648
- * we don't care about trailers in dechunk-only mode,
649
- * but if we did we'd set UH_FL_HASTRAILER and clear hp->env
650
- */
651
- if (0) {
652
- rb_funcall(hp->env, id_clear, 0);
653
- hp->flags = UH_FL_HASTRAILER;
654
- }
655
-
656
- hp->flags |= UH_FL_HASBODY | UH_FL_INBODY | UH_FL_CHUNKED;
657
- hp->cs = http_parser_en_ChunkedBody;
653
+ my_hash_clear(hp->env);
658
654
 
659
655
  return self;
660
656
  }
661
657
 
662
- /**
663
- * call-seq:
664
- * parser.reset => nil
665
- *
666
- * Resets the parser to it's initial state so that you can reuse it
667
- * rather than making new ones.
668
- *
669
- * This method is deprecated and to be removed in Unicorn 4.x
670
- */
671
- static VALUE HttpParser_reset(VALUE self)
672
- {
673
- static int warned;
674
-
675
- if (!warned) {
676
- rb_warn("Unicorn::HttpParser#reset is deprecated; "
677
- "use Unicorn::HttpParser#clear instead");
678
- }
679
- HttpParser_clear(self);
680
- return Qnil;
681
- }
682
-
683
658
  static void advance_str(VALUE str, off_t nr)
684
659
  {
685
660
  long len = RSTRING_LEN(str);
@@ -842,15 +817,13 @@ static VALUE HttpParser_keepalive(VALUE self)
842
817
  * parser.next? => true or false
843
818
  *
844
819
  * Exactly like HttpParser#keepalive?, except it will reset the internal
845
- * parser state on next parse if it returns true. It will also respect
846
- * the maximum *keepalive_requests* value and return false if that is
847
- * reached.
820
+ * parser state on next parse if it returns true.
848
821
  */
849
822
  static VALUE HttpParser_next(VALUE self)
850
823
  {
851
824
  struct http_parser *hp = data_get(self);
852
825
 
853
- if ((HP_FL_ALL(hp, KEEPALIVE)) && (hp->nr_requests-- != 0)) {
826
+ if (HP_FL_ALL(hp, KEEPALIVE)) {
854
827
  HP_FL_SET(hp, TO_CLEAR);
855
828
  return Qtrue;
856
829
  }
@@ -882,6 +855,15 @@ static VALUE HttpParser_env(VALUE self)
882
855
  return data_get(self)->env;
883
856
  }
884
857
 
858
+ static VALUE HttpParser_hijacked_bang(VALUE self)
859
+ {
860
+ struct http_parser *hp = data_get(self);
861
+
862
+ HP_FL_SET(hp, HIJACK);
863
+
864
+ return self;
865
+ }
866
+
885
867
  /**
886
868
  * call-seq:
887
869
  * parser.filter_body(dst, src) => nil/src
@@ -960,6 +942,25 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
960
942
  return src;
961
943
  }
962
944
 
945
+ static VALUE HttpParser_rssset(VALUE self, VALUE boolean)
946
+ {
947
+ struct http_parser *hp = data_get(self);
948
+
949
+ if (RTEST(boolean))
950
+ HP_FL_SET(hp, RESSTART);
951
+ else
952
+ HP_FL_UNSET(hp, RESSTART);
953
+
954
+ return boolean; /* ignored by Ruby anyways */
955
+ }
956
+
957
+ static VALUE HttpParser_rssget(VALUE self)
958
+ {
959
+ struct http_parser *hp = data_get(self);
960
+
961
+ return HP_FL_TEST(hp, RESSTART) ? Qtrue : Qfalse;
962
+ }
963
+
963
964
  #define SET_GLOBAL(var,str) do { \
964
965
  var = find_common_field(str, sizeof(str) - 1); \
965
966
  assert(!NIL_P(var) && "missed global field"); \
@@ -967,9 +968,9 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
967
968
 
968
969
  void Init_unicorn_http(void)
969
970
  {
970
- VALUE mUnicorn, cHttpParser;
971
+ VALUE mUnicorn;
971
972
 
972
- mUnicorn = rb_const_get(rb_cObject, rb_intern("Unicorn"));
973
+ mUnicorn = rb_define_module("Unicorn");
973
974
  cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
974
975
  eHttpParserError =
975
976
  rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
@@ -982,8 +983,6 @@ void Init_unicorn_http(void)
982
983
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
983
984
  rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
984
985
  rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
985
- rb_define_method(cHttpParser, "reset", HttpParser_reset, 0);
986
- rb_define_method(cHttpParser, "dechunk!", HttpParser_dechunk_bang, 0);
987
986
  rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
988
987
  rb_define_method(cHttpParser, "add_parse", HttpParser_add_parse, 1);
989
988
  rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
@@ -996,6 +995,9 @@ void Init_unicorn_http(void)
996
995
  rb_define_method(cHttpParser, "next?", HttpParser_next, 0);
997
996
  rb_define_method(cHttpParser, "buf", HttpParser_buf, 0);
998
997
  rb_define_method(cHttpParser, "env", HttpParser_env, 0);
998
+ rb_define_method(cHttpParser, "hijacked!", HttpParser_hijacked_bang, 0);
999
+ rb_define_method(cHttpParser, "response_start_sent=", HttpParser_rssset, 1);
1000
+ rb_define_method(cHttpParser, "response_start_sent", HttpParser_rssget, 0);
999
1001
 
1000
1002
  /*
1001
1003
  * The maximum size a single chunk when using chunked transfer encoding.
@@ -1012,14 +1014,6 @@ void Init_unicorn_http(void)
1012
1014
  */
1013
1015
  rb_define_const(cHttpParser, "LENGTH_MAX", OFFT2NUM(UH_OFF_T_MAX));
1014
1016
 
1015
- /* default value for keepalive_requests */
1016
- rb_define_const(cHttpParser, "KEEPALIVE_REQUESTS_DEFAULT",
1017
- ULONG2NUM(keepalive_requests));
1018
-
1019
- rb_define_singleton_method(cHttpParser, "keepalive_requests", ka_req, 0);
1020
- rb_define_singleton_method(cHttpParser, "keepalive_requests=", set_ka_req, 1);
1021
- rb_define_singleton_method(cHttpParser, "trust_x_forwarded=", set_xftrust, 1);
1022
- rb_define_singleton_method(cHttpParser, "trust_x_forwarded?", xftrust, 0);
1023
1017
  rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
1024
1018
 
1025
1019
  init_common_fields();
@@ -1028,9 +1022,12 @@ void Init_unicorn_http(void)
1028
1022
  SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
1029
1023
  SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
1030
1024
  SET_GLOBAL(g_http_connection, "CONNECTION");
1031
- id_clear = rb_intern("clear");
1032
1025
  id_set_backtrace = rb_intern("set_backtrace");
1033
- id_response_start_sent = rb_intern("@response_start_sent");
1034
1026
  init_unicorn_httpdate();
1027
+
1028
+ #ifndef HAVE_RB_HASH_CLEAR
1029
+ id_clear = rb_intern("clear");
1030
+ #endif
1031
+ id_is_chunked_p = rb_intern("is_chunked?");
1035
1032
  }
1036
1033
  #undef SET_GLOBAL
@@ -4,7 +4,7 @@
4
4
 
5
5
  #### HTTP PROTOCOL GRAMMAR
6
6
  # line endings
7
- CRLF = "\r\n";
7
+   CRLF = ("\r\n" | "\n");
8
8
 
9
9
  # character types
10
10
  CTL = (cntrl | 127);