unicorn 4.9.0 → 6.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +5 -0
  3. data/.manifest +14 -15
  4. data/.olddoc.yml +16 -6
  5. data/Application_Timeouts +7 -7
  6. data/CONTRIBUTORS +6 -2
  7. data/DESIGN +2 -4
  8. data/Documentation/.gitignore +1 -3
  9. data/Documentation/unicorn.1 +222 -0
  10. data/Documentation/unicorn_rails.1 +207 -0
  11. data/FAQ +17 -8
  12. data/GIT-VERSION-FILE +1 -1
  13. data/GIT-VERSION-GEN +1 -1
  14. data/GNUmakefile +121 -56
  15. data/HACKING +2 -10
  16. data/ISSUES +40 -43
  17. data/KNOWN_ISSUES +11 -11
  18. data/LATEST +16 -22
  19. data/LICENSE +2 -2
  20. data/Links +24 -25
  21. data/NEWS +771 -0
  22. data/PHILOSOPHY +0 -6
  23. data/README +46 -40
  24. data/SIGNALS +2 -2
  25. data/Sandbox +11 -10
  26. data/TODO +0 -2
  27. data/TUNING +30 -9
  28. data/archive/slrnpull.conf +1 -1
  29. data/bin/unicorn +4 -2
  30. data/bin/unicorn_rails +3 -3
  31. data/examples/big_app_gc.rb +1 -1
  32. data/examples/init.sh +36 -8
  33. data/examples/logrotate.conf +17 -2
  34. data/examples/nginx.conf +14 -14
  35. data/examples/unicorn.conf.minimal.rb +2 -2
  36. data/examples/unicorn.conf.rb +3 -6
  37. data/examples/unicorn.socket +11 -0
  38. data/examples/unicorn@.service +40 -0
  39. data/ext/unicorn_http/c_util.h +5 -13
  40. data/ext/unicorn_http/common_field_optimization.h +22 -5
  41. data/ext/unicorn_http/epollexclusive.h +124 -0
  42. data/ext/unicorn_http/ext_help.h +0 -44
  43. data/ext/unicorn_http/extconf.rb +32 -5
  44. data/ext/unicorn_http/global_variables.h +2 -2
  45. data/ext/unicorn_http/httpdate.c +3 -2
  46. data/ext/unicorn_http/unicorn_http.c +926 -638
  47. data/ext/unicorn_http/unicorn_http.rl +159 -170
  48. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  49. data/lib/unicorn/configurator.rb +110 -44
  50. data/lib/unicorn/const.rb +2 -25
  51. data/lib/unicorn/http_request.rb +110 -31
  52. data/lib/unicorn/http_response.rb +17 -31
  53. data/lib/unicorn/http_server.rb +255 -179
  54. data/lib/unicorn/launcher.rb +1 -1
  55. data/lib/unicorn/oob_gc.rb +6 -6
  56. data/lib/unicorn/select_waiter.rb +6 -0
  57. data/lib/unicorn/socket_helper.rb +58 -78
  58. data/lib/unicorn/stream_input.rb +8 -7
  59. data/lib/unicorn/tee_input.rb +8 -10
  60. data/lib/unicorn/tmpio.rb +8 -7
  61. data/lib/unicorn/util.rb +5 -4
  62. data/lib/unicorn/version.rb +1 -1
  63. data/lib/unicorn/worker.rb +36 -23
  64. data/lib/unicorn.rb +64 -46
  65. data/man/man1/unicorn.1 +123 -119
  66. data/man/man1/unicorn_rails.1 +106 -107
  67. data/t/GNUmakefile +3 -72
  68. data/t/README +4 -4
  69. data/t/t0011-active-unix-socket.sh +1 -1
  70. data/t/t0012-reload-empty-config.sh +2 -1
  71. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  72. data/t/t0301.ru +13 -0
  73. data/t/test-lib.sh +4 -3
  74. data/test/benchmark/README +14 -4
  75. data/test/benchmark/ddstream.ru +50 -0
  76. data/test/benchmark/readinput.ru +40 -0
  77. data/test/benchmark/uconnect.perl +66 -0
  78. data/test/exec/test_exec.rb +73 -19
  79. data/test/test_helper.rb +40 -31
  80. data/test/unit/test_ccc.rb +91 -0
  81. data/test/unit/test_droplet.rb +1 -1
  82. data/test/unit/test_http_parser.rb +46 -16
  83. data/test/unit/test_http_parser_ng.rb +97 -114
  84. data/test/unit/test_request.rb +10 -10
  85. data/test/unit/test_response.rb +28 -16
  86. data/test/unit/test_server.rb +86 -12
  87. data/test/unit/test_signals.rb +8 -8
  88. data/test/unit/test_socket_helper.rb +14 -10
  89. data/test/unit/test_upload.rb +9 -14
  90. data/test/unit/test_util.rb +31 -5
  91. data/test/unit/test_waiter.rb +34 -0
  92. data/unicorn.gemspec +27 -19
  93. metadata +28 -45
  94. data/Documentation/GNUmakefile +0 -30
  95. data/Documentation/unicorn.1.txt +0 -185
  96. data/Documentation/unicorn_rails.1.txt +0 -175
  97. data/examples/git.ru +0 -13
  98. data/lib/unicorn/app/exec_cgi.rb +0 -154
  99. data/lib/unicorn/app/inetd.rb +0 -109
  100. data/lib/unicorn/ssl_client.rb +0 -11
  101. data/lib/unicorn/ssl_configurator.rb +0 -104
  102. data/lib/unicorn/ssl_server.rb +0 -42
  103. data/t/hijack.ru +0 -42
  104. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  105. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  106. data/t/t0200-rack-hijack.sh +0 -27
  107. data/test/unit/test_http_parser_xftrust.rb +0 -38
  108. 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);