unicorn 0.2.3 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG +1 -0
- data/DESIGN +4 -0
- data/GNUmakefile +30 -6
- data/Manifest +62 -3
- data/README +52 -42
- data/SIGNALS +17 -17
- data/TODO +27 -5
- data/bin/unicorn +15 -13
- data/bin/unicorn_rails +59 -22
- data/ext/unicorn/http11/http11.c +25 -104
- data/ext/unicorn/http11/http11_parser.c +24 -23
- data/ext/unicorn/http11/http11_parser.h +1 -3
- data/ext/unicorn/http11/http11_parser.rl +2 -1
- data/lib/unicorn.rb +58 -44
- data/lib/unicorn/app/old_rails.rb +23 -0
- data/lib/unicorn/app/old_rails/static.rb +58 -0
- data/lib/unicorn/cgi_wrapper.rb +151 -0
- data/lib/unicorn/configurator.rb +71 -31
- data/lib/unicorn/const.rb +9 -34
- data/lib/unicorn/http_request.rb +63 -66
- data/lib/unicorn/http_response.rb +6 -1
- data/lib/unicorn/socket.rb +15 -2
- data/test/benchmark/README +55 -0
- data/test/benchmark/big_request.rb +35 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/benchmark/request.rb +47 -0
- data/test/benchmark/response.rb +29 -0
- data/test/exec/test_exec.rb +41 -157
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +4 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-1.2.3/config/boot.rb +9 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +10 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +7 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +3 -0
- data/test/rails/app-1.2.3/config/routes.rb +4 -0
- data/test/rails/app-1.2.3/db/.gitignore +0 -0
- data/test/rails/app-1.2.3/public/404.html +1 -0
- data/test/rails/app-1.2.3/public/500.html +1 -0
- data/test/rails/app-2.0.2/.gitignore +2 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.0.2/config/boot.rb +9 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +14 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +6 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +3 -0
- data/test/rails/app-2.0.2/config/routes.rb +4 -0
- data/test/rails/app-2.0.2/db/.gitignore +0 -0
- data/test/rails/app-2.0.2/public/404.html +1 -0
- data/test/rails/app-2.0.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +2 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.2.2/config/boot.rb +109 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +14 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +5 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +3 -0
- data/test/rails/app-2.2.2/config/routes.rb +4 -0
- data/test/rails/app-2.2.2/db/.gitignore +0 -0
- data/test/rails/app-2.2.2/public/404.html +1 -0
- data/test/rails/app-2.2.2/public/500.html +1 -0
- data/test/rails/app-2.3.2.1/.gitignore +2 -0
- data/test/rails/app-2.3.2.1/app/controllers/application_controller.rb +3 -0
- data/test/rails/app-2.3.2.1/app/controllers/foo_controller.rb +34 -0
- data/test/rails/app-2.3.2.1/app/helpers/application_helper.rb +2 -0
- data/test/rails/app-2.3.2.1/config/boot.rb +107 -0
- data/test/rails/app-2.3.2.1/config/database.yml +12 -0
- data/test/rails/app-2.3.2.1/config/environment.rb +14 -0
- data/test/rails/app-2.3.2.1/config/environments/development.rb +5 -0
- data/test/rails/app-2.3.2.1/config/environments/production.rb +4 -0
- data/test/rails/app-2.3.2.1/config/routes.rb +4 -0
- data/test/rails/app-2.3.2.1/db/.gitignore +0 -0
- data/test/rails/app-2.3.2.1/public/404.html +1 -0
- data/test/rails/app-2.3.2.1/public/500.html +1 -0
- data/test/rails/test_rails.rb +243 -0
- data/test/test_helper.rb +149 -2
- data/test/unit/test_configurator.rb +46 -0
- data/test/unit/test_http_parser.rb +77 -36
- data/test/unit/test_request.rb +2 -0
- data/test/unit/test_response.rb +20 -4
- data/test/unit/test_server.rb +30 -1
- data/test/unit/test_socket_helper.rb +159 -0
- data/unicorn.gemspec +5 -5
- metadata +68 -5
- data/test/benchmark/previous.rb +0 -11
- data/test/benchmark/simple.rb +0 -11
- data/test/benchmark/utils.rb +0 -82
@@ -8,6 +8,28 @@ class TestConfigurator < Test::Unit::TestCase
|
|
8
8
|
assert_nothing_raised { Unicorn::Configurator.new {} }
|
9
9
|
end
|
10
10
|
|
11
|
+
def test_expand_addr
|
12
|
+
meth = Unicorn::Configurator.new.method(:expand_addr)
|
13
|
+
|
14
|
+
assert_equal "/var/run/unicorn.sock", meth.call("/var/run/unicorn.sock")
|
15
|
+
assert_equal "#{Dir.pwd}/foo/bar.sock", meth.call("unix:foo/bar.sock")
|
16
|
+
|
17
|
+
path = meth.call("~/foo/bar.sock")
|
18
|
+
assert_equal "/", path[0..0]
|
19
|
+
assert_match %r{/foo/bar\.sock\z}, path
|
20
|
+
|
21
|
+
path = meth.call("~root/foo/bar.sock")
|
22
|
+
assert_equal "/", path[0..0]
|
23
|
+
assert_match %r{/foo/bar\.sock\z}, path
|
24
|
+
|
25
|
+
assert_equal "1.2.3.4:2007", meth.call('1.2.3.4:2007')
|
26
|
+
assert_equal "0.0.0.0:2007", meth.call('0.0.0.0:2007')
|
27
|
+
assert_equal "0.0.0.0:2007", meth.call(':2007')
|
28
|
+
assert_equal "0.0.0.0:2007", meth.call('*:2007')
|
29
|
+
assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007')
|
30
|
+
assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007')
|
31
|
+
end
|
32
|
+
|
11
33
|
def test_config_invalid
|
12
34
|
tmp = Tempfile.new('unicorn_config')
|
13
35
|
tmp.syswrite(%q(asdfasdf "hello-world"))
|
@@ -45,4 +67,28 @@ class TestConfigurator < Test::Unit::TestCase
|
|
45
67
|
assert_nil @logger
|
46
68
|
end
|
47
69
|
|
70
|
+
def test_listen_options
|
71
|
+
tmp = Tempfile.new('unicorn_config')
|
72
|
+
expect = { :sndbuf => 1, :rcvbuf => 2, :backlog => 10 }.freeze
|
73
|
+
listener = "127.0.0.1:12345"
|
74
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
75
|
+
cfg = nil
|
76
|
+
assert_nothing_raised do
|
77
|
+
cfg = Unicorn::Configurator.new(:config_file => tmp.path)
|
78
|
+
end
|
79
|
+
assert_nothing_raised { cfg.commit!(self) }
|
80
|
+
assert(listener_opts = instance_variable_get("@listener_opts"))
|
81
|
+
assert_equal expect, listener_opts[listener]
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_listen_option_bad
|
85
|
+
tmp = Tempfile.new('unicorn_config')
|
86
|
+
expect = { :sndbuf => "five" }
|
87
|
+
listener = "127.0.0.1:12345"
|
88
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
89
|
+
assert_raises(ArgumentError) do
|
90
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
48
94
|
end
|
@@ -14,33 +14,40 @@ class HttpParserTest < Test::Unit::TestCase
|
|
14
14
|
parser = HttpParser.new
|
15
15
|
req = {}
|
16
16
|
http = "GET / HTTP/1.1\r\n\r\n"
|
17
|
-
|
18
|
-
|
19
|
-
assert nread == http.length, "Failed to parse the full HTTP request"
|
20
|
-
assert parser.finished?, "Parser didn't finish"
|
21
|
-
assert !parser.error?, "Parser had error"
|
22
|
-
assert nread == parser.nread, "Number read returned from execute does not match"
|
17
|
+
assert parser.execute(req, http)
|
23
18
|
|
24
19
|
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
25
20
|
assert_equal '/', req['REQUEST_PATH']
|
26
21
|
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
27
22
|
assert_equal '/', req['REQUEST_URI']
|
28
|
-
assert_equal 'GET', req['REQUEST_METHOD']
|
23
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
29
24
|
assert_nil req['FRAGMENT']
|
30
25
|
assert_nil req['QUERY_STRING']
|
31
26
|
|
32
27
|
parser.reset
|
33
|
-
|
28
|
+
req.clear
|
29
|
+
|
30
|
+
assert ! parser.execute(req, "G")
|
31
|
+
assert req.empty?
|
32
|
+
|
33
|
+
# try parsing again to ensure we were reset correctly
|
34
|
+
http = "GET /hello-world HTTP/1.1\r\n\r\n"
|
35
|
+
assert parser.execute(req, http)
|
36
|
+
|
37
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
38
|
+
assert_equal '/hello-world', req['REQUEST_PATH']
|
39
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
40
|
+
assert_equal '/hello-world', req['REQUEST_URI']
|
41
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
42
|
+
assert_nil req['FRAGMENT']
|
43
|
+
assert_nil req['QUERY_STRING']
|
34
44
|
end
|
35
|
-
|
45
|
+
|
36
46
|
def test_parse_strange_headers
|
37
47
|
parser = HttpParser.new
|
38
48
|
req = {}
|
39
49
|
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
40
|
-
|
41
|
-
assert_equal should_be_good.length, nread
|
42
|
-
assert parser.finished?
|
43
|
-
assert !parser.error?
|
50
|
+
assert parser.execute(req, should_be_good)
|
44
51
|
|
45
52
|
# ref: http://thread.gmane.org/gmane.comp.lang.ruby.Unicorn.devel/37/focus=45
|
46
53
|
# (note we got 'pen' mixed up with 'pound' in that thread,
|
@@ -49,10 +56,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
49
56
|
# nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit: -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
|
50
57
|
# parser = HttpParser.new
|
51
58
|
# req = {}
|
52
|
-
#
|
53
|
-
# assert_equal nasty_pound_header.length, nread
|
54
|
-
# assert parser.finished?
|
55
|
-
# assert !parser.error?
|
59
|
+
# assert parser.execute(req, nasty_pound_header, 0)
|
56
60
|
end
|
57
61
|
|
58
62
|
def test_parse_ie6_urls
|
@@ -66,10 +70,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
66
70
|
parser = HttpParser.new
|
67
71
|
req = {}
|
68
72
|
sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
|
69
|
-
|
70
|
-
assert_equal sorta_safe.length, nread
|
71
|
-
assert parser.finished?
|
72
|
-
assert !parser.error?
|
73
|
+
assert parser.execute(req, sorta_safe)
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
@@ -78,28 +79,68 @@ class HttpParserTest < Test::Unit::TestCase
|
|
78
79
|
req = {}
|
79
80
|
bad_http = "GET / SsUTF/1.1"
|
80
81
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
error = true
|
86
|
-
end
|
82
|
+
assert_raises(HttpParserError) { parser.execute(req, bad_http) }
|
83
|
+
parser.reset
|
84
|
+
assert(parser.execute({}, "GET / HTTP/1.0\r\n\r\n"))
|
85
|
+
end
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
def test_piecemeal
|
88
|
+
parser = HttpParser.new
|
89
|
+
req = {}
|
90
|
+
http = "GET"
|
91
|
+
assert ! parser.execute(req, http)
|
92
|
+
assert_raises(HttpParserError) { parser.execute(req, http) }
|
93
|
+
assert ! parser.execute(req, http << " / HTTP/1.0")
|
94
|
+
assert_equal '/', req['REQUEST_PATH']
|
95
|
+
assert_equal '/', req['REQUEST_URI']
|
96
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
97
|
+
assert ! parser.execute(req, http << "\r\n")
|
98
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
99
|
+
assert ! parser.execute(req, http << "\r")
|
100
|
+
assert parser.execute(req, http << "\n")
|
101
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
102
|
+
assert_nil req['FRAGMENT']
|
103
|
+
assert_nil req['QUERY_STRING']
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_put_body_oneshot
|
107
|
+
parser = HttpParser.new
|
108
|
+
req = {}
|
109
|
+
http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
|
110
|
+
assert parser.execute(req, http)
|
111
|
+
assert_equal '/', req['REQUEST_PATH']
|
112
|
+
assert_equal '/', req['REQUEST_URI']
|
113
|
+
assert_equal 'PUT', req['REQUEST_METHOD']
|
114
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
115
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
116
|
+
assert_equal "abcde", req[:http_body]
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_put_body_later
|
120
|
+
parser = HttpParser.new
|
121
|
+
req = {}
|
122
|
+
http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
|
123
|
+
assert parser.execute(req, http)
|
124
|
+
assert_equal '/l', req['REQUEST_PATH']
|
125
|
+
assert_equal '/l', req['REQUEST_URI']
|
126
|
+
assert_equal 'PUT', req['REQUEST_METHOD']
|
127
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
128
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
129
|
+
assert_equal "", req[:http_body]
|
91
130
|
end
|
92
131
|
|
93
132
|
def test_fragment_in_uri
|
94
133
|
parser = HttpParser.new
|
95
134
|
req = {}
|
96
135
|
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
136
|
+
ok = false
|
97
137
|
assert_nothing_raised do
|
98
|
-
parser.execute(req, get
|
138
|
+
ok = parser.execute(req, get)
|
99
139
|
end
|
100
|
-
assert
|
140
|
+
assert ok
|
101
141
|
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
102
142
|
assert_equal 'posts-17408', req['FRAGMENT']
|
143
|
+
assert_equal 'page=1', req['QUERY_STRING']
|
103
144
|
end
|
104
145
|
|
105
146
|
# lame random garbage maker
|
@@ -124,7 +165,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
124
165
|
10.times do |c|
|
125
166
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
126
167
|
assert_raises Unicorn::HttpParserError do
|
127
|
-
parser.execute({}, get
|
168
|
+
parser.execute({}, get)
|
128
169
|
parser.reset
|
129
170
|
end
|
130
171
|
end
|
@@ -133,7 +174,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
133
174
|
10.times do |c|
|
134
175
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
135
176
|
assert_raises Unicorn::HttpParserError do
|
136
|
-
parser.execute({}, get
|
177
|
+
parser.execute({}, get)
|
137
178
|
parser.reset
|
138
179
|
end
|
139
180
|
end
|
@@ -142,7 +183,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
142
183
|
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
143
184
|
get << "X-Test: test\r\n" * (80 * 1024)
|
144
185
|
assert_raises Unicorn::HttpParserError do
|
145
|
-
parser.execute({}, get
|
186
|
+
parser.execute({}, get)
|
146
187
|
parser.reset
|
147
188
|
end
|
148
189
|
|
@@ -150,7 +191,7 @@ class HttpParserTest < Test::Unit::TestCase
|
|
150
191
|
10.times do |c|
|
151
192
|
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
152
193
|
assert_raises Unicorn::HttpParserError do
|
153
|
-
parser.execute({}, get
|
194
|
+
parser.execute({}, get)
|
154
195
|
parser.reset
|
155
196
|
end
|
156
197
|
end
|
data/test/unit/test_request.rb
CHANGED
@@ -50,6 +50,7 @@ class RequestTest < Test::Unit::TestCase
|
|
50
50
|
"abcde")
|
51
51
|
res = env = nil
|
52
52
|
assert_nothing_raised { env = @request.read(client) }
|
53
|
+
assert ! env.include?(:http_body)
|
53
54
|
assert_nothing_raised { res = @lint.call(env) }
|
54
55
|
end
|
55
56
|
|
@@ -71,6 +72,7 @@ class RequestTest < Test::Unit::TestCase
|
|
71
72
|
assert_equal 0, client.sysseek(0)
|
72
73
|
res = env = nil
|
73
74
|
assert_nothing_raised { env = @request.read(client) }
|
75
|
+
assert ! env.include?(:http_body)
|
74
76
|
assert_equal length, env['rack.input'].size
|
75
77
|
count.times { assert_equal buf, env['rack.input'].read(bs) }
|
76
78
|
assert_nil env['rack.input'].read(bs)
|
data/test/unit/test_response.rb
CHANGED
@@ -13,6 +13,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
13
13
|
def test_response_headers
|
14
14
|
out = StringIO.new
|
15
15
|
HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, ["cool"]])
|
16
|
+
assert out.closed?
|
16
17
|
|
17
18
|
assert out.length > 0, "output didn't have data"
|
18
19
|
end
|
@@ -22,7 +23,8 @@ class ResponseTest < Test::Unit::TestCase
|
|
22
23
|
$, = "\f\v"
|
23
24
|
out = StringIO.new
|
24
25
|
HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, ["cool"]])
|
25
|
-
|
26
|
+
assert out.closed?
|
27
|
+
resp = out.string
|
26
28
|
assert ! resp.include?("\f\v"), "output didn't use $, ($OFS)"
|
27
29
|
ensure
|
28
30
|
$, = old_ofs
|
@@ -31,6 +33,7 @@ class ResponseTest < Test::Unit::TestCase
|
|
31
33
|
def test_response_200
|
32
34
|
io = StringIO.new
|
33
35
|
HttpResponse.write(io, [200, {}, []])
|
36
|
+
assert io.closed?
|
34
37
|
assert io.length > 0, "output didn't have data"
|
35
38
|
end
|
36
39
|
|
@@ -38,15 +41,28 @@ class ResponseTest < Test::Unit::TestCase
|
|
38
41
|
code = 400
|
39
42
|
io = StringIO.new
|
40
43
|
HttpResponse.write(io, [code, {}, []])
|
41
|
-
io.
|
42
|
-
|
44
|
+
assert io.closed?
|
45
|
+
lines = io.string.split(/\r\n/)
|
46
|
+
assert_match(/.* #{HTTP_STATUS_CODES[code]}$/, lines.first,
|
47
|
+
"wrong default reason phrase")
|
43
48
|
end
|
44
49
|
|
45
50
|
def test_rack_multivalue_headers
|
46
51
|
out = StringIO.new
|
47
52
|
HttpResponse.write(out,[200, {"X-Whatever" => "stuff\nbleh"}, []])
|
53
|
+
assert out.closed?
|
48
54
|
assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string)
|
49
55
|
end
|
50
56
|
|
51
|
-
|
57
|
+
def test_body_closed
|
58
|
+
expect_body = %w(1 2 3 4).join("\n")
|
59
|
+
body = StringIO.new(expect_body)
|
60
|
+
body.rewind
|
61
|
+
out = StringIO.new
|
62
|
+
HttpResponse.write(out,[200, {}, body])
|
63
|
+
assert out.closed?
|
64
|
+
assert body.closed?
|
65
|
+
assert_match(expect_body, out.string.split(/\r\n/).last)
|
66
|
+
end
|
52
67
|
|
68
|
+
end
|
data/test/unit/test_server.rb
CHANGED
@@ -25,8 +25,8 @@ class WebServerTest < Test::Unit::TestCase
|
|
25
25
|
@tester = TestHandler.new
|
26
26
|
redirect_test_io do
|
27
27
|
@server = HttpServer.new(@tester, :listeners => [ "127.0.0.1:#{@port}" ] )
|
28
|
+
@server.start
|
28
29
|
end
|
29
|
-
@server.start
|
30
30
|
end
|
31
31
|
|
32
32
|
def teardown
|
@@ -35,6 +35,25 @@ class WebServerTest < Test::Unit::TestCase
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
def test_broken_app
|
39
|
+
teardown
|
40
|
+
port = unused_port
|
41
|
+
app = lambda { |env| raise RuntimeError, "hello" }
|
42
|
+
# [200, {}, []] }
|
43
|
+
redirect_test_io do
|
44
|
+
@server = HttpServer.new(app, :listeners => [ "127.0.0.1:#{port}"] )
|
45
|
+
@server.start
|
46
|
+
end
|
47
|
+
sock = nil
|
48
|
+
assert_nothing_raised do
|
49
|
+
sock = TCPSocket.new('127.0.0.1', port)
|
50
|
+
sock.syswrite("GET / HTTP/1.0\r\n\r\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_match %r{\AHTTP/1.[01] 500\b}, sock.sysread(4096)
|
54
|
+
assert_nothing_raised { sock.close }
|
55
|
+
end
|
56
|
+
|
38
57
|
def test_simple_server
|
39
58
|
results = hit(["http://localhost:#{@port}/test"])
|
40
59
|
assert_equal 'hello!\n', results[0], "Handler didn't really run"
|
@@ -77,6 +96,16 @@ class WebServerTest < Test::Unit::TestCase
|
|
77
96
|
end
|
78
97
|
end
|
79
98
|
|
99
|
+
def test_bad_client_400
|
100
|
+
sock = nil
|
101
|
+
assert_nothing_raised do
|
102
|
+
sock = TCPSocket.new('127.0.0.1', @port)
|
103
|
+
sock.syswrite("GET / HTTP/1.0\r\nHost: foo\rbar\r\n\r\n")
|
104
|
+
end
|
105
|
+
assert_match %r{\AHTTP/1.[01] 400\b}, sock.sysread(4096)
|
106
|
+
assert_nothing_raised { sock.close }
|
107
|
+
end
|
108
|
+
|
80
109
|
def test_header_is_too_long
|
81
110
|
redirect_test_io do
|
82
111
|
long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n"
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
class TestSocketHelper < Test::Unit::TestCase
|
5
|
+
include Unicorn::SocketHelper
|
6
|
+
attr_reader :logger
|
7
|
+
GET_SLASH = "GET / HTTP/1.0\r\n\r\n".freeze
|
8
|
+
|
9
|
+
def setup
|
10
|
+
@log_tmp = Tempfile.new 'logger'
|
11
|
+
@logger = Logger.new(@log_tmp.path)
|
12
|
+
@test_addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_bind_listen_tcp
|
16
|
+
port = unused_port @test_addr
|
17
|
+
@tcp_listener_name = "#@test_addr:#{port}"
|
18
|
+
@tcp_listener = bind_listen(@tcp_listener_name)
|
19
|
+
assert Socket === @tcp_listener
|
20
|
+
assert_equal @tcp_listener_name, sock_name(@tcp_listener)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_bind_listen_options
|
24
|
+
port = unused_port @test_addr
|
25
|
+
tcp_listener_name = "#@test_addr:#{port}"
|
26
|
+
tmp = Tempfile.new 'unix.sock'
|
27
|
+
unix_listener_name = tmp.path
|
28
|
+
File.unlink(tmp.path)
|
29
|
+
[ { :backlog => 5 }, { :sndbuf => 4096 }, { :rcvbuf => 4096 },
|
30
|
+
{ :backlog => 16, :rcvbuf => 4096, :sndbuf => 4096 }
|
31
|
+
].each do |opts|
|
32
|
+
assert_nothing_raised do
|
33
|
+
tcp_listener = bind_listen(tcp_listener_name, opts)
|
34
|
+
assert Socket === tcp_listener
|
35
|
+
tcp_listener.close
|
36
|
+
unix_listener = bind_listen(unix_listener_name, opts)
|
37
|
+
assert Socket === unix_listener
|
38
|
+
unix_listener.close
|
39
|
+
end
|
40
|
+
end
|
41
|
+
#system('cat', @log_tmp.path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_bind_listen_unix
|
45
|
+
tmp = Tempfile.new 'unix.sock'
|
46
|
+
@unix_listener_path = tmp.path
|
47
|
+
File.unlink(@unix_listener_path)
|
48
|
+
@unix_listener = bind_listen(@unix_listener_path)
|
49
|
+
assert Socket === @unix_listener
|
50
|
+
assert_equal @unix_listener_path, sock_name(@unix_listener)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_bind_listen_unix_idempotent
|
54
|
+
test_bind_listen_unix
|
55
|
+
a = bind_listen(@unix_listener)
|
56
|
+
assert_equal a.fileno, @unix_listener.fileno
|
57
|
+
unix_server = server_cast(@unix_listener)
|
58
|
+
a = bind_listen(unix_server)
|
59
|
+
assert_equal a.fileno, unix_server.fileno
|
60
|
+
assert_equal a.fileno, @unix_listener.fileno
|
61
|
+
end
|
62
|
+
|
63
|
+
def test_bind_listen_tcp_idempotent
|
64
|
+
test_bind_listen_tcp
|
65
|
+
a = bind_listen(@tcp_listener)
|
66
|
+
assert_equal a.fileno, @tcp_listener.fileno
|
67
|
+
tcp_server = server_cast(@tcp_listener)
|
68
|
+
a = bind_listen(tcp_server)
|
69
|
+
assert_equal a.fileno, tcp_server.fileno
|
70
|
+
assert_equal a.fileno, @tcp_listener.fileno
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_bind_listen_unix_rebind
|
74
|
+
test_bind_listen_unix
|
75
|
+
new_listener = bind_listen(@unix_listener_path)
|
76
|
+
assert Socket === new_listener
|
77
|
+
assert new_listener.fileno != @unix_listener.fileno
|
78
|
+
assert_equal sock_name(new_listener), sock_name(@unix_listener)
|
79
|
+
assert_equal @unix_listener_path, sock_name(new_listener)
|
80
|
+
pid = fork do
|
81
|
+
client = server_cast(new_listener).accept
|
82
|
+
client.syswrite('abcde')
|
83
|
+
exit 0
|
84
|
+
end
|
85
|
+
s = UNIXSocket.new(@unix_listener_path)
|
86
|
+
IO.select([s])
|
87
|
+
assert_equal 'abcde', s.sysread(5)
|
88
|
+
pid, status = Process.waitpid2(pid)
|
89
|
+
assert status.success?
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_server_cast
|
93
|
+
assert_nothing_raised do
|
94
|
+
test_bind_listen_unix
|
95
|
+
test_bind_listen_tcp
|
96
|
+
end
|
97
|
+
@unix_server = server_cast(@unix_listener)
|
98
|
+
assert_equal @unix_listener.fileno, @unix_server.fileno
|
99
|
+
assert UNIXServer === @unix_server
|
100
|
+
assert File.socket?(@unix_server.path)
|
101
|
+
assert_equal @unix_listener_path, sock_name(@unix_server)
|
102
|
+
|
103
|
+
@tcp_server = server_cast(@tcp_listener)
|
104
|
+
assert_equal @tcp_listener.fileno, @tcp_server.fileno
|
105
|
+
assert TCPServer === @tcp_server
|
106
|
+
assert_equal @tcp_listener_name, sock_name(@tcp_server)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_sock_name
|
110
|
+
test_server_cast
|
111
|
+
sock_name(@unix_server)
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_tcp_unicorn_peeraddr
|
115
|
+
test_bind_listen_tcp
|
116
|
+
@tcp_server = server_cast(@tcp_listener)
|
117
|
+
tmp = Tempfile.new 'shared'
|
118
|
+
pid = fork do
|
119
|
+
client = @tcp_server.accept
|
120
|
+
IO.select([client])
|
121
|
+
assert_equal GET_SLASH, client.sysread(GET_SLASH.size)
|
122
|
+
tmp.syswrite "#{client.unicorn_peeraddr}"
|
123
|
+
exit 0
|
124
|
+
end
|
125
|
+
host, port = sock_name(@tcp_server).split(/:/)
|
126
|
+
client = TCPSocket.new(host, port.to_i)
|
127
|
+
client.syswrite(GET_SLASH)
|
128
|
+
|
129
|
+
pid, status = Process.waitpid2(pid)
|
130
|
+
assert_nothing_raised { client.close }
|
131
|
+
assert status.success?
|
132
|
+
tmp.sysseek 0
|
133
|
+
assert_equal @test_addr, tmp.sysread(4096)
|
134
|
+
tmp.sysseek 0
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_unix_unicorn_peeraddr
|
138
|
+
test_bind_listen_unix
|
139
|
+
@unix_server = server_cast(@unix_listener)
|
140
|
+
tmp = Tempfile.new 'shared'
|
141
|
+
pid = fork do
|
142
|
+
client = @unix_server.accept
|
143
|
+
IO.select([client])
|
144
|
+
assert_equal GET_SLASH, client.sysread(4096)
|
145
|
+
tmp.syswrite "#{client.unicorn_peeraddr}"
|
146
|
+
exit 0
|
147
|
+
end
|
148
|
+
client = UNIXSocket.new(@unix_listener_path)
|
149
|
+
client.syswrite(GET_SLASH)
|
150
|
+
|
151
|
+
pid, status = Process.waitpid2(pid)
|
152
|
+
assert_nothing_raised { client.close }
|
153
|
+
assert status.success?
|
154
|
+
tmp.sysseek 0
|
155
|
+
assert_equal '127.0.0.1', tmp.sysread(4096)
|
156
|
+
tmp.sysseek 0
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|