unicorn 4.8.0 → 6.1.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 (123) 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 +17 -21
  6. data/.olddoc.yml +25 -0
  7. data/Application_Timeouts +7 -7
  8. data/CONTRIBUTORS +6 -2
  9. data/DESIGN +2 -4
  10. data/Documentation/.gitignore +1 -3
  11. data/Documentation/unicorn.1 +222 -0
  12. data/Documentation/unicorn_rails.1 +207 -0
  13. data/FAQ +23 -6
  14. data/GIT-VERSION-FILE +1 -1
  15. data/GIT-VERSION-GEN +1 -1
  16. data/GNUmakefile +138 -92
  17. data/HACKING +10 -32
  18. data/ISSUES +80 -19
  19. data/KNOWN_ISSUES +18 -18
  20. data/LATEST +23 -27
  21. data/LICENSE +2 -2
  22. data/Links +24 -22
  23. data/NEWS +823 -0
  24. data/PHILOSOPHY +0 -6
  25. data/README +49 -48
  26. data/Rakefile +0 -44
  27. data/SIGNALS +2 -2
  28. data/Sandbox +11 -10
  29. data/TODO +0 -2
  30. data/TUNING +30 -9
  31. data/archive/.gitignore +3 -0
  32. data/archive/slrnpull.conf +4 -0
  33. data/bin/unicorn +4 -2
  34. data/bin/unicorn_rails +3 -3
  35. data/examples/big_app_gc.rb +1 -1
  36. data/examples/init.sh +36 -8
  37. data/examples/logrotate.conf +17 -2
  38. data/examples/nginx.conf +14 -14
  39. data/examples/unicorn.conf.minimal.rb +2 -2
  40. data/examples/unicorn.conf.rb +14 -6
  41. data/examples/unicorn.socket +11 -0
  42. data/examples/unicorn@.service +40 -0
  43. data/ext/unicorn_http/c_util.h +5 -13
  44. data/ext/unicorn_http/common_field_optimization.h +22 -5
  45. data/ext/unicorn_http/epollexclusive.h +124 -0
  46. data/ext/unicorn_http/ext_help.h +0 -44
  47. data/ext/unicorn_http/extconf.rb +32 -5
  48. data/ext/unicorn_http/global_variables.h +2 -2
  49. data/ext/unicorn_http/httpdate.c +3 -2
  50. data/ext/unicorn_http/unicorn_http.c +926 -638
  51. data/ext/unicorn_http/unicorn_http.rl +159 -170
  52. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  53. data/lib/unicorn/configurator.rb +110 -46
  54. data/lib/unicorn/const.rb +2 -25
  55. data/lib/unicorn/http_request.rb +110 -31
  56. data/lib/unicorn/http_response.rb +17 -31
  57. data/lib/unicorn/http_server.rb +272 -180
  58. data/lib/unicorn/launcher.rb +1 -1
  59. data/lib/unicorn/oob_gc.rb +16 -6
  60. data/lib/unicorn/select_waiter.rb +6 -0
  61. data/lib/unicorn/socket_helper.rb +58 -78
  62. data/lib/unicorn/stream_input.rb +8 -7
  63. data/lib/unicorn/tee_input.rb +16 -11
  64. data/lib/unicorn/tmpio.rb +10 -6
  65. data/lib/unicorn/util.rb +5 -4
  66. data/lib/unicorn/version.rb +1 -1
  67. data/lib/unicorn/worker.rb +36 -23
  68. data/lib/unicorn.rb +64 -44
  69. data/man/man1/unicorn.1 +124 -122
  70. data/man/man1/unicorn_rails.1 +113 -127
  71. data/t/.gitignore +0 -1
  72. data/t/GNUmakefile +3 -80
  73. data/t/README +4 -4
  74. data/t/t0002-parser-error.sh +3 -3
  75. data/t/t0011-active-unix-socket.sh +1 -1
  76. data/t/t0012-reload-empty-config.sh +2 -1
  77. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  78. data/t/t0301.ru +13 -0
  79. data/t/test-lib.sh +4 -3
  80. data/test/benchmark/README +14 -4
  81. data/test/benchmark/ddstream.ru +50 -0
  82. data/test/benchmark/readinput.ru +40 -0
  83. data/test/benchmark/uconnect.perl +66 -0
  84. data/test/exec/test_exec.rb +74 -20
  85. data/test/test_helper.rb +42 -33
  86. data/test/unit/test_ccc.rb +91 -0
  87. data/test/unit/test_droplet.rb +1 -1
  88. data/test/unit/test_http_parser.rb +49 -19
  89. data/test/unit/test_http_parser_ng.rb +98 -115
  90. data/test/unit/test_request.rb +11 -11
  91. data/test/unit/test_response.rb +31 -19
  92. data/test/unit/test_server.rb +89 -15
  93. data/test/unit/test_signals.rb +9 -9
  94. data/test/unit/test_socket_helper.rb +16 -12
  95. data/test/unit/test_tee_input.rb +10 -0
  96. data/test/unit/test_upload.rb +10 -15
  97. data/test/unit/test_util.rb +32 -6
  98. data/test/unit/test_waiter.rb +34 -0
  99. data/unicorn.gemspec +28 -23
  100. data/unicorn_1 +1 -0
  101. data/unicorn_rails_1 +1 -0
  102. metadata +68 -134
  103. data/.wrongdoc.yml +0 -10
  104. data/ChangeLog +0 -4861
  105. data/Documentation/GNUmakefile +0 -30
  106. data/Documentation/unicorn.1.txt +0 -178
  107. data/Documentation/unicorn_rails.1.txt +0 -175
  108. data/examples/git.ru +0 -13
  109. data/lib/unicorn/app/exec_cgi.rb +0 -154
  110. data/lib/unicorn/app/inetd.rb +0 -109
  111. data/lib/unicorn/ssl_client.rb +0 -11
  112. data/lib/unicorn/ssl_configurator.rb +0 -104
  113. data/lib/unicorn/ssl_server.rb +0 -42
  114. data/local.mk.sample +0 -59
  115. data/script/isolate_for_tests +0 -32
  116. data/t/hijack.ru +0 -42
  117. data/t/sslgen.sh +0 -71
  118. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  119. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  120. data/t/t0200-rack-hijack.sh +0 -27
  121. data/t/t0600-https-server-basic.sh +0 -48
  122. data/test/unit/test_http_parser_xftrust.rb +0 -38
  123. data/test/unit/test_sni_hostnames.rb +0 -47
