unicorn 3.6.0 → 4.0.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 (58) hide show
  1. data/.document +1 -0
  2. data/.manifest +13 -0
  3. data/ChangeLog +783 -1
  4. data/DESIGN +0 -8
  5. data/Documentation/GNUmakefile +1 -1
  6. data/GIT-VERSION-FILE +1 -1
  7. data/GIT-VERSION-GEN +1 -1
  8. data/GNUmakefile +2 -2
  9. data/HACKING +11 -0
  10. data/KNOWN_ISSUES +2 -2
  11. data/LATEST +24 -24
  12. data/Links +53 -0
  13. data/NEWS +66 -0
  14. data/PHILOSOPHY +49 -49
  15. data/Sandbox +13 -4
  16. data/TODO +0 -2
  17. data/TUNING +31 -9
  18. data/bin/unicorn +2 -1
  19. data/bin/unicorn_rails +2 -1
  20. data/examples/big_app_gc.rb +2 -33
  21. data/examples/nginx.conf +17 -4
  22. data/ext/unicorn_http/ext_help.h +16 -0
  23. data/ext/unicorn_http/extconf.rb +1 -0
  24. data/ext/unicorn_http/global_variables.h +9 -3
  25. data/ext/unicorn_http/unicorn_http.c +357 -259
  26. data/ext/unicorn_http/unicorn_http.rl +148 -50
  27. data/lib/unicorn/configurator.rb +36 -8
  28. data/lib/unicorn/const.rb +5 -3
  29. data/lib/unicorn/http_request.rb +1 -3
  30. data/lib/unicorn/http_server.rb +82 -95
  31. data/lib/unicorn/oob_gc.rb +61 -50
  32. data/lib/unicorn/socket_helper.rb +23 -8
  33. data/lib/unicorn/worker.rb +45 -4
  34. data/lib/unicorn.rb +8 -6
  35. data/script/isolate_for_tests +4 -2
  36. data/t/broken-app.ru +12 -0
  37. data/t/heartbeat-timeout.ru +12 -0
  38. data/t/oob_gc.ru +21 -0
  39. data/t/oob_gc_path.ru +21 -0
  40. data/t/t0001-reload-bad-config.sh +1 -0
  41. data/t/t0002-parser-error.sh +64 -1
  42. data/t/t0004-heartbeat-timeout.sh +69 -0
  43. data/t/t0009-broken-app.sh +56 -0
  44. data/t/t0019-max_header_len.sh +49 -0
  45. data/t/t0020-at_exit-handler.sh +49 -0
  46. data/t/t9001-oob_gc.sh +47 -0
  47. data/t/t9002-oob_gc-path.sh +75 -0
  48. data/test/benchmark/stack.ru +8 -0
  49. data/test/unit/test_droplet.rb +28 -0
  50. data/test/unit/test_http_parser.rb +60 -4
  51. data/test/unit/test_http_parser_ng.rb +54 -0
  52. data/test/unit/test_response.rb +1 -1
  53. data/test/unit/test_server.rb +1 -1
  54. data/test/unit/test_signals.rb +1 -1
  55. data/test/unit/test_socket_helper.rb +8 -0
  56. data/test/unit/test_upload.rb +1 -1
  57. data/unicorn.gemspec +3 -2
  58. metadata +44 -16
@@ -82,6 +82,14 @@ static VALUE xftrust(VALUE self)
82
82
  return trust_x_forward;
83
83
  }
84
84
 
85
+ static size_t MAX_HEADER_LEN = 1024 * (80 + 32); /* same as Mongrel */
86
+
87
+ /* this is only intended for use with Rainbows! */
88
+ static VALUE set_maxhdrlen(VALUE self, VALUE len)
89
+ {
90
+ return SIZET2NUM(MAX_HEADER_LEN = NUM2SIZET(len));
91
+ }
92
+
85
93
  /* keep this small for Rainbows! since every client has one */
86
94
  struct http_parser {
87
95
  int cs; /* Ragel internal state */
@@ -106,17 +114,17 @@ struct http_parser {
106
114
  } len;
107
115
  };
108
116
 
