unicorn 3.6.0 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -0
- data/.manifest +13 -0
- data/ChangeLog +783 -1
- data/DESIGN +0 -8
- data/Documentation/GNUmakefile +1 -1
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +2 -2
- data/HACKING +11 -0
- data/KNOWN_ISSUES +2 -2
- data/LATEST +24 -24
- data/Links +53 -0
- data/NEWS +66 -0
- data/PHILOSOPHY +49 -49
- data/Sandbox +13 -4
- data/TODO +0 -2
- data/TUNING +31 -9
- data/bin/unicorn +2 -1
- data/bin/unicorn_rails +2 -1
- data/examples/big_app_gc.rb +2 -33
- data/examples/nginx.conf +17 -4
- data/ext/unicorn_http/ext_help.h +16 -0
- data/ext/unicorn_http/extconf.rb +1 -0
- data/ext/unicorn_http/global_variables.h +9 -3
- data/ext/unicorn_http/unicorn_http.c +357 -259
- data/ext/unicorn_http/unicorn_http.rl +148 -50
- data/lib/unicorn/configurator.rb +36 -8
- data/lib/unicorn/const.rb +5 -3
- data/lib/unicorn/http_request.rb +1 -3
- data/lib/unicorn/http_server.rb +82 -95
- data/lib/unicorn/oob_gc.rb +61 -50
- data/lib/unicorn/socket_helper.rb +23 -8
- data/lib/unicorn/worker.rb +45 -4
- data/lib/unicorn.rb +8 -6
- data/script/isolate_for_tests +4 -2
- data/t/broken-app.ru +12 -0
- data/t/heartbeat-timeout.ru +12 -0
- data/t/oob_gc.ru +21 -0
- data/t/oob_gc_path.ru +21 -0
- data/t/t0001-reload-bad-config.sh +1 -0
- data/t/t0002-parser-error.sh +64 -1
- data/t/t0004-heartbeat-timeout.sh +69 -0
- data/t/t0009-broken-app.sh +56 -0
- data/t/t0019-max_header_len.sh +49 -0
- data/t/t0020-at_exit-handler.sh +49 -0
- data/t/t9001-oob_gc.sh +47 -0
- data/t/t9002-oob_gc-path.sh +75 -0
- data/test/benchmark/stack.ru +8 -0
- data/test/unit/test_droplet.rb +28 -0
- data/test/unit/test_http_parser.rb +60 -4
- data/test/unit/test_http_parser_ng.rb +54 -0
- data/test/unit/test_response.rb +1 -1
- data/test/unit/test_server.rb +1 -1
- data/test/unit/test_signals.rb +1 -1
- data/test/unit/test_socket_helper.rb +8 -0
- data/test/unit/test_upload.rb +1 -1
- data/unicorn.gemspec +3 -2
- 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
|
121
|
+
static void parser_raise(VALUE klass, const char *msg)
|
114
122
|
{
|
115
|
-
VALUE exc = rb_exc_new2(
|
123
|
+
VALUE exc = rb_exc_new2(klass, msg);
|
116
124
|
VALUE bt = rb_ary_new();
|
117
125
|
|
118
|
-
|
119
|
-
|
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
|
-
|
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
|
-
|
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 (
|
231
|
+
if (len == 0)
|
206
232
|
return;
|
207
233
|
|
208
|
-
|
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
|
-
|
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
|
-
|
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) :
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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(
|
887
|
+
* parser.filter_body(dst, src) => nil/src
|
807
888
|
*
|
808
|
-
* Takes a String of +
|
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
|
-
* +
|
811
|
-
* it may modify +
|
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 +
|
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
|
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 *
|
822
|
-
long
|
902
|
+
char *srcptr;
|
903
|
+
long srclen;
|
823
904
|
|
824
|
-
|
825
|
-
|
905
|
+
srcptr = RSTRING_PTR(src);
|
906
|
+
srclen = RSTRING_LEN(src);
|
826
907
|
|
827
|
-
StringValue(
|
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 =
|
835
|
-
hp->buf =
|
836
|
-
http_parser_execute(hp,
|
916
|
+
hp->cont = dst;
|
917
|
+
hp->buf = src;
|
918
|
+
http_parser_execute(hp, srcptr, srclen);
|
837
919
|
if (hp->cs == http_parser_error)
|
838
|
-
|
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(
|
843
|
-
rb_str_set_len(
|
924
|
+
advance_str(src, hp->offset);
|
925
|
+
rb_str_set_len(dst, hp->s.dest_offset);
|
844
926
|
|
845
|
-
if (RSTRING_LEN(
|
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
|
-
|
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(
|
856
|
-
|
857
|
-
|
858
|
-
|
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(
|
865
|
-
|
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
|
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
|
data/lib/unicorn/configurator.rb
CHANGED
@@ -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:
|
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
|
267
|
-
#
|
268
|
-
#
|
269
|
-
#
|
270
|
-
#
|
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
|
12
|
-
UNICORN_VERSION = "
|
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
|
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
|
|
data/lib/unicorn/http_request.rb
CHANGED
@@ -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
|
-
|
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)
|