@@ -12,6 +12,7 @@
12
12
  #include "common_field_optimization.h"
13
13
  #include "global_variables.h"
14
14
  #include "c_util.h"
15
+ #include "epollexclusive.h"
15
16
 
16
17
  void init_unicorn_httpdate(void);
17
18
 
@@ -25,86 +26,33 @@ void init_unicorn_httpdate(void);
25
26
  #define UH_FL_KAVERSION 0x80
26
27
  #define UH_FL_HASHEADER 0x100
27
28
  #define UH_FL_TO_CLEAR 0x200
29
+ #define UH_FL_RESSTART 0x400 /* for check_client_connection */
30
+ #define UH_FL_HIJACK 0x800
28
31
 
29
32
  /* all of these flags need to be set for keepalive to be supported */
30
33
  #define UH_FL_KEEPALIVE (UH_FL_KAVERSION | UH_FL_REQEOF | UH_FL_HASHEADER)
31
34
 
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 */
35
+ static unsigned int MAX_HEADER_LEN = 1024 * (80 + 32); /* same as Mongrel */
87
36
 
88
37
  /* this is only intended for use with Rainbows! */
89
38
  static VALUE set_maxhdrlen(VALUE self, VALUE len)
90
39
  {
91
- return SIZET2NUM(MAX_HEADER_LEN = NUM2SIZET(len));
40
+ return UINT2NUM(MAX_HEADER_LEN = NUM2UINT(len));
92
41
  }
93
42
 
94
- /* keep this small for Rainbows! since every client has one */
43
+ /* keep this small for other servers (e.g. yahns) since every client has one */
95
44
  struct http_parser {
96
45
  int cs; /* Ragel internal state */
97
46
  unsigned int flags;
98
- unsigned long nr_requests;
99
- size_t mark;
100
- size_t offset;
47
+ unsigned int mark;
48
+ unsigned int offset;
101
49
  union { /* these 2 fields don't nest */
102
- size_t field;
103
- size_t query;
50
+ unsigned int field;
51
+ unsigned int query;
104
52
  } start;
105
53
  union {
106
- size_t field_len; /* only used during header processing */
107
- size_t dest_offset; /* only used during body processing */
54
+ unsigned int field_len; /* only used during header processing */
55
+ unsigned int dest_offset; /* only used during body processing */
108
56
  } s;
109
57
  VALUE buf;
110
58
  VALUE env;
@@ -115,7 +63,8 @@ struct http_parser {
115
63
  } len;
116
64
  };
117
65
 
118
- static ID id_clear, id_set_backtrace, id_response_start_sent;
66
+ static ID id_set_backtrace, id_is_chunked_p;
67
+ static VALUE cHttpParser;
119
68
 
120
69
  static void finalize_header(struct http_parser *hp);
121
70
 
@@ -124,13 +73,25 @@ static void parser_raise(VALUE klass, const char *msg)
124
73
  VALUE exc = rb_exc_new2(klass, msg);
125
74
  VALUE bt = rb_ary_new();
126
75
 
127
- rb_funcall(exc, id_set_backtrace, 1, bt);
128
- rb_exc_raise(exc);
76
+ rb_funcall(exc, id_set_backtrace, 1, bt);
77
+ rb_exc_raise(exc);
78
+ }
79
+
80
+ static inline unsigned int ulong2uint(unsigned long n)
81
+ {
82
+ unsigned int i = (unsigned int)n;
83
+
84
+ if (sizeof(unsigned int) != sizeof(unsigned long)) {
85
+ if ((unsigned long)i != n) {
86
+ rb_raise(rb_eRangeError, "too large to be 32-bit uint: %lu", n);
87
+ }
88
+ }
89
+ return i;
129
90
  }
130
91
 
131
92
  #define REMAINING (unsigned long)(pe - p)
132
- #define LEN(AT, FPC) (FPC - buffer - hp->AT)
133
- #define MARK(M,FPC) (hp->M = (FPC) - buffer)
93
+ #define LEN(AT, FPC) (ulong2uint(FPC - buffer) - hp->AT)
94
+ #define MARK(M,FPC) (hp->M = ulong2uint((FPC) - buffer))
134
95
  #define PTR_TO(F) (buffer + hp->F)
135
96
  #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
136
97
  #define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
@@ -249,6 +210,19 @@ static void write_cont_value(struct http_parser *hp,
249
210
  rb_str_buf_cat(hp->cont, vptr, end + 1);
250
211
  }
251
212
 
213
+ static int is_chunked(VALUE v)
214
+ {
215
+ /* common case first */
216
+ if (STR_CSTR_CASE_EQ(v, "chunked"))
217
+ return 1;
218
+
219
+ /*
220
+ * call Ruby function in unicorn/http_request.rb to deal with unlikely
221
+ * comma-delimited case
222
+ */
223
+ return rb_funcall(cHttpParser, id_is_chunked_p, 1, v) != Qfalse;
224
+ }
225
+
252
226
  static void write_value(struct http_parser *hp,
253
227
  const char *buffer, const char *p)
