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.
- checksums.yaml +7 -0
- data/.document +0 -1
- data/.gitattributes +5 -0
- data/.gitignore +2 -2
- data/.manifest +17 -21
- data/.olddoc.yml +25 -0
- data/Application_Timeouts +7 -7
- data/CONTRIBUTORS +6 -2
- data/DESIGN +2 -4
- data/Documentation/.gitignore +1 -3
- data/Documentation/unicorn.1 +222 -0
- data/Documentation/unicorn_rails.1 +207 -0
- data/FAQ +23 -6
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +138 -92
- data/HACKING +10 -32
- data/ISSUES +80 -19
- data/KNOWN_ISSUES +18 -18
- data/LATEST +23 -27
- data/LICENSE +2 -2
- data/Links +24 -22
- data/NEWS +823 -0
- data/PHILOSOPHY +0 -6
- data/README +49 -48
- data/Rakefile +0 -44
- data/SIGNALS +2 -2
- data/Sandbox +11 -10
- data/TODO +0 -2
- data/TUNING +30 -9
- data/archive/.gitignore +3 -0
- data/archive/slrnpull.conf +4 -0
- data/bin/unicorn +4 -2
- data/bin/unicorn_rails +3 -3
- data/examples/big_app_gc.rb +1 -1
- data/examples/init.sh +36 -8
- data/examples/logrotate.conf +17 -2
- data/examples/nginx.conf +14 -14
- data/examples/unicorn.conf.minimal.rb +2 -2
- data/examples/unicorn.conf.rb +14 -6
- data/examples/unicorn.socket +11 -0
- data/examples/unicorn@.service +40 -0
- data/ext/unicorn_http/c_util.h +5 -13
- data/ext/unicorn_http/common_field_optimization.h +22 -5
- data/ext/unicorn_http/epollexclusive.h +124 -0
- data/ext/unicorn_http/ext_help.h +0 -44
- data/ext/unicorn_http/extconf.rb +32 -5
- data/ext/unicorn_http/global_variables.h +2 -2
- data/ext/unicorn_http/httpdate.c +3 -2
- data/ext/unicorn_http/unicorn_http.c +926 -638
- data/ext/unicorn_http/unicorn_http.rl +159 -170
- data/ext/unicorn_http/unicorn_http_common.rl +1 -1
- data/lib/unicorn/configurator.rb +110 -46
- data/lib/unicorn/const.rb +2 -25
- data/lib/unicorn/http_request.rb +110 -31
- data/lib/unicorn/http_response.rb +17 -31
- data/lib/unicorn/http_server.rb +272 -180
- data/lib/unicorn/launcher.rb +1 -1
- data/lib/unicorn/oob_gc.rb +16 -6
- data/lib/unicorn/select_waiter.rb +6 -0
- data/lib/unicorn/socket_helper.rb +58 -78
- data/lib/unicorn/stream_input.rb +8 -7
- data/lib/unicorn/tee_input.rb +16 -11
- data/lib/unicorn/tmpio.rb +10 -6
- data/lib/unicorn/util.rb +5 -4
- data/lib/unicorn/version.rb +1 -1
- data/lib/unicorn/worker.rb +36 -23
- data/lib/unicorn.rb +64 -44
- data/man/man1/unicorn.1 +124 -122
- data/man/man1/unicorn_rails.1 +113 -127
- data/t/.gitignore +0 -1
- data/t/GNUmakefile +3 -80
- data/t/README +4 -4
- data/t/t0002-parser-error.sh +3 -3
- data/t/t0011-active-unix-socket.sh +1 -1
- data/t/t0012-reload-empty-config.sh +2 -1
- data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
- data/t/t0301.ru +13 -0
- data/t/test-lib.sh +4 -3
- data/test/benchmark/README +14 -4
- data/test/benchmark/ddstream.ru +50 -0
- data/test/benchmark/readinput.ru +40 -0
- data/test/benchmark/uconnect.perl +66 -0
- data/test/exec/test_exec.rb +74 -20
- data/test/test_helper.rb +42 -33
- data/test/unit/test_ccc.rb +91 -0
- data/test/unit/test_droplet.rb +1 -1
- data/test/unit/test_http_parser.rb +49 -19
- data/test/unit/test_http_parser_ng.rb +98 -115
- data/test/unit/test_request.rb +11 -11
- data/test/unit/test_response.rb +31 -19
- data/test/unit/test_server.rb +89 -15
- data/test/unit/test_signals.rb +9 -9
- data/test/unit/test_socket_helper.rb +16 -12
- data/test/unit/test_tee_input.rb +10 -0
- data/test/unit/test_upload.rb +10 -15
- data/test/unit/test_util.rb +32 -6
- data/test/unit/test_waiter.rb +34 -0
- data/unicorn.gemspec +28 -23
- data/unicorn_1 +1 -0
- data/unicorn_rails_1 +1 -0
- metadata +68 -134
- data/.wrongdoc.yml +0 -10
- data/ChangeLog +0 -4861
- data/Documentation/GNUmakefile +0 -30
- data/Documentation/unicorn.1.txt +0 -178
- data/Documentation/unicorn_rails.1.txt +0 -175
- data/examples/git.ru +0 -13
- data/lib/unicorn/app/exec_cgi.rb +0 -154
- data/lib/unicorn/app/inetd.rb +0 -109
- data/lib/unicorn/ssl_client.rb +0 -11
- data/lib/unicorn/ssl_configurator.rb +0 -104
- data/lib/unicorn/ssl_server.rb +0 -42
- data/local.mk.sample +0 -59
- data/script/isolate_for_tests +0 -32
- data/t/hijack.ru +0 -42
- data/t/sslgen.sh +0 -71
- data/t/t0016-trust-x-forwarded-false.sh +0 -30
- data/t/t0017-trust-x-forwarded-true.sh +0 -30
- data/t/t0200-rack-hijack.sh +0 -27
- data/t/t0600-https-server-basic.sh +0 -48
- data/test/unit/test_http_parser_xftrust.rb +0 -38
- data/test/unit/test_sni_hostnames.rb +0 -47
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
2
|
|
|
3
|
-
require 'test/test_helper'
|
|
3
|
+
require './test/test_helper'
|
|
4
4
|
require 'digest/md5'
|
|
5
5
|
|
|
6
6
|
include Unicorn
|
|
@@ -8,10 +8,29 @@ include Unicorn
|
|
|
8
8
|
class HttpParserNgTest < Test::Unit::TestCase
|
|
9
9
|
|
|
10
10
|
def setup
|
|
11
|
-
HttpParser.keepalive_requests = HttpParser::KEEPALIVE_REQUESTS_DEFAULT
|
|
12
11
|
@parser = HttpParser.new
|
|
13
12
|
end
|
|
14
13
|
|
|
14
|
+
# RFC 7230 allows gzip/deflate/compress Transfer-Encoding,
|
|
15
|
+
# but "chunked" must be last if used
|
|
16
|
+
def test_is_chunked
|
|
17
|
+
[ 'chunked,chunked', 'chunked,gzip', 'chunked,gzip,chunked' ].each do |x|
|
|
18
|
+
assert_raise(HttpParserError) { HttpParser.is_chunked?(x) }
|
|
19
|
+
end
|
|
20
|
+
[ 'gzip, chunked', 'gzip,chunked', 'gzip ,chunked' ].each do |x|
|
|
21
|
+
assert HttpParser.is_chunked?(x)
|
|
22
|
+
end
|
|
23
|
+
[ 'gzip', 'xhunked', 'xchunked' ].each do |x|
|
|
24
|
+
assert !HttpParser.is_chunked?(x)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_parser_max_len
|
|
29
|
+
assert_raises(RangeError) do
|
|
30
|
+
HttpParser.max_header_len = 0xffffffff + 1
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
15
34
|
def test_next_clear
|
|
16
35
|
r = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
|
|
17
36
|
@parser.buf << r
|
|
@@ -29,23 +48,15 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
|
29
48
|
assert_equal false, @parser.response_start_sent
|
|
30
49
|
end
|
|
31
50
|
|
|
32
|
-
def
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
assert HttpParser.keepalive_requests >= 0xffffffff
|
|
42
|
-
HttpParser.keepalive_requests = 1
|
|
43
|
-
assert_equal 1, HttpParser.keepalive_requests
|
|
44
|
-
HttpParser.keepalive_requests = 666
|
|
45
|
-
assert_equal 666, HttpParser.keepalive_requests
|
|
46
|
-
|
|
47
|
-
assert_raises(TypeError) { HttpParser.keepalive_requests = "666" }
|
|
48
|
-
assert_raises(TypeError) { HttpParser.keepalive_requests = [] }
|
|
51
|
+
def test_response_start_sent
|
|
52
|
+
assert_equal false, @parser.response_start_sent, "default is false"
|
|
53
|
+
@parser.response_start_sent = true
|
|
54
|
+
assert_equal true, @parser.response_start_sent
|
|
55
|
+
@parser.response_start_sent = false
|
|
56
|
+
assert_equal false, @parser.response_start_sent
|
|
57
|
+
@parser.response_start_sent = true
|
|
58
|
+
@parser.clear
|
|
59
|
+
assert_equal false, @parser.response_start_sent
|
|
49
60
|
end
|
|
50
61
|
|
|
51
62
|
def test_connection_TE
|
|
@@ -71,41 +82,11 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
|
71
82
|
"REQUEST_METHOD" => "GET",
|
|
72
83
|
"QUERY_STRING" => ""
|
|
73
84
|
}.freeze
|
|
74
|
-
|
|
85
|
+
100.times do |nr|
|
|
75
86
|
@parser.buf << req
|
|
76
87
|
assert_equal expect, @parser.parse
|
|
77
88
|
assert @parser.next?
|
|
78
89
|
end
|
|
79
|
-
@parser.buf << req
|
|
80
|
-
assert_equal expect, @parser.parse
|
|
81
|
-
assert ! @parser.next?
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def test_fewer_keepalive_requests_with_next?
|
|
85
|
-
HttpParser.keepalive_requests = 5
|
|
86
|
-
@parser = HttpParser.new
|
|
87
|
-
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
|
88
|
-
expect = {
|
|
89
|
-
"SERVER_NAME" => "example.com",
|
|
90
|
-
"HTTP_HOST" => "example.com",
|
|
91
|
-
"rack.url_scheme" => "http",
|
|
92
|
-
"REQUEST_PATH" => "/",
|
|
93
|
-
"SERVER_PROTOCOL" => "HTTP/1.1",
|
|
94
|
-
"PATH_INFO" => "/",
|
|
95
|
-
"HTTP_VERSION" => "HTTP/1.1",
|
|
96
|
-
"REQUEST_URI" => "/",
|
|
97
|
-
"SERVER_PORT" => "80",
|
|
98
|
-
"REQUEST_METHOD" => "GET",
|
|
99
|
-
"QUERY_STRING" => ""
|
|
100
|
-
}.freeze
|
|
101
|
-
5.times do |nr|
|
|
102
|
-
@parser.buf << req
|
|
103
|
-
assert_equal expect, @parser.parse
|
|
104
|
-
assert @parser.next?
|
|
105
|
-
end
|
|
106
|
-
@parser.buf << req
|
|
107
|
-
assert_equal expect, @parser.parse
|
|
108
|
-
assert ! @parser.next?
|
|
109
90
|
end
|
|
110
91
|
|
|
111
92
|
def test_default_keepalive_is_off
|
|
@@ -599,6 +580,73 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
|
599
580
|
end
|
|
600
581
|
end
|
|
601
582
|
|
|
583
|
+
def test_duplicate_content_length
|
|
584
|
+
str = "PUT / HTTP/1.1\r\n" \
|
|
585
|
+
"Content-Length: 1\r\n" \
|
|
586
|
+
"Content-Length: 9\r\n" \
|
|
587
|
+
"\r\n"
|
|
588
|
+
assert_raises(HttpParserError) { @parser.headers({}, str) }
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
def test_chunked_overrides_content_length
|
|
592
|
+
order = [ 'Transfer-Encoding: chunked', 'Content-Length: 666' ]
|
|
593
|
+
%w(a b).each do |x|
|
|
594
|
+
str = "PUT /#{x} HTTP/1.1\r\n" \
|
|
595
|
+
"#{order.join("\r\n")}" \
|
|
596
|
+
"\r\n\r\na\r\nhelloworld\r\n0\r\n\r\n"
|
|
597
|
+
order.reverse!
|
|
598
|
+
env = @parser.headers({}, str)
|
|
599
|
+
assert_nil @parser.content_length
|
|
600
|
+
assert_equal 'chunked', env['HTTP_TRANSFER_ENCODING']
|
|
601
|
+
assert_equal '666', env['CONTENT_LENGTH'],
|
|
602
|
+
'Content-Length logged so the app can log a possible client bug/attack'
|
|
603
|
+
@parser.filter_body(dst = '', str)
|
|
604
|
+
assert_equal 'helloworld', dst
|
|
605
|
+
@parser.parse # handle the non-existent trailer
|
|
606
|
+
assert @parser.next?
|
|
607
|
+
end
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
def test_chunked_order_good
|
|
611
|
+
str = "PUT /x HTTP/1.1\r\n" \
|
|
612
|
+
"Transfer-Encoding: gzip\r\n" \
|
|
613
|
+
"Transfer-Encoding: chunked\r\n" \
|
|
614
|
+
"\r\n"
|
|
615
|
+
env = @parser.headers({}, str)
|
|
616
|
+
assert_equal 'gzip,chunked', env['HTTP_TRANSFER_ENCODING']
|
|
617
|
+
assert_nil @parser.content_length
|
|
618
|
+
|
|
619
|
+
@parser.clear
|
|
620
|
+
str = "PUT /x HTTP/1.1\r\n" \
|
|
621
|
+
"Transfer-Encoding: gzip, chunked\r\n" \
|
|
622
|
+
"\r\n"
|
|
623
|
+
env = @parser.headers({}, str)
|
|
624
|
+
assert_equal 'gzip, chunked', env['HTTP_TRANSFER_ENCODING']
|
|
625
|
+
assert_nil @parser.content_length
|
|
626
|
+
end
|
|
627
|
+
|
|
628
|
+
def test_chunked_order_bad
|
|
629
|
+
str = "PUT /x HTTP/1.1\r\n" \
|
|
630
|
+
"Transfer-Encoding: chunked\r\n" \
|
|
631
|
+
"Transfer-Encoding: gzip\r\n" \
|
|
632
|
+
"\r\n"
|
|
633
|
+
assert_raise(HttpParserError) { @parser.headers({}, str) }
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
def test_double_chunked
|
|
637
|
+
str = "PUT /x HTTP/1.1\r\n" \
|
|
638
|
+
"Transfer-Encoding: chunked\r\n" \
|
|
639
|
+
"Transfer-Encoding: chunked\r\n" \
|
|
640
|
+
"\r\n"
|
|
641
|
+
assert_raise(HttpParserError) { @parser.headers({}, str) }
|
|
642
|
+
|
|
643
|
+
@parser.clear
|
|
644
|
+
str = "PUT /x HTTP/1.1\r\n" \
|
|
645
|
+
"Transfer-Encoding: chunked,chunked\r\n" \
|
|
646
|
+
"\r\n"
|
|
647
|
+
assert_raise(HttpParserError) { @parser.headers({}, str) }
|
|
648
|
+
end
|
|
649
|
+
|
|
602
650
|
def test_backtrace_is_empty
|
|
603
651
|
begin
|
|
604
652
|
@parser.headers({}, "AAADFSFDSFD\r\n\r\n")
|
|
@@ -663,69 +711,4 @@ class HttpParserNgTest < Test::Unit::TestCase
|
|
|
663
711
|
assert_equal expect, env2
|
|
664
712
|
assert_equal "", @parser.buf
|
|
665
713
|
end
|
|
666
|
-
|
|
667
|
-
def test_keepalive_requests_disabled
|
|
668
|
-
req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n".freeze
|
|
669
|
-
expect = {
|
|
670
|
-
"SERVER_NAME" => "example.com",
|
|
671
|
-
"HTTP_HOST" => "example.com",
|
|
672
|
-
"rack.url_scheme" => "http",
|
|
673
|
-
"REQUEST_PATH" => "/",
|
|
674
|
-
"SERVER_PROTOCOL" => "HTTP/1.1",
|
|
675
|
-
"PATH_INFO" => "/",
|
|
676
|
-
"HTTP_VERSION" => "HTTP/1.1",
|
|
677
|
-
"REQUEST_URI" => "/",
|
|
678
|
-
"SERVER_PORT" => "80",
|
|
679
|
-
"REQUEST_METHOD" => "GET",
|
|
680
|
-
"QUERY_STRING" => ""
|
|
681
|
-
}.freeze
|
|
682
|
-
HttpParser.keepalive_requests = 0
|
|
683
|
-
@parser = HttpParser.new
|
|
684
|
-
@parser.buf << req
|
|
685
|
-
assert_equal expect, @parser.parse
|
|
686
|
-
assert ! @parser.next?
|
|
687
|
-
end
|
|
688
|
-
|
|
689
|
-
def test_chunk_only
|
|
690
|
-
tmp = ""
|
|
691
|
-
assert_equal @parser, @parser.dechunk!
|
|
692
|
-
assert_nil @parser.filter_body(tmp, "6\r\n")
|
|
693
|
-
assert_equal "", tmp
|
|
694
|
-
assert_nil @parser.filter_body(tmp, "abcdef")
|
|
695
|
-
assert_equal "abcdef", tmp
|
|
696
|
-
assert_nil @parser.filter_body(tmp, "\r\n")
|
|
697
|
-
assert_equal "", tmp
|
|
698
|
-
src = "0\r\n\r\n"
|
|
699
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
|
700
|
-
assert_equal "", tmp
|
|
701
|
-
end
|
|
702
|
-
|
|
703
|
-
def test_chunk_only_bad_align
|
|
704
|
-
tmp = ""
|
|
705
|
-
assert_equal @parser, @parser.dechunk!
|
|
706
|
-
assert_nil @parser.filter_body(tmp, "6\r\na")
|
|
707
|
-
assert_equal "a", tmp
|
|
708
|
-
assert_nil @parser.filter_body(tmp, "bcde")
|
|
709
|
-
assert_equal "bcde", tmp
|
|
710
|
-
assert_nil @parser.filter_body(tmp, "f\r")
|
|
711
|
-
assert_equal "f", tmp
|
|
712
|
-
src = "\n0\r\n\r\n"
|
|
713
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
|
714
|
-
assert_equal "", tmp
|
|
715
|
-
end
|
|
716
|
-
|
|
717
|
-
def test_chunk_only_reset_ok
|
|
718
|
-
tmp = ""
|
|
719
|
-
assert_equal @parser, @parser.dechunk!
|
|
720
|
-
src = "1\r\na\r\n0\r\n\r\n"
|
|
721
|
-
assert_nil @parser.filter_body(tmp, src)
|
|
722
|
-
assert_equal "a", tmp
|
|
723
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
|
724
|
-
|
|
725
|
-
assert_equal @parser, @parser.dechunk!
|
|
726
|
-
src = "0\r\n\r\n"
|
|
727
|
-
assert_equal src.object_id, @parser.filter_body(tmp, src).object_id
|
|
728
|
-
assert_equal "", tmp
|
|
729
|
-
assert_equal src, @parser.filter_body(tmp, src)
|
|
730
|
-
end
|
|
731
714
|
end
|
data/test/unit/test_request.rb
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
|
|
5
5
|
# the GPLv2+ (GPLv3+ preferred)
|
|
6
6
|
|
|
7
|
-
require 'test/test_helper'
|
|
7
|
+
require './test/test_helper'
|
|
8
8
|
|
|
9
9
|
include Unicorn
|
|
10
10
|
|
|
@@ -34,7 +34,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
34
34
|
assert_equal '', env['REQUEST_PATH']
|
|
35
35
|
assert_equal '', env['PATH_INFO']
|
|
36
36
|
assert_equal '*', env['REQUEST_URI']
|
|
37
|
-
|
|
37
|
+
assert_kind_of Array, @lint.call(env)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def test_absolute_uri_with_query
|
|
@@ -44,7 +44,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
44
44
|
assert_equal '/x', env['REQUEST_PATH']
|
|
45
45
|
assert_equal '/x', env['PATH_INFO']
|
|
46
46
|
assert_equal 'y=z', env['QUERY_STRING']
|
|
47
|
-
|
|
47
|
+
assert_kind_of Array, @lint.call(env)
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def test_absolute_uri_with_fragment
|
|
@@ -55,7 +55,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
55
55
|
assert_equal '/x', env['PATH_INFO']
|
|
56
56
|
assert_equal '', env['QUERY_STRING']
|
|
57
57
|
assert_equal 'frag', env['FRAGMENT']
|
|
58
|
-
|
|
58
|
+
assert_kind_of Array, @lint.call(env)
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def test_absolute_uri_with_query_and_fragment
|
|
@@ -66,7 +66,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
66
66
|
assert_equal '/x', env['PATH_INFO']
|
|
67
67
|
assert_equal 'a=b', env['QUERY_STRING']
|
|
68
68
|
assert_equal 'frag', env['FRAGMENT']
|
|
69
|
-
|
|
69
|
+
assert_kind_of Array, @lint.call(env)
|
|
70
70
|
end
|
|
71
71
|
|
|
72
72
|
def test_absolute_uri_unsupported_schemes
|
|
@@ -83,7 +83,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
83
83
|
"Host: foo\r\n\r\n")
|
|
84
84
|
env = @request.read(client)
|
|
85
85
|
assert_equal "https", env['rack.url_scheme']
|
|
86
|
-
|
|
86
|
+
assert_kind_of Array, @lint.call(env)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def test_x_forwarded_proto_http
|
|
@@ -92,7 +92,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
92
92
|
"Host: foo\r\n\r\n")
|
|
93
93
|
env = @request.read(client)
|
|
94
94
|
assert_equal "http", env['rack.url_scheme']
|
|
95
|
-
|
|
95
|
+
assert_kind_of Array, @lint.call(env)
|
|
96
96
|
end
|
|
97
97
|
|
|
98
98
|
def test_x_forwarded_proto_invalid
|
|
@@ -101,7 +101,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
101
101
|
"Host: foo\r\n\r\n")
|
|
102
102
|
env = @request.read(client)
|
|
103
103
|
assert_equal "http", env['rack.url_scheme']
|
|
104
|
-
|
|
104
|
+
assert_kind_of Array, @lint.call(env)
|
|
105
105
|
end
|
|
106
106
|
|
|
107
107
|
def test_rack_lint_get
|
|
@@ -109,7 +109,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
109
109
|
env = @request.read(client)
|
|
110
110
|
assert_equal "http", env['rack.url_scheme']
|
|
111
111
|
assert_equal '127.0.0.1', env['REMOTE_ADDR']
|
|
112
|
-
|
|
112
|
+
assert_kind_of Array, @lint.call(env)
|
|
113
113
|
end
|
|
114
114
|
|
|
115
115
|
def test_no_content_stringio
|
|
@@ -143,7 +143,7 @@ class RequestTest < Test::Unit::TestCase
|
|
|
143
143
|
"abcde")
|
|
144
144
|
env = @request.read(client)
|
|
145
145
|
assert ! env.include?(:http_body)
|
|
146
|
-
|
|
146
|
+
assert_kind_of Array, @lint.call(env)
|
|
147
147
|
end
|
|
148
148
|
|
|
149
149
|
def test_rack_lint_big_put
|
|
@@ -177,6 +177,6 @@ class RequestTest < Test::Unit::TestCase
|
|
|
177
177
|
}
|
|
178
178
|
assert_nil env['rack.input'].read(bs)
|
|
179
179
|
env['rack.input'].rewind
|
|
180
|
-
|
|
180
|
+
assert_kind_of Array, @lint.call(env)
|
|
181
181
|
end
|
|
182
182
|
end
|
data/test/unit/test_response.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2005 Zed A. Shaw
|
|
3
|
+
# Copyright (c) 2005 Zed A. Shaw
|
|
4
4
|
# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
|
|
5
5
|
# the GPLv2+ (GPLv3+ preferred)
|
|
6
6
|
#
|
|
7
|
-
# Additional work donated by contributors. See
|
|
7
|
+
# Additional work donated by contributors. See git history
|
|
8
8
|
# for more information.
|
|
9
9
|
|
|
10
|
-
require 'test/test_helper'
|
|
10
|
+
require './test/test_helper'
|
|
11
11
|
require 'time'
|
|
12
12
|
|
|
13
13
|
include Unicorn
|
|
@@ -33,12 +33,20 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
33
33
|
assert out.length > 0, "output didn't have data"
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
# ref: <CAO47=rJa=zRcLn_Xm4v2cHPr6c0UswaFC_omYFEH+baSxHOWKQ@mail.gmail.com>
|
|
37
|
+
def test_response_header_broken_nil
|
|
38
|
+
out = StringIO.new
|
|
39
|
+
http_response_write(out, 200, {"Nil" => nil}, %w(hysterical raisin))
|
|
40
|
+
assert ! out.closed?
|
|
41
|
+
|
|
42
|
+
assert_match %r{^Nil: \r\n}sm, out.string, 'nil accepted'
|
|
43
|
+
end
|
|
44
|
+
|
|
36
45
|
def test_response_string_status
|
|
37
46
|
out = StringIO.new
|
|
38
47
|
http_response_write(out,'200', {}, [])
|
|
39
48
|
assert ! out.closed?
|
|
40
49
|
assert out.length > 0, "output didn't have data"
|
|
41
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/).size
|
|
42
50
|
end
|
|
43
51
|
|
|
44
52
|
def test_response_200
|
|
@@ -71,18 +79,6 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
71
79
|
out = StringIO.new
|
|
72
80
|
http_response_write(out,200, {"X-Whatever" => "stuff"}, [])
|
|
73
81
|
assert ! out.closed?
|
|
74
|
-
assert_equal 1, out.string.split(/\r\n/).grep(/^Status: 200 OK/i).size
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def test_body_closed
|
|
78
|
-
expect_body = %w(1 2 3 4).join("\n")
|
|
79
|
-
body = StringIO.new(expect_body)
|
|
80
|
-
body.rewind
|
|
81
|
-
out = StringIO.new
|
|
82
|
-
http_response_write(out,200, {}, body)
|
|
83
|
-
assert ! out.closed?
|
|
84
|
-
assert body.closed?
|
|
85
|
-
assert_match(expect_body, out.string.split(/\r\n/).last)
|
|
86
82
|
end
|
|
87
83
|
|
|
88
84
|
def test_unknown_status_pass_through
|
|
@@ -91,9 +87,25 @@ class ResponseTest < Test::Unit::TestCase
|
|
|
91
87
|
assert ! out.closed?
|
|
92
88
|
headers = out.string.split(/\r\n\r\n/).first.split(/\r\n/)
|
|
93
89
|
assert %r{\AHTTP/\d\.\d 666 I AM THE BEAST\z}.match(headers[0])
|
|
94
|
-
status = headers.grep(/\AStatus:/i).first
|
|
95
|
-
assert status
|
|
96
|
-
assert_equal "Status: 666 I AM THE BEAST", status
|
|
97
90
|
end
|
|
98
91
|
|
|
92
|
+
def test_modified_rack_http_status_codes_late
|
|
93
|
+
r, w = IO.pipe
|
|
94
|
+
pid = fork do
|
|
95
|
+
r.close
|
|
96
|
+
# Users may want to globally override the status text associated
|
|
97
|
+
# with an HTTP status code in their app.
|
|
98
|
+
Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
|
|
99
|
+
http_response_write(w, 200, {}, [])
|
|
100
|
+
w.close
|
|
101
|
+
end
|
|
102
|
+
w.close
|
|
103
|
+
assert_equal "HTTP/1.1 200 HI\r\n", r.gets
|
|
104
|
+
r.read # just drain the pipe
|
|
105
|
+
pid, status = Process.waitpid2(pid)
|
|
106
|
+
assert status.success?, status.inspect
|
|
107
|
+
ensure
|
|
108
|
+
r.close
|
|
109
|
+
w.close unless w.closed?
|
|
110
|
+
end
|
|
99
111
|
end
|
data/test/unit/test_server.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# -*- encoding: binary -*-
|
|
2
2
|
|
|
3
|
-
# Copyright (c) 2005 Zed A. Shaw
|
|
3
|
+
# Copyright (c) 2005 Zed A. Shaw
|
|
4
4
|
# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
|
|
5
5
|
# the GPLv2+ (GPLv3+ preferred)
|
|
6
6
|
#
|
|
7
|
-
# Additional work donated by contributors. See
|
|
7
|
+
# Additional work donated by contributors. See git history
|
|
8
8
|
# for more information.
|
|
9
9
|
|
|
10
|
-
require 'test/test_helper'
|
|
10
|
+
require './test/test_helper'
|
|
11
11
|
|
|
12
12
|
include Unicorn
|
|
13
13
|
|
|
@@ -17,12 +17,40 @@ class TestHandler
|
|
|
17
17
|
while env['rack.input'].read(4096)
|
|
18
18
|
end
|
|
19
19
|
[200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
|
|
21
|
+
$stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
|
|
22
|
+
raise e
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
+
class TestEarlyHintsHandler
|
|
27
|
+
def call(env)
|
|
28
|
+
while env['rack.input'].read(4096)
|
|
29
|
+
end
|
|
30
|
+
env['rack.early_hints'].call(
|
|
31
|
+
"Link" => "</style.css>; rel=preload; as=style\n</script.js>; rel=preload"
|
|
32
|
+
)
|
|
33
|
+
[200, { 'Content-Type' => 'text/plain' }, ['hello!\n']]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class TestRackAfterReply
|
|
38
|
+
def initialize
|
|
39
|
+
@called = false
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def call(env)
|
|
43
|
+
while env['rack.input'].read(4096)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
env["rack.after_reply"] << -> { @called = true }
|
|
47
|
+
|
|
48
|
+
[200, { 'Content-Type' => 'text/plain' }, ["after_reply_called: #{@called}"]]
|
|
49
|
+
rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e
|
|
50
|
+
$stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n")
|
|
51
|
+
raise e
|
|
52
|
+
end
|
|
53
|
+
end
|
|
26
54
|
|
|
27
55
|
class WebServerTest < Test::Unit::TestCase
|
|
28
56
|
|
|
@@ -80,8 +108,54 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
80
108
|
loader_pid = tmp.sysread(4096).to_i
|
|
81
109
|
assert_equal $$, loader_pid
|
|
82
110
|
assert worker_pid != loader_pid
|
|
83
|
-
|
|
84
|
-
|
|
111
|
+
ensure
|
|
112
|
+
tmp.close!
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def test_early_hints
|
|
116
|
+
teardown
|
|
117
|
+
redirect_test_io do
|
|
118
|
+
@server = HttpServer.new(TestEarlyHintsHandler.new,
|
|
119
|
+
:listeners => [ "127.0.0.1:#@port"],
|
|
120
|
+
:early_hints => true)
|
|
121
|
+
@server.start
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
125
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
126
|
+
|
|
127
|
+
responses = sock.read(4096)
|
|
128
|
+
assert_match %r{\AHTTP/1.[01] 103\b}, responses
|
|
129
|
+
assert_match %r{^Link: </style\.css>}, responses
|
|
130
|
+
assert_match %r{^Link: </script\.js>}, responses
|
|
131
|
+
|
|
132
|
+
assert_match %r{^HTTP/1.[01] 200\b}, responses
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def test_after_reply
|
|
136
|
+
teardown
|
|
137
|
+
|
|
138
|
+
redirect_test_io do
|
|
139
|
+
@server = HttpServer.new(TestRackAfterReply.new,
|
|
140
|
+
:listeners => [ "127.0.0.1:#@port"])
|
|
141
|
+
@server.start
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
145
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
146
|
+
|
|
147
|
+
responses = sock.read(4096)
|
|
148
|
+
assert_match %r{\AHTTP/1.[01] 200\b}, responses
|
|
149
|
+
assert_match %r{^after_reply_called: false}, responses
|
|
150
|
+
|
|
151
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
152
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
153
|
+
|
|
154
|
+
responses = sock.read(4096)
|
|
155
|
+
assert_match %r{\AHTTP/1.[01] 200\b}, responses
|
|
156
|
+
assert_match %r{^after_reply_called: true}, responses
|
|
157
|
+
|
|
158
|
+
sock.close
|
|
85
159
|
end
|
|
86
160
|
|
|
87
161
|
def test_broken_app
|
|
@@ -92,7 +166,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
92
166
|
@server = HttpServer.new(app, :listeners => [ "127.0.0.1:#@port"] )
|
|
93
167
|
@server.start
|
|
94
168
|
end
|
|
95
|
-
sock =
|
|
169
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
96
170
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
97
171
|
assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
|
|
98
172
|
assert_nil sock.close
|
|
@@ -105,7 +179,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
105
179
|
|
|
106
180
|
def test_client_shutdown_writes
|
|
107
181
|
bs = 15609315 * rand
|
|
108
|
-
sock =
|
|
182
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
109
183
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
|
110
184
|
sock.syswrite("Host: example.com\r\n")
|
|
111
185
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
|
@@ -132,7 +206,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
132
206
|
|
|
133
207
|
def test_client_shutdown_write_truncates
|
|
134
208
|
bs = 15609315 * rand
|
|
135
|
-
sock =
|
|
209
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
136
210
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
|
137
211
|
sock.syswrite("Host: example.com\r\n")
|
|
138
212
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
|
@@ -158,7 +232,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
158
232
|
|
|
159
233
|
def test_client_malformed_body
|
|
160
234
|
bs = 15653984
|
|
161
|
-
sock =
|
|
235
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
162
236
|
sock.syswrite("PUT /hello HTTP/1.1\r\n")
|
|
163
237
|
sock.syswrite("Host: example.com\r\n")
|
|
164
238
|
sock.syswrite("Transfer-Encoding: chunked\r\n")
|
|
@@ -180,7 +254,7 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
180
254
|
|
|
181
255
|
def do_test(string, chunk, close_after=nil, shutdown_delay=0)
|
|
182
256
|
# Do not use instance variables here, because it needs to be thread safe
|
|
183
|
-
socket =
|
|
257
|
+
socket = tcp_socket("127.0.0.1", @port);
|
|
184
258
|
request = StringIO.new(string)
|
|
185
259
|
chunks_out = 0
|
|
186
260
|
|
|
@@ -225,14 +299,14 @@ class WebServerTest < Test::Unit::TestCase
|
|
|
225
299
|
end
|
|
226
300
|
|
|
227
301
|
def test_bad_client_400
|
|
228
|
-
sock =
|
|
302
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
229
303
|
sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
|
|
230
304
|
assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
|
|
231
305
|
assert_nil sock.close
|
|
232
306
|
end
|
|
233
307
|
|
|
234
308
|
def test_http_0_9
|
|
235
|
-
sock =
|
|
309
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
236
310
|
sock.syswrite("GET /hello\r\n")
|
|
237
311
|
assert_match 'hello!\n', sock.sysread(4096)
|
|
238
312
|
assert_nil sock.close
|
data/test/unit/test_signals.rb
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
#
|
|
7
7
|
# Ensure we stay sane in the face of signals being sent to us
|
|
8
8
|
|
|
9
|
-
require 'test/test_helper'
|
|
9
|
+
require './test/test_helper'
|
|
10
10
|
|
|
11
11
|
include Unicorn
|
|
12
12
|
|
|
@@ -52,7 +52,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
52
52
|
redirect_test_io { HttpServer.new(app, opts).start.join }
|
|
53
53
|
}
|
|
54
54
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
|
55
|
-
sock =
|
|
55
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
56
56
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
57
57
|
buf = sock.readpartial(4096)
|
|
58
58
|
assert_nil sock.close
|
|
@@ -79,7 +79,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
79
79
|
}
|
|
80
80
|
wr.close
|
|
81
81
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
|
82
|
-
sock =
|
|
82
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
83
83
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
84
84
|
buf = rd.readpartial(1)
|
|
85
85
|
wait_master_ready("test_stderr.#{pid}.log")
|
|
@@ -102,7 +102,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
102
102
|
}
|
|
103
103
|
t0 = Time.now
|
|
104
104
|
wait_workers_ready("test_stderr.#{pid}.log", 1)
|
|
105
|
-
sock =
|
|
105
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
106
106
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
107
107
|
|
|
108
108
|
buf = nil
|
|
@@ -114,8 +114,8 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
114
114
|
assert_nil buf
|
|
115
115
|
assert diff > 1.0, "diff was #{diff.inspect}"
|
|
116
116
|
assert diff < 60.0
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
ensure
|
|
118
|
+
Process.kill(:TERM, pid) rescue nil
|
|
119
119
|
end
|
|
120
120
|
|
|
121
121
|
def test_response_write
|
|
@@ -125,7 +125,7 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
125
125
|
}
|
|
126
126
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
|
127
127
|
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
|
128
|
-
sock =
|
|
128
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
129
129
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
130
130
|
buf = ''
|
|
131
131
|
header_len = pid = nil
|
|
@@ -163,13 +163,13 @@ class SignalsTest < Test::Unit::TestCase
|
|
|
163
163
|
redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
|
|
164
164
|
|
|
165
165
|
wait_workers_ready("test_stderr.#{$$}.log", 1)
|
|
166
|
-
sock =
|
|
166
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
167
167
|
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
|
168
168
|
pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
|
|
169
169
|
assert_nil sock.close
|
|
170
170
|
|
|
171
171
|
assert pid > 0, "pid not positive: #{pid.inspect}"
|
|
172
|
-
sock =
|
|
172
|
+
sock = tcp_socket('127.0.0.1', @port)
|
|
173
173
|
sock.syswrite("PUT / HTTP/1.0\r\n")
|
|
174
174
|
sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
|
|
175
175
|
1000.times { Process.kill(:HUP, pid) }
|