109
- static ID id_clear;
117
+ static ID id_clear, id_set_backtrace;
110
118
 
111
119
  static void finalize_header(struct http_parser *hp);
112
120
 
113
- static void parser_error(const char *msg)
121
+ static void parser_raise(VALUE klass, const char *msg)
114
122
  {
115
- VALUE exc = rb_exc_new2(eHttpParserError, msg);
123
+ VALUE exc = rb_exc_new2(klass, msg);
116
124
  VALUE bt = rb_ary_new();
117
125
 
118
- rb_funcall(exc, rb_intern("set_backtrace"), 1, bt);
119
- rb_exc_raise(exc);
126
+ rb_funcall(exc, id_set_backtrace, 1, bt);
127
+ rb_exc_raise(exc);
120
128
  }
121
129
 
122
130
  #define REMAINING (unsigned long)(pe - p)
@@ -124,12 +132,27 @@ static void parser_error(const char *msg)
124
132
  #define MARK(M,FPC) (hp->M = (FPC) - buffer)
125
133
  #define PTR_TO(F) (buffer + hp->F)
126
134
  #define STR_NEW(M,FPC) rb_str_new(PTR_TO(M), LEN(M, FPC))
135
+ #define STRIPPED_STR_NEW(M,FPC) stripped_str_new(PTR_TO(M), LEN(M, FPC))
127
136
 
128
137
  #define HP_FL_TEST(hp,fl) ((hp)->flags & (UH_FL_##fl))
129
138
  #define HP_FL_SET(hp,fl) ((hp)->flags |= (UH_FL_##fl))
130
139
  #define HP_FL_UNSET(hp,fl) ((hp)->flags &= ~(UH_FL_##fl))
131
140
  #define HP_FL_ALL(hp,fl) (HP_FL_TEST(hp, fl) == (UH_FL_##fl))
132
141
 
142
+ static int is_lws(char c)
143
+ {
144
+ return (c == ' ' || c == '\t');
145
+ }
146
+
147
+ static VALUE stripped_str_new(const char *str, long len)
148
+ {
149
+ long end;
150
+
151
+ for (end = len - 1; end >= 0 && is_lws(str[end]); end--);
152
+
153
+ return rb_str_new(str, end + 1);
154
+ }
155
+
133
156
  /*
134
157
  * handles values of the "Connection:" header, keepalive is implied
135
158
  * for HTTP/1.1 but needs to be explicitly enabled with HTTP/1.0
@@ -186,35 +209,43 @@ http_version(struct http_parser *hp, const char *ptr, size_t len)
186
209
  static inline void hp_invalid_if_trailer(struct http_parser *hp)
187
210
  {
188
211
  if (HP_FL_TEST(hp, INTRAILER))
189
- parser_error("invalid Trailer");
212
+ parser_raise(eHttpParserError, "invalid Trailer");
190
213
  }
191
214
 
192
215
  static void write_cont_value(struct http_parser *hp,
193
216
  char *buffer, const char *p)
194
217
  {
195
218
  char *vptr;
219
+ long end;
220
+ long len = LEN(mark, p);
221
+ long cont_len;
196
222
 
197
223
  if (hp->cont == Qfalse)
198
- parser_error("invalid continuation line");
224
+ parser_raise(eHttpParserError, "invalid continuation line");
199
225
  if (NIL_P(hp->cont))
200
226
  return; /* we're ignoring this header (probably Host:) */
201
227
 
202
228
  assert(TYPE(hp->cont) == T_STRING && "continuation line is not a string");
203
229
  assert(hp->mark > 0 && "impossible continuation line offset");
204
230
 
205
- if (LEN(mark, p) == 0)
231
+ if (len == 0)
206
232
  return;
207
233
 
208
- if (RSTRING_LEN(hp->cont) > 0)
234
+ cont_len = RSTRING_LEN(hp->cont);
235
+ if (cont_len > 0) {
209
236
  --hp->mark;
210
-
237
+ len = LEN(mark, p);
238
+ }
211
239
  vptr = PTR_TO(mark);
212
240
 