254
228
  {
@@ -275,7 +249,9 @@ static void write_value(struct http_parser *hp,
275
249
  f = uncommon_field(field, flen);
276
250
  } else if (f == g_http_connection) {
277
251
  hp_keepalive_connection(hp, v);
278
- } else if (f == g_content_length) {
252
+ } else if (f == g_content_length && !HP_FL_TEST(hp, CHUNKED)) {
253
+ if (hp->len.content)
254
+ parser_raise(eHttpParserError, "Content-Length already set");
279
255
  hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
280
256
  if (hp->len.content < 0)
281
257
  parser_raise(eHttpParserError, "invalid Content-Length");
@@ -283,9 +259,30 @@ static void write_value(struct http_parser *hp,
283
259
  HP_FL_SET(hp, HASBODY);
284
260
  hp_invalid_if_trailer(hp);
285
261
  } else if (f == g_http_transfer_encoding) {
286
- if (STR_CSTR_CASE_EQ(v, "chunked")) {
262
+ if (is_chunked(v)) {
263
+ if (HP_FL_TEST(hp, CHUNKED))
264
+ /*
265
+ * RFC 7230 3.3.1:
266
+ * A sender MUST NOT apply chunked more than once to a message body
267
+ * (i.e., chunking an already chunked message is not allowed).
268
+ */
269
+ parser_raise(eHttpParserError, "Transfer-Encoding double chunked");
270
+
287
271
  HP_FL_SET(hp, CHUNKED);
288
272
  HP_FL_SET(hp, HASBODY);
273
+
274
+ /* RFC 7230 3.3.3, 3: favor chunked if Content-Length exists */
275
+ hp->len.content = 0;
276
+ } else if (HP_FL_TEST(hp, CHUNKED)) {
277
+ /*
278
+ * RFC 7230 3.3.3, point 3 states:
279
+ * If a Transfer-Encoding header field is present in a request and
280
+ * the chunked transfer coding is not the final encoding, the
281
+ * message body length cannot be determined reliably; the server
282
+ * MUST respond with the 400 (Bad Request) status code and then
283
+ * close the connection.
284
+ */
285
+ parser_raise(eHttpParserError, "invalid Transfer-Encoding");
289
286
  }
290
287
  hp_invalid_if_trailer(hp);
291
288
  } else if (f == g_http_trailer) {
@@ -466,17 +463,37 @@ http_parser_execute(struct http_parser *hp, char *buffer, size_t len)
466
463
  post_exec: /* "_out:" also goes here */
467
464
  if (hp->cs != http_parser_error)
468
465
  hp->cs = cs;
469
- hp->offset = p - buffer;
466
+ hp->offset = ulong2uint(p - buffer);
470
467
 
471
468
  assert(p <= pe && "buffer overflow after parsing execute");
472
469
  assert(hp->offset <= len && "offset longer than length");
473
470
  }
474
471
 
472
+ static void hp_mark(void *ptr)
473
+ {
474
+ struct http_parser *hp = ptr;
475
+
476
+ rb_gc_mark(hp->buf);
477
+ rb_gc_mark(hp->env);
478
+ rb_gc_mark(hp->cont);
479
+ }
480
+
481
+ static size_t hp_memsize(const void *ptr)
482
+ {
483
+ return sizeof(struct http_parser);
484
+ }
485
+
486
+ static const rb_data_type_t hp_type = {
487
+ "unicorn_http",
488
+ { hp_mark, RUBY_TYPED_DEFAULT_FREE, hp_memsize, /* reserved */ },
489
+ /* parent, data, [ flags ] */
490
+ };
491
+
475
492
  static struct http_parser *data_get(VALUE self)
476
493
  {
477
494
  struct http_parser *hp;
478
495
 
479
- Data_Get_Struct(self, struct http_parser, hp);
496
+ TypedData_Get_Struct(self, struct http_parser, &hp_type, hp);
480
497
  assert(hp && "failed to extract http_parser struct");
481
498
  return hp;
482
499
  }
@@ -491,26 +508,29 @@ static void set_url_scheme(VALUE env, VALUE *server_port)
491
508
  VALUE scheme = rb_hash_aref(env, g_rack_url_scheme);
492
509
 
493
510
  if (NIL_P(scheme)) {
494
- if (trust_x_forward == Qfalse) {
495
- scheme = g_http;
511
+ /*
512
+ * would anybody be horribly opposed to removing the X-Forwarded-SSL
513
+ * and X-Forwarded-Proto handling from this parser? We've had it
514
+ * forever and nobody has said anything against it, either.
515
+ * Anyways, please send comments to our public mailing list:
516
+ * unicorn-public@yhbt.net (no HTML mail, no subscription necessary)
517
+ */
518
+ scheme = rb_hash_aref(env, g_http_x_forwarded_ssl);
519
+ if (!NIL_P(scheme) && STR_CSTR_EQ(scheme, "on")) {
520
+ *server_port = g_port_443;
521
+ scheme = g_https;
496
522
  } 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;
523
+ scheme = rb_hash_aref(env, g_http_x_forwarded_proto);
524
+ if (NIL_P(scheme)) {
525
+ scheme = g_http;
501
526
  } else {
502
- scheme = rb_hash_aref(env, g_http_x_forwarded_proto);
503
- if (NIL_P(scheme)) {
504
- scheme = g_http;
527
+ long len = RSTRING_LEN(scheme);
528
+ if (len >= 5 && !memcmp(RSTRING_PTR(scheme), "https", 5)) {
529
+ if (len != 5)
530
+ scheme = g_https;
531
+ *server_port = g_port_443;
505
532
  } 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
- }
533
+ scheme = g_http;
514
534
  }
515
535
  }
516
536
  }
@@ -579,21 +599,12 @@ static void finalize_header(struct http_parser *hp)
579
599
  rb_hash_aset(hp->env, g_query_string, rb_str_new(NULL, 0));
580
600
  }
581
601
 
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
602
  static VALUE HttpParser_alloc(VALUE klass)
592
603
  {
593
604
  struct http_parser *hp;
594
- return Data_Make_Struct(klass, struct http_parser, hp_mark, -1, hp);
595
- }
596
605
 
606
+ return TypedData_Make_Struct(klass, struct http_parser, &hp_type, hp);
607
+ }
597
608
 
598
609
  /**
599
610
  * call-seq:
@@ -608,7 +619,6 @@ static VALUE HttpParser_init(VALUE self)
608
619
  http_parser_init(hp);
609
620
  hp->buf = rb_str_new(NULL, 0);
610
621
  hp->env = rb_hash_new();
611
- hp->nr_requests = keepalive_requests;
612
622
 
613
623
  return self;
614
624
  }
@@ -624,62 +634,16 @@ static VALUE HttpParser_clear(VALUE self)
624
634
  {
625
635
  struct http_parser *hp = data_get(self);
626
636
 
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);
637
+ /* we can't safely reuse .buf and .env if hijacked */
638
+ if (HP_FL_TEST(hp, HIJACK))
639
+ return HttpParser_init(self);
644
640
 
645
641
  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;
642
+ rb_hash_clear(hp->env);
658
643
 
659
644
  return self;
660
645
  }
661
646
 
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
647
  static void advance_str(VALUE str, off_t nr)
684
648
  {
685
649
  long len = RSTRING_LEN(str);
@@ -842,15 +806,13 @@ static VALUE HttpParser_keepalive(VALUE self)
842
806
  * parser.next? => true or false
843
807
  *
844
808
  * 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.
809
+ * parser state on next parse if it returns true.
848
810
  */
849
811
  static VALUE HttpParser_next(VALUE self)
850
812
  {
851
813
  struct http_parser *hp = data_get(self);
852
814
 
853
- if ((HP_FL_ALL(hp, KEEPALIVE)) && (hp->nr_requests-- != 0)) {
815
+ if (HP_FL_ALL(hp, KEEPALIVE)) {
854
816
  HP_FL_SET(hp, TO_CLEAR);
855
817
  return Qtrue;
856
818
  }
@@ -882,6 +844,15 @@ static VALUE HttpParser_env(VALUE self)
882
844
  return data_get(self)->env;
883
845
  }
884
846
 
847
+ static VALUE HttpParser_hijacked_bang(VALUE self)
848
+ {
849
+ struct http_parser *hp = data_get(self);
850
+
851
+ HP_FL_SET(hp, HIJACK);
852
+
853
+ return self;
854
+ }
855
+
885
856
  /**
886
857
  * call-seq:
887
858
  * parser.filter_body(dst, src) => nil/src
@@ -960,6 +931,25 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
960
931
  return src;
961
932
  }
962
933
 
934
+ static VALUE HttpParser_rssset(VALUE self, VALUE boolean)
935
+ {
936
+ struct http_parser *hp = data_get(self);
937
+
938
+ if (RTEST(boolean))
939
+ HP_FL_SET(hp, RESSTART);
940
+ else
941
+ HP_FL_UNSET(hp, RESSTART);
942
+
943
+ return boolean; /* ignored by Ruby anyways */
944
+ }
945
+
946
+ static VALUE HttpParser_rssget(VALUE self)
947
+ {
948
+ struct http_parser *hp = data_get(self);
949
+
950
+ return HP_FL_TEST(hp, RESSTART) ? Qtrue : Qfalse;
951
+ }
952
+
963
953
  #define SET_GLOBAL(var,str) do { \
964
954
  var = find_common_field(str, sizeof(str) - 1); \
965
955
  assert(!NIL_P(var) && "missed global field"); \
@@ -967,9 +957,9 @@ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
967
957
 
968
958
  void Init_unicorn_http(void)
969
959
  {
970
- VALUE mUnicorn, cHttpParser;
960
+ VALUE mUnicorn;
971
961
 
972
- mUnicorn = rb_const_get(rb_cObject, rb_intern("Unicorn"));
962
+ mUnicorn = rb_define_module("Unicorn");
973
963
  cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
974
964
  eHttpParserError =
975
965
  rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
@@ -978,12 +968,11 @@ void Init_unicorn_http(void)
978
968
  e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
979
969
  eHttpParserError);
980
970
 
971
+ id_uminus = rb_intern("-@");
981
972
  init_globals();
982
973
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
983
974
  rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
984
975
  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
976
  rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
988
977
  rb_define_method(cHttpParser, "add_parse", HttpParser_add_parse, 1);
989
978
  rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
@@ -996,6 +985,9 @@ void Init_unicorn_http(void)
996
985
  rb_define_method(cHttpParser, "next?", HttpParser_next, 0);
997
986
  rb_define_method(cHttpParser, "buf", HttpParser_buf, 0);
998
987
  rb_define_method(cHttpParser, "env", HttpParser_env, 0);
988
+ rb_define_method(cHttpParser, "hijacked!", HttpParser_hijacked_bang, 0);
989
+ rb_define_method(cHttpParser, "response_start_sent=", HttpParser_rssset, 1);
990
+ rb_define_method(cHttpParser, "response_start_sent", HttpParser_rssget, 0);
999
991
 
1000
992
  /*
1001
993
  * The maximum size a single chunk when using chunked transfer encoding.
@@ -1012,14 +1004,6 @@ void Init_unicorn_http(void)
1012
1004
  */
1013
1005
  rb_define_const(cHttpParser, "LENGTH_MAX", OFFT2NUM(UH_OFF_T_MAX));
1014
1006
 
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
1007
  rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
1024
1008
 
1025
1009
  init_common_fields();
@@ -1028,9 +1012,14 @@ void Init_unicorn_http(void)
1028
1012
  SET_GLOBAL(g_http_transfer_encoding, "TRANSFER_ENCODING");
1029
1013
  SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
1030
1014
  SET_GLOBAL(g_http_connection, "CONNECTION");
1031
- id_clear = rb_intern("clear");
1032
1015
  id_set_backtrace = rb_intern("set_backtrace");
1033
- id_response_start_sent = rb_intern("@response_start_sent");
1034
1016
  init_unicorn_httpdate();
1017
+
1018
+ #ifndef HAVE_RB_HASH_CLEAR
1019
+ id_clear = rb_intern("clear");
1020
+ #endif
1021
+ id_is_chunked_p = rb_intern("is_chunked?");
1022
+
1023
+ init_epollexclusive(mUnicorn);
1035
1024
  }
1036
1025
  #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);