213
- if (RSTRING_LEN(hp->cont) > 0) {
241
+ /* normalize tab to space */
242
+ if (cont_len > 0) {
214
243
  assert((' ' == *vptr || '\t' == *vptr) && "invalid leading white space");
215
244
  *vptr = ' ';
216
245
  }
217
- rb_str_buf_cat(hp->cont, vptr, LEN(mark, p));
246
+
247
+ for (end = len - 1; end >= 0 && is_lws(vptr[end]); end--);
248
+ rb_str_buf_cat(hp->cont, vptr, end + 1);
218
249
  }
219
250
 
220
251
  static void write_value(struct http_parser *hp,
@@ -225,7 +256,7 @@ static void write_value(struct http_parser *hp,
225
256
  VALUE e;
226
257
 
227
258
  VALIDATE_MAX_LENGTH(LEN(mark, p), FIELD_VALUE);
228
- v = LEN(mark, p) == 0 ? rb_str_buf_new(128) : STR_NEW(mark, p);
259
+ v = LEN(mark, p) == 0 ? rb_str_buf_new(128) : STRIPPED_STR_NEW(mark, p);
229
260
  if (NIL_P(f)) {
230
261
  const char *field = PTR_TO(start.field);
231
262
  size_t flen = hp->s.field_len;
@@ -246,7 +277,7 @@ static void write_value(struct http_parser *hp,
246
277
  } else if (f == g_content_length) {
247
278
  hp->len.content = parse_length(RSTRING_PTR(v), RSTRING_LEN(v));
248
279
  if (hp->len.content < 0)
249
- parser_error("invalid Content-Length");
280
+ parser_raise(eHttpParserError, "invalid Content-Length");
250
281
  if (hp->len.content != 0)
251
282
  HP_FL_SET(hp, HASBODY);
252
283
  hp_invalid_if_trailer(hp);
@@ -301,7 +332,7 @@ static void write_value(struct http_parser *hp,
301
332
  action request_uri {
302
333
  VALUE str;
303
334
 
304
- VALIDATE_MAX_LENGTH(LEN(mark, fpc), REQUEST_URI);
335
+ VALIDATE_MAX_URI_LENGTH(LEN(mark, fpc), REQUEST_URI);
305
336
  str = rb_hash_aset(hp->env, g_request_uri, STR_NEW(mark, fpc));
306
337
  /*
307
338
  * "OPTIONS * HTTP/1.1\r\n" is a valid request, but we can't have '*'
@@ -314,19 +345,19 @@ static void write_value(struct http_parser *hp,
314
345
  }
315
346
  }
316
347
  action fragment {
317
- VALIDATE_MAX_LENGTH(LEN(mark, fpc), FRAGMENT);
348
+ VALIDATE_MAX_URI_LENGTH(LEN(mark, fpc), FRAGMENT);
318
349
  rb_hash_aset(hp->env, g_fragment, STR_NEW(mark, fpc));
319
350
  }
320
351
  action start_query {MARK(start.query, fpc); }
321
352
  action query_string {
322
- VALIDATE_MAX_LENGTH(LEN(start.query, fpc), QUERY_STRING);
353
+ VALIDATE_MAX_URI_LENGTH(LEN(start.query, fpc), QUERY_STRING);
323
354
  rb_hash_aset(hp->env, g_query_string, STR_NEW(start.query, fpc));
324
355
  }
325
356
  action http_version { http_version(hp, PTR_TO(mark), LEN(mark, fpc)); }
326
357
  action request_path {
327
358
  VALUE val;
328
359
 
329
- VALIDATE_MAX_LENGTH(LEN(mark, fpc), REQUEST_PATH);
360
+ VALIDATE_MAX_URI_LENGTH(LEN(mark, fpc), REQUEST_PATH);
330
361
  val = rb_hash_aset(hp->env, g_request_path, STR_NEW(mark, fpc));
331
362
 
332
363
  /* rack says PATH_INFO must start with "/" or be empty */
@@ -336,7 +367,7 @@ static void write_value(struct http_parser *hp,
336
367
  action add_to_chunk_size {
337
368
  hp->len.chunk = step_incr(hp->len.chunk, fc, 16);
338
369
  if (hp->len.chunk < 0)
339
- parser_error("invalid chunk size");
370
+ parser_raise(eHttpParserError, "invalid chunk size");
340
371
  }
341
372
  action header_done {
342
373
  finalize_header(hp);
@@ -598,6 +629,34 @@ static VALUE HttpParser_clear(VALUE self)
598
629
  return self;
599
630
  }
600
631
 
632
+ /**
633
+ * call-seq:
634
+ * parser.dechunk! => parser
635
+ *
636
+ * Resets the parser to a state suitable for dechunking response bodies
637
+ *
638
+ */
639
+ static VALUE HttpParser_dechunk_bang(VALUE self)
640
+ {
641
+ struct http_parser *hp = data_get(self);
642
+
643
+ http_parser_init(hp);
644
+
645
+ /*
646
+ * we don't care about trailers in dechunk-only mode,
647
+ * but if we did we'd set UH_FL_HASTRAILER and clear hp->env
648
+ */
649
+ if (0) {
650
+ rb_funcall(hp->env, id_clear, 0);
651
+ hp->flags = UH_FL_HASTRAILER;
652
+ }
653
+
654
+ hp->flags |= UH_FL_HASBODY | UH_FL_INBODY | UH_FL_CHUNKED;
655
+ hp->cs = http_parser_en_ChunkedBody;
656
+
657
+ return self;
658
+ }
659
+
601
660
  /**
602
661
  * call-seq:
603
662
  * parser.reset => nil
@@ -677,7 +736,8 @@ static VALUE HttpParser_parse(VALUE self)
677
736
  }
678
737
 
679
738
  http_parser_execute(hp, RSTRING_PTR(data), RSTRING_LEN(data));
680
- VALIDATE_MAX_LENGTH(hp->offset, HEADER);
739
+ if (hp->offset > MAX_HEADER_LEN)
740
+ parser_raise(e413, "HTTP header is too large");
681
741
 
682
742
  if (hp->cs == http_parser_first_final ||
683
743
  hp->cs == http_parser_en_ChunkedBody) {
@@ -690,11 +750,32 @@ static VALUE HttpParser_parse(VALUE self)
690
750
  }
691
751
 
692
752
  if (hp->cs == http_parser_error)
693
- parser_error("Invalid HTTP format, parsing fails.");
753
+ parser_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
694
754
 
695
755
  return Qnil;
696
756
  }
697
757
 
758
+ /**
759
+ * Document-method: parse
760
+ * call-seq:
761
+ * parser.add_parse(buffer) => env or nil
762
+ *
763
+ * adds the contents of +buffer+ to the internal buffer and attempts to
764
+ * continue parsing. Returns the +env+ Hash on success or nil if more
765
+ * data is needed.
766
+ *
767
+ * Raises HttpParserError if there are parsing errors.
768
+ */
769
+ static VALUE HttpParser_add_parse(VALUE self, VALUE buffer)
770
+ {
771
+ struct http_parser *hp = data_get(self);
772
+
773
+ Check_Type(buffer, T_STRING);
774
+ rb_str_buf_append(hp->buf, buffer);
775
+
776
+ return HttpParser_parse(self);
777
+ }
778
+
698
779
  /**
699
780
  * Document-method: trailers
700
781
  * call-seq:
@@ -803,71 +884,80 @@ static VALUE HttpParser_env(VALUE self)
803
884
 
804
885
  /**
805
886
  * call-seq:
806
- * parser.filter_body(buf, data) => nil/data
887
+ * parser.filter_body(dst, src) => nil/src
807
888
  *
808
- * Takes a String of +data+, will modify data if dechunking is done.
889
+ * Takes a String of +src+, will modify data if dechunking is done.
809
890
  * Returns +nil+ if there is more data left to process. Returns
810
- * +data+ if body processing is complete. When returning +data+,
811
- * it may modify +data+ so the start of the string points to where
891
+ * +src+ if body processing is complete. When returning +src+,
892
+ * it may modify +src+ so the start of the string points to where
812
893
  * the body ended so that trailer processing can begin.
813
894
  *
814
895
  * Raises HttpParserError if there are dechunking errors.
815
- * Basically this is a glorified memcpy(3) that copies +data+
896
+ * Basically this is a glorified memcpy(3) that copies +src+
816
897
  * into +buf+ while filtering it through the dechunker.
817
898
  */
818
- static VALUE HttpParser_filter_body(VALUE self, VALUE buf, VALUE data)
899
+ static VALUE HttpParser_filter_body(VALUE self, VALUE dst, VALUE src)
819
900
  {
820
901
  struct http_parser *hp = data_get(self);
821
- char *dptr;
822
- long dlen;
902
+ char *srcptr;
903
+ long srclen;
823
904
 
824
- dptr = RSTRING_PTR(data);
825
- dlen = RSTRING_LEN(data);
905
+ srcptr = RSTRING_PTR(src);
906
+ srclen = RSTRING_LEN(src);
826
907
 
827
- StringValue(buf);
828
- rb_str_resize(buf, dlen); /* we can never copy more than dlen bytes */
829
- OBJ_TAINT(buf); /* keep weirdo $SAFE users happy */
908
+ StringValue(dst);
830
909
 
831
910
  if (HP_FL_TEST(hp, CHUNKED)) {
832
911
  if (!chunked_eof(hp)) {
912
+ rb_str_modify(dst);
913
+ rb_str_resize(dst, srclen); /* we can never copy more than srclen bytes */
914
+
833
915
  hp->s.dest_offset = 0;
834
- hp->cont = buf;
835
- hp->buf = data;
836
- http_parser_execute(hp, dptr, dlen);
916
+ hp->cont = dst;
917
+ hp->buf = src;
918
+ http_parser_execute(hp, srcptr, srclen);
837
919
  if (hp->cs == http_parser_error)
838
- parser_error("Invalid HTTP format, parsing fails.");
920
+ parser_raise(eHttpParserError, "Invalid HTTP format, parsing fails.");
839
921
 
840
922
  assert(hp->s.dest_offset <= hp->offset &&
841
923
  "destination buffer overflow");
842
- advance_str(data, hp->offset);
843
- rb_str_set_len(buf, hp->s.dest_offset);
924
+ advance_str(src, hp->offset);
925
+ rb_str_set_len(dst, hp->s.dest_offset);
844
926
 
845
- if (RSTRING_LEN(buf) == 0 && chunked_eof(hp)) {
927
+ if (RSTRING_LEN(dst) == 0 && chunked_eof(hp)) {
846
928
  assert(hp->len.chunk == 0 && "chunk at EOF but more to parse");
847
929
  } else {
848
- data = Qnil;
930
+ src = Qnil;
849
931
  }
850
932
  }
851
933
  } else {
852
934
  /* no need to enter the Ragel machine for unchunked transfers */
853
935
  assert(hp->len.content >= 0 && "negative Content-Length");
854
936
  if (hp->len.content > 0) {
855
- long nr = MIN(dlen, hp->len.content);
856
-
857
- hp->buf = data;
858
- memcpy(RSTRING_PTR(buf), dptr, nr);
937
+ long nr = MIN(srclen, hp->len.content);
938
+
939
+ rb_str_modify(dst);
940
+ rb_str_resize(dst, nr);
941
+ /*
942
+ * using rb_str_replace() to avoid memcpy() doesn't help in
943
+ * most cases because a GC-aware programmer will pass an explicit
944
+ * buffer to env["rack.input"].read and reuse the buffer in a loop.
945
+ * This causes copy-on-write behavior to be triggered anyways
946
+ * when the +src+ buffer is modified (when reading off the socket).
947
+ */
948
+ hp->buf = src;
949
+ memcpy(RSTRING_PTR(dst), srcptr, nr);
859
950
  hp->len.content -= nr;
860
951
  if (hp->len.content == 0) {
861
952
  HP_FL_SET(hp, REQEOF);
862
953
  hp->cs = http_parser_first_final;
863
954
  }
864
- advance_str(data, nr);
865
- rb_str_set_len(buf, nr);
866
- data = Qnil;
955
+ advance_str(src, nr);
956
+ src = Qnil;
867
957
  }
868
958
  }
869
959
  hp->offset = 0; /* for trailer parsing */
870
- return data;
960
+ return src;
871
961
  }
872
962
 
873
963
  #define SET_GLOBAL(var,str) do { \
@@ -883,13 +973,19 @@ void Init_unicorn_http(void)
883
973
  cHttpParser = rb_define_class_under(mUnicorn, "HttpParser", rb_cObject);
884
974
  eHttpParserError =
885
975
  rb_define_class_under(mUnicorn, "HttpParserError", rb_eIOError);
976
+ e413 = rb_define_class_under(mUnicorn, "RequestEntityTooLargeError",
977
+ eHttpParserError);
978
+ e414 = rb_define_class_under(mUnicorn, "RequestURITooLongError",
979
+ eHttpParserError);
886
980
 
887
981
  init_globals();
888
982
  rb_define_alloc_func(cHttpParser, HttpParser_alloc);
889
983
  rb_define_method(cHttpParser, "initialize", HttpParser_init, 0);
890
984
  rb_define_method(cHttpParser, "clear", HttpParser_clear, 0);
891
985
  rb_define_method(cHttpParser, "reset", HttpParser_reset, 0);
986
+ rb_define_method(cHttpParser, "dechunk!", HttpParser_dechunk_bang, 0);
892
987
  rb_define_method(cHttpParser, "parse", HttpParser_parse, 0);
988
+ rb_define_method(cHttpParser, "add_parse", HttpParser_add_parse, 1);
893
989
  rb_define_method(cHttpParser, "headers", HttpParser_headers, 2);
894
990
  rb_define_method(cHttpParser, "trailers", HttpParser_headers, 2);
895
991
  rb_define_method(cHttpParser, "filter_body", HttpParser_filter_body, 2);
@@ -924,6 +1020,7 @@ void Init_unicorn_http(void)
924
1020
  rb_define_singleton_method(cHttpParser, "keepalive_requests=", set_ka_req, 1);
925
1021
  rb_define_singleton_method(cHttpParser, "trust_x_forwarded=", set_xftrust, 1);
926
1022
  rb_define_singleton_method(cHttpParser, "trust_x_forwarded?", xftrust, 0);
1023
+ rb_define_singleton_method(cHttpParser, "max_header_len=", set_maxhdrlen, 1);
927
1024
 
928
1025
  init_common_fields();
929
1026
  SET_GLOBAL(g_http_host, "HOST");
@@ -932,6 +1029,7 @@ void Init_unicorn_http(void)
932
1029
  SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
933
1030
  SET_GLOBAL(g_http_connection, "CONNECTION");
934
1031
  id_clear = rb_intern("clear");
1032
+ id_set_backtrace = rb_intern("set_backtrace");
935
1033
  init_unicorn_httpdate();
936
1034
  }
937
1035
  #undef SET_GLOBAL
@@ -8,6 +8,8 @@ require 'logger'
8
8
  # example configuration files. An example config file for use with
9
9
  # nginx is also available at
10
10
  # http://unicorn.bogomips.org/examples/nginx.conf
11
+ #
12
+ # See the link:/TUNING.html document for more information on tuning unicorn.
11
13
  class Unicorn::Configurator
12
14
  include Unicorn
13
15
 
@@ -253,24 +255,50 @@ class Unicorn::Configurator
253
255
  #
254
256
  # [:tcp_nodelay => true or false]
255
257
  #
256
- # Disables Nagle's algorithm on TCP sockets if +true+
258
+ # Disables Nagle's algorithm on TCP sockets if +true+.
259
+ #
260
+ # Setting this to +true+ can make streaming responses in Rails 3.1
261
+ # appear more quickly at the cost of slightly higher bandwidth usage.
262
+ # The effect of this option is most visible if nginx is not used,
263
+ # but nginx remains highly recommended with \Unicorn.
257
264
  #
258
265
  # This has no effect on UNIX sockets.
259
266
  #
260
- # Default: operating system defaults (usually Nagle's algorithm enabled)
267
+ # Default: +true+ (Nagle's algorithm disabled) in \Unicorn,
268
+ # +true+ in Rainbows! This defaulted to +false+ in \Unicorn
269
+ # 3.x
261
270
  #
262
271
  # [:tcp_nopush => true or false]
263
272
  #
264
273
  # Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
265
274
  #
266
- # This is enabled by default as of Unicorn 3.4. This prevents partial
267
- # TCP frames from being sent out and reduces wakeups in nginx if it is
268
- # on a different machine. Since Unicorn is only designed for applications
269
- # that send the response body quickly without keepalive, sockets will
270
- # always be flushed on close to prevent delays.
275
+ # This prevents partial TCP frames from being sent out and reduces
276
+ # wakeups in nginx if it is on a different machine. Since \Unicorn
277
+ # is only designed for applications that send the response body
278
+ # quickly without keepalive, sockets will always be flushed on close
279
+ # to prevent delays.
271
280
  #
272
281
  # This has no effect on UNIX sockets.
273
282
  #
283
+ # Default: +false+
284
+ # This defaulted to +true+ in \Unicorn 3.4 - 3.7
285
+ #
286
+ # [:ipv6only => true or false]
287
+ #
288
+ # This option makes IPv6-capable TCP listeners IPv6-only and unable
289
+ # to receive IPv4 queries on dual-stack systems. A separate IPv4-only
290
+ # listener is required if this is true.
291
+ #
292
+ # This option is only available for Ruby 1.9.2 and later.
293
+ #
294
+ # Enabling this option for the IPv6-only listener and having a
295
+ # separate IPv4 listener is recommended if you wish to support IPv6
296
+ # on the same TCP port. Otherwise, the value of \env[\"REMOTE_ADDR\"]
297
+ # will appear as an ugly IPv4-mapped-IPv6 address for IPv4 clients
298
+ # (e.g ":ffff:10.0.0.1" instead of just "10.0.0.1").
299
+ #
300
+ # Default: Operating-system dependent
301
+ #
274
302
  # [:tries => Integer]
275
303
  #
276
304
  # Times to retry binding a socket if it is already in use
@@ -348,7 +376,7 @@ class Unicorn::Configurator
348
376
  Integer === value or
349
377
  raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
350
378
  end
351
- [ :tcp_nodelay, :tcp_nopush ].each do |key|
379
+ [ :tcp_nodelay, :tcp_nopush, :ipv6only ].each do |key|
352
380
  (value = options[key]).nil? and next
353
381
  TrueClass === value || FalseClass === value or
354
382
  raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
data/lib/unicorn/const.rb CHANGED
@@ -8,8 +8,8 @@
8
8
  # improve things much compared to constants.
9
9
  module Unicorn::Const
10
10
 
11
- # The current version of Unicorn, currently 3.4.0
12
- UNICORN_VERSION = "3.4.0"
11
+ # The current version of Unicorn, currently 4.0.0
12
+ UNICORN_VERSION = "4.0.0"
13
13
 
14
14
  # default TCP listen host address (0.0.0.0, all interfaces)
15
15
  DEFAULT_HOST = "0.0.0.0"
@@ -25,12 +25,14 @@ module Unicorn::Const
25
25
 
26
26
  # Maximum request body size before it is moved out of memory and into a
27
27
  # temporary file for reading (112 kilobytes). This is the default
28
- # value of of client_body_buffer_size.
28
+ # value of client_body_buffer_size.
29
29
  MAX_BODY = 1024 * 112
30
30
 
31
31
  # :stopdoc:
32
32
  # common errors we'll send back
33
33
  ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
34
+ ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
35
+ ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
34
36
  ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
35
37
  EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
36
38
 
@@ -68,9 +68,7 @@ class Unicorn::HttpParser
68
68
  if parse.nil?
69
69
  # Parser is not done, queue up more data to read and continue parsing
70
70
  # an Exception thrown from the parser will throw us out of the loop
71
- begin
72
- buf << socket.kgio_read!(16384)
73
- end while parse.nil?
71
+ false until add_parse(socket.kgio_read!(16384))
74
72
  end
75
73
  e[RACK_INPUT] = 0 == content_length ?
76
74
  NULL_IO : @@input_class.new(socket, self)