unicorn-fotopedia 0.99.1
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.
- data/.CHANGELOG.old +25 -0
- data/.document +19 -0
- data/.gitignore +21 -0
- data/.mailmap +26 -0
- data/CONTRIBUTORS +32 -0
- data/COPYING +339 -0
- data/DESIGN +105 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +30 -0
- data/Documentation/unicorn.1.txt +171 -0
- data/Documentation/unicorn_rails.1.txt +172 -0
- data/FAQ +52 -0
- data/GIT-VERSION-GEN +40 -0
- data/GNUmakefile +292 -0
- data/HACKING +116 -0
- data/ISSUES +36 -0
- data/KNOWN_ISSUES +50 -0
- data/LICENSE +55 -0
- data/PHILOSOPHY +145 -0
- data/README +149 -0
- data/Rakefile +191 -0
- data/SIGNALS +109 -0
- data/Sandbox +78 -0
- data/TODO +5 -0
- data/TUNING +70 -0
- data/bin/unicorn +126 -0
- data/bin/unicorn_rails +203 -0
- data/examples/big_app_gc.rb +33 -0
- data/examples/echo.ru +27 -0
- data/examples/git.ru +13 -0
- data/examples/init.sh +58 -0
- data/examples/logger_mp_safe.rb +25 -0
- data/examples/nginx.conf +139 -0
- data/examples/unicorn.conf.rb +78 -0
- data/ext/unicorn_http/CFLAGS +13 -0
- data/ext/unicorn_http/c_util.h +124 -0
- data/ext/unicorn_http/common_field_optimization.h +111 -0
- data/ext/unicorn_http/ext_help.h +77 -0
- data/ext/unicorn_http/extconf.rb +14 -0
- data/ext/unicorn_http/global_variables.h +89 -0
- data/ext/unicorn_http/unicorn_http.rl +714 -0
- data/ext/unicorn_http/unicorn_http_common.rl +75 -0
- data/lib/unicorn.rb +847 -0
- data/lib/unicorn/app/exec_cgi.rb +150 -0
- data/lib/unicorn/app/inetd.rb +109 -0
- data/lib/unicorn/app/old_rails.rb +33 -0
- data/lib/unicorn/app/old_rails/static.rb +58 -0
- data/lib/unicorn/cgi_wrapper.rb +145 -0
- data/lib/unicorn/configurator.rb +421 -0
- data/lib/unicorn/const.rb +34 -0
- data/lib/unicorn/http_request.rb +72 -0
- data/lib/unicorn/http_response.rb +75 -0
- data/lib/unicorn/launcher.rb +65 -0
- data/lib/unicorn/oob_gc.rb +58 -0
- data/lib/unicorn/socket_helper.rb +152 -0
- data/lib/unicorn/tee_input.rb +217 -0
- data/lib/unicorn/util.rb +90 -0
- data/local.mk.sample +62 -0
- data/setup.rb +1586 -0
- data/t/.gitignore +2 -0
- data/t/GNUmakefile +67 -0
- data/t/README +42 -0
- data/t/bin/content-md5-put +36 -0
- data/t/bin/sha1sum.rb +23 -0
- data/t/bin/unused_listen +40 -0
- data/t/bin/utee +12 -0
- data/t/env.ru +3 -0
- data/t/my-tap-lib.sh +200 -0
- data/t/t0000-http-basic.sh +50 -0
- data/t/t0001-reload-bad-config.sh +52 -0
- data/t/t0002-config-conflict.sh +49 -0
- data/t/test-lib.sh +100 -0
- data/test/aggregate.rb +15 -0
- data/test/benchmark/README +50 -0
- data/test/benchmark/dd.ru +18 -0
- data/test/exec/README +5 -0
- data/test/exec/test_exec.rb +1038 -0
- data/test/rails/app-1.2.3/.gitignore +2 -0
- data/test/rails/app-1.2.3/Rakefile +7 -0
- data/test/rails/app-1.2.3/app/controllers/application.rb +6 -0
- data/test/rails/app-1.2.3/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-1.2.3/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-1.2.3/config/boot.rb +11 -0
- data/test/rails/app-1.2.3/config/database.yml +12 -0
- data/test/rails/app-1.2.3/config/environment.rb +13 -0
- data/test/rails/app-1.2.3/config/environments/development.rb +9 -0
- data/test/rails/app-1.2.3/config/environments/production.rb +5 -0
- data/test/rails/app-1.2.3/config/routes.rb +6 -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/Rakefile +7 -0
- data/test/rails/app-2.0.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.0.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.0.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.0.2/config/boot.rb +11 -0
- data/test/rails/app-2.0.2/config/database.yml +12 -0
- data/test/rails/app-2.0.2/config/environment.rb +17 -0
- data/test/rails/app-2.0.2/config/environments/development.rb +8 -0
- data/test/rails/app-2.0.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.0.2/config/routes.rb +6 -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.1.2/.gitignore +2 -0
- data/test/rails/app-2.1.2/Rakefile +7 -0
- data/test/rails/app-2.1.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.1.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.1.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.1.2/config/boot.rb +111 -0
- data/test/rails/app-2.1.2/config/database.yml +12 -0
- data/test/rails/app-2.1.2/config/environment.rb +17 -0
- data/test/rails/app-2.1.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.1.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.1.2/config/routes.rb +6 -0
- data/test/rails/app-2.1.2/db/.gitignore +0 -0
- data/test/rails/app-2.1.2/public/404.html +1 -0
- data/test/rails/app-2.1.2/public/500.html +1 -0
- data/test/rails/app-2.2.2/.gitignore +2 -0
- data/test/rails/app-2.2.2/Rakefile +7 -0
- data/test/rails/app-2.2.2/app/controllers/application.rb +4 -0
- data/test/rails/app-2.2.2/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.2.2/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.2.2/config/boot.rb +111 -0
- data/test/rails/app-2.2.2/config/database.yml +12 -0
- data/test/rails/app-2.2.2/config/environment.rb +17 -0
- data/test/rails/app-2.2.2/config/environments/development.rb +7 -0
- data/test/rails/app-2.2.2/config/environments/production.rb +5 -0
- data/test/rails/app-2.2.2/config/routes.rb +6 -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.5/.gitignore +2 -0
- data/test/rails/app-2.3.5/Rakefile +7 -0
- data/test/rails/app-2.3.5/app/controllers/application_controller.rb +5 -0
- data/test/rails/app-2.3.5/app/controllers/foo_controller.rb +36 -0
- data/test/rails/app-2.3.5/app/helpers/application_helper.rb +4 -0
- data/test/rails/app-2.3.5/config/boot.rb +109 -0
- data/test/rails/app-2.3.5/config/database.yml +12 -0
- data/test/rails/app-2.3.5/config/environment.rb +17 -0
- data/test/rails/app-2.3.5/config/environments/development.rb +7 -0
- data/test/rails/app-2.3.5/config/environments/production.rb +6 -0
- data/test/rails/app-2.3.5/config/routes.rb +6 -0
- data/test/rails/app-2.3.5/db/.gitignore +0 -0
- data/test/rails/app-2.3.5/public/404.html +1 -0
- data/test/rails/app-2.3.5/public/500.html +1 -0
- data/test/rails/app-2.3.5/public/x.txt +1 -0
- data/test/rails/test_rails.rb +280 -0
- data/test/test_helper.rb +301 -0
- data/test/unit/test_configurator.rb +150 -0
- data/test/unit/test_http_parser.rb +555 -0
- data/test/unit/test_http_parser_ng.rb +443 -0
- data/test/unit/test_request.rb +184 -0
- data/test/unit/test_response.rb +110 -0
- data/test/unit/test_server.rb +291 -0
- data/test/unit/test_signals.rb +206 -0
- data/test/unit/test_socket_helper.rb +147 -0
- data/test/unit/test_tee_input.rb +257 -0
- data/test/unit/test_upload.rb +298 -0
- data/test/unit/test_util.rb +96 -0
- data/unicorn.gemspec +52 -0
- metadata +283 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
require 'tempfile'
|
5
|
+
require 'unicorn'
|
6
|
+
|
7
|
+
TestStruct = Struct.new(
|
8
|
+
*(Unicorn::Configurator::DEFAULTS.keys + %w(listener_opts listeners)))
|
9
|
+
class TestConfigurator < Test::Unit::TestCase
|
10
|
+
|
11
|
+
def test_config_init
|
12
|
+
assert_nothing_raised { Unicorn::Configurator.new {} }
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_expand_addr
|
16
|
+
meth = Unicorn::Configurator.new.method(:expand_addr)
|
17
|
+
|
18
|
+
assert_equal "/var/run/unicorn.sock", meth.call("/var/run/unicorn.sock")
|
19
|
+
assert_equal "#{Dir.pwd}/foo/bar.sock", meth.call("unix:foo/bar.sock")
|
20
|
+
|
21
|
+
path = meth.call("~/foo/bar.sock")
|
22
|
+
assert_equal "/", path[0..0]
|
23
|
+
assert_match %r{/foo/bar\.sock\z}, path
|
24
|
+
|
25
|
+
path = meth.call("~root/foo/bar.sock")
|
26
|
+
assert_equal "/", path[0..0]
|
27
|
+
assert_match %r{/foo/bar\.sock\z}, path
|
28
|
+
|
29
|
+
assert_equal "1.2.3.4:2007", meth.call('1.2.3.4:2007')
|
30
|
+
assert_equal "0.0.0.0:2007", meth.call('0.0.0.0:2007')
|
31
|
+
assert_equal "0.0.0.0:2007", meth.call(':2007')
|
32
|
+
assert_equal "0.0.0.0:2007", meth.call('*:2007')
|
33
|
+
assert_equal "0.0.0.0:2007", meth.call('2007')
|
34
|
+
assert_equal "0.0.0.0:2007", meth.call(2007)
|
35
|
+
|
36
|
+
# the next two aren't portable, consider them unsupported for now
|
37
|
+
# assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007')
|
38
|
+
# assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007')
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_config_invalid
|
42
|
+
tmp = Tempfile.new('unicorn_config')
|
43
|
+
tmp.syswrite(%q(asdfasdf "hello-world"))
|
44
|
+
assert_raises(NoMethodError) do
|
45
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_config_non_existent
|
50
|
+
tmp = Tempfile.new('unicorn_config')
|
51
|
+
path = tmp.path
|
52
|
+
tmp.close!
|
53
|
+
assert_raises(Errno::ENOENT) do
|
54
|
+
Unicorn::Configurator.new(:config_file => path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_config_defaults
|
59
|
+
cfg = Unicorn::Configurator.new(:use_defaults => true)
|
60
|
+
test_struct = TestStruct.new
|
61
|
+
assert_nothing_raised { cfg.commit!(test_struct) }
|
62
|
+
Unicorn::Configurator::DEFAULTS.each do |key,value|
|
63
|
+
assert_equal value, test_struct.__send__(key)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_config_defaults_skip
|
68
|
+
cfg = Unicorn::Configurator.new(:use_defaults => true)
|
69
|
+
skip = [ :logger ]
|
70
|
+
test_struct = TestStruct.new
|
71
|
+
assert_nothing_raised { cfg.commit!(test_struct, :skip => skip) }
|
72
|
+
Unicorn::Configurator::DEFAULTS.each do |key,value|
|
73
|
+
next if skip.include?(key)
|
74
|
+
assert_equal value, test_struct.__send__(key)
|
75
|
+
end
|
76
|
+
assert_nil test_struct.logger
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_listen_options
|
80
|
+
tmp = Tempfile.new('unicorn_config')
|
81
|
+
expect = { :sndbuf => 1, :rcvbuf => 2, :backlog => 10 }.freeze
|
82
|
+
listener = "127.0.0.1:12345"
|
83
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
84
|
+
cfg = nil
|
85
|
+
assert_nothing_raised do
|
86
|
+
cfg = Unicorn::Configurator.new(:config_file => tmp.path)
|
87
|
+
end
|
88
|
+
test_struct = TestStruct.new
|
89
|
+
assert_nothing_raised { cfg.commit!(test_struct) }
|
90
|
+
assert(listener_opts = test_struct.listener_opts)
|
91
|
+
assert_equal expect, listener_opts[listener]
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_listen_option_bad
|
95
|
+
tmp = Tempfile.new('unicorn_config')
|
96
|
+
expect = { :sndbuf => "five" }
|
97
|
+
listener = "127.0.0.1:12345"
|
98
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
99
|
+
assert_raises(ArgumentError) do
|
100
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_listen_option_bad_delay
|
105
|
+
tmp = Tempfile.new('unicorn_config')
|
106
|
+
expect = { :delay => "five" }
|
107
|
+
listener = "127.0.0.1:12345"
|
108
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
109
|
+
assert_raises(ArgumentError) do
|
110
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_listen_option_float_delay
|
115
|
+
tmp = Tempfile.new('unicorn_config')
|
116
|
+
expect = { :delay => 0.5 }
|
117
|
+
listener = "127.0.0.1:12345"
|
118
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
119
|
+
assert_nothing_raised do
|
120
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_listen_option_int_delay
|
125
|
+
tmp = Tempfile.new('unicorn_config')
|
126
|
+
expect = { :delay => 5 }
|
127
|
+
listener = "127.0.0.1:12345"
|
128
|
+
tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
|
129
|
+
assert_nothing_raised do
|
130
|
+
Unicorn::Configurator.new(:config_file => tmp.path)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def test_after_fork_proc
|
135
|
+
test_struct = TestStruct.new
|
136
|
+
[ proc { |a,b| }, Proc.new { |a,b| }, lambda { |a,b| } ].each do |my_proc|
|
137
|
+
Unicorn::Configurator.new(:after_fork => my_proc).commit!(test_struct)
|
138
|
+
assert_equal my_proc, test_struct.after_fork
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_after_fork_wrong_arity
|
143
|
+
[ proc { |a| }, Proc.new { }, lambda { |a,b,c| } ].each do |my_proc|
|
144
|
+
assert_raises(ArgumentError) do
|
145
|
+
Unicorn::Configurator.new(:after_fork => my_proc)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,555 @@
|
|
1
|
+
# -*- encoding: binary -*-
|
2
|
+
|
3
|
+
# Copyright (c) 2005 Zed A. Shaw
|
4
|
+
# You can redistribute it and/or modify it under the same terms as Ruby.
|
5
|
+
#
|
6
|
+
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
7
|
+
# for more information.
|
8
|
+
|
9
|
+
require 'test/test_helper'
|
10
|
+
|
11
|
+
include Unicorn
|
12
|
+
|
13
|
+
class HttpParserTest < Test::Unit::TestCase
|
14
|
+
|
15
|
+
def test_parse_simple
|
16
|
+
parser = HttpParser.new
|
17
|
+
req = {}
|
18
|
+
http = "GET / HTTP/1.1\r\n\r\n"
|
19
|
+
assert_equal req, parser.headers(req, http)
|
20
|
+
assert_equal '', http
|
21
|
+
|
22
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
23
|
+
assert_equal '/', req['REQUEST_PATH']
|
24
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
25
|
+
assert_equal '/', req['REQUEST_URI']
|
26
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
27
|
+
assert_nil req['FRAGMENT']
|
28
|
+
assert_equal '', req['QUERY_STRING']
|
29
|
+
|
30
|
+
assert parser.keepalive?
|
31
|
+
parser.reset
|
32
|
+
req.clear
|
33
|
+
|
34
|
+
http = "G"
|
35
|
+
assert_nil parser.headers(req, http)
|
36
|
+
assert_equal "G", http
|
37
|
+
assert req.empty?
|
38
|
+
|
39
|
+
# try parsing again to ensure we were reset correctly
|
40
|
+
http = "GET /hello-world HTTP/1.1\r\n\r\n"
|
41
|
+
assert parser.headers(req, http)
|
42
|
+
|
43
|
+
assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
|
44
|
+
assert_equal '/hello-world', req['REQUEST_PATH']
|
45
|
+
assert_equal 'HTTP/1.1', req['HTTP_VERSION']
|
46
|
+
assert_equal '/hello-world', req['REQUEST_URI']
|
47
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
48
|
+
assert_nil req['FRAGMENT']
|
49
|
+
assert_equal '', req['QUERY_STRING']
|
50
|
+
assert_equal '', http
|
51
|
+
assert parser.keepalive?
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_tab_lws
|
55
|
+
parser = HttpParser.new
|
56
|
+
req = {}
|
57
|
+
tmp = "GET / HTTP/1.1\r\nHost:\tfoo.bar\r\n\r\n"
|
58
|
+
assert_equal req.object_id, parser.headers(req, tmp).object_id
|
59
|
+
assert_equal "foo.bar", req['HTTP_HOST']
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_connection_close_no_ka
|
63
|
+
parser = HttpParser.new
|
64
|
+
req = {}
|
65
|
+
tmp = "GET / HTTP/1.1\r\nConnection: close\r\n\r\n"
|
66
|
+
assert_equal req.object_id, parser.headers(req, tmp).object_id
|
67
|
+
assert_equal "GET", req['REQUEST_METHOD']
|
68
|
+
assert ! parser.keepalive?
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_connection_keep_alive_ka
|
72
|
+
parser = HttpParser.new
|
73
|
+
req = {}
|
74
|
+
tmp = "HEAD / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
|
75
|
+
assert_equal req.object_id, parser.headers(req, tmp).object_id
|
76
|
+
assert parser.keepalive?
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_connection_keep_alive_ka_bad_method
|
80
|
+
parser = HttpParser.new
|
81
|
+
req = {}
|
82
|
+
tmp = "POST / HTTP/1.1\r\nConnection: keep-alive\r\n\r\n"
|
83
|
+
assert_equal req.object_id, parser.headers(req, tmp).object_id
|
84
|
+
assert ! parser.keepalive?
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_connection_keep_alive_ka_bad_version
|
88
|
+
parser = HttpParser.new
|
89
|
+
req = {}
|
90
|
+
tmp = "GET / HTTP/1.0\r\nConnection: keep-alive\r\n\r\n"
|
91
|
+
assert_equal req.object_id, parser.headers(req, tmp).object_id
|
92
|
+
assert parser.keepalive?
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_parse_server_host_default_port
|
96
|
+
parser = HttpParser.new
|
97
|
+
req = {}
|
98
|
+
tmp = "GET / HTTP/1.1\r\nHost: foo\r\n\r\n"
|
99
|
+
assert_equal req, parser.headers(req, tmp)
|
100
|
+
assert_equal 'foo', req['SERVER_NAME']
|
101
|
+
assert_equal '80', req['SERVER_PORT']
|
102
|
+
assert_equal '', tmp
|
103
|
+
assert parser.keepalive?
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_parse_server_host_alt_port
|
107
|
+
parser = HttpParser.new
|
108
|
+
req = {}
|
109
|
+
tmp = "GET / HTTP/1.1\r\nHost: foo:999\r\n\r\n"
|
110
|
+
assert_equal req, parser.headers(req, tmp)
|
111
|
+
assert_equal 'foo', req['SERVER_NAME']
|
112
|
+
assert_equal '999', req['SERVER_PORT']
|
113
|
+
assert_equal '', tmp
|
114
|
+
assert parser.keepalive?
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_parse_server_host_empty_port
|
118
|
+
parser = HttpParser.new
|
119
|
+
req = {}
|
120
|
+
tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n\r\n"
|
121
|
+
assert_equal req, parser.headers(req, tmp)
|
122
|
+
assert_equal 'foo', req['SERVER_NAME']
|
123
|
+
assert_equal '80', req['SERVER_PORT']
|
124
|
+
assert_equal '', tmp
|
125
|
+
assert parser.keepalive?
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_parse_server_host_xfp_https
|
129
|
+
parser = HttpParser.new
|
130
|
+
req = {}
|
131
|
+
tmp = "GET / HTTP/1.1\r\nHost: foo:\r\n" \
|
132
|
+
"X-Forwarded-Proto: https\r\n\r\n"
|
133
|
+
assert_equal req, parser.headers(req, tmp)
|
134
|
+
assert_equal 'foo', req['SERVER_NAME']
|
135
|
+
assert_equal '443', req['SERVER_PORT']
|
136
|
+
assert_equal '', tmp
|
137
|
+
assert parser.keepalive?
|
138
|
+
end
|
139
|
+
|
140
|
+
def test_parse_strange_headers
|
141
|
+
parser = HttpParser.new
|
142
|
+
req = {}
|
143
|
+
should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
|
144
|
+
assert_equal req, parser.headers(req, should_be_good)
|
145
|
+
assert_equal '', should_be_good
|
146
|
+
assert parser.keepalive?
|
147
|
+
end
|
148
|
+
|
149
|
+
# legacy test case from Mongrel that we never supported before...
|
150
|
+
# I still consider Pound irrelevant, unfortunately stupid clients that
|
151
|
+
# send extremely big headers do exist and they've managed to find Unicorn...
|
152
|
+
def test_nasty_pound_header
|
153
|
+
parser = HttpParser.new
|
154
|
+
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"
|
155
|
+
req = {}
|
156
|
+
buf = nasty_pound_header.dup
|
157
|
+
|
158
|
+
assert nasty_pound_header =~ /(-----BEGIN .*--END CERTIFICATE-----)/m
|
159
|
+
expect = $1.dup
|
160
|
+
expect.gsub!(/\r\n\t/, ' ')
|
161
|
+
assert_equal req, parser.headers(req, buf)
|
162
|
+
assert_equal '', buf
|
163
|
+
assert_equal expect, req['HTTP_X_SSL_BULLSHIT']
|
164
|
+
end
|
165
|
+
|
166
|
+
def test_continuation_eats_leading_spaces
|
167
|
+
parser = HttpParser.new
|
168
|
+
header = "GET / HTTP/1.1\r\n" \
|
169
|
+
"X-ASDF: \r\n" \
|
170
|
+
"\t\r\n" \
|
171
|
+
" \r\n" \
|
172
|
+
" ASDF\r\n\r\n"
|
173
|
+
req = {}
|
174
|
+
assert_equal req, parser.headers(req, header)
|
175
|
+
assert_equal '', header
|
176
|
+
assert_equal 'ASDF', req['HTTP_X_ASDF']
|
177
|
+
end
|
178
|
+
|
179
|
+
def test_continuation_eats_scattered_leading_spaces
|
180
|
+
parser = HttpParser.new
|
181
|
+
header = "GET / HTTP/1.1\r\n" \
|
182
|
+
"X-ASDF: hi\r\n" \
|
183
|
+
" y\r\n" \
|
184
|
+
"\t\r\n" \
|
185
|
+
" x\r\n" \
|
186
|
+
" ASDF\r\n\r\n"
|
187
|
+
req = {}
|
188
|
+
assert_equal req, parser.headers(req, header)
|
189
|
+
assert_equal '', header
|
190
|
+
assert_equal 'hi y x ASDF', req['HTTP_X_ASDF']
|
191
|
+
end
|
192
|
+
|
193
|
+
def test_continuation_with_absolute_uri_and_ignored_host_header
|
194
|
+
parser = HttpParser.new
|
195
|
+
header = "GET http://example.com/ HTTP/1.1\r\n" \
|
196
|
+
"Host: \r\n" \
|
197
|
+
" YHBT.net\r\n" \
|
198
|
+
"\r\n"
|
199
|
+
req = {}
|
200
|
+
assert_equal req, parser.headers(req, header)
|
201
|
+
assert_equal 'example.com', req['HTTP_HOST']
|
202
|
+
end
|
203
|
+
|
204
|
+
# this may seem to be testing more of an implementation detail, but
|
205
|
+
# it also helps ensure we're safe in the presence of multiple parsers
|
206
|
+
# in case we ever go multithreaded/evented...
|
207
|
+
def test_resumable_continuations
|
208
|
+
nr = 1000
|
209
|
+
req = {}
|
210
|
+
header = "GET / HTTP/1.1\r\n" \
|
211
|
+
"X-ASDF: \r\n" \
|
212
|
+
" hello\r\n"
|
213
|
+
tmp = []
|
214
|
+
nr.times { |i|
|
215
|
+
parser = HttpParser.new
|
216
|
+
assert parser.headers(req, "#{header} #{i}\r\n").nil?
|
217
|
+
asdf = req['HTTP_X_ASDF']
|
218
|
+
assert_equal "hello #{i}", asdf
|
219
|
+
tmp << [ parser, asdf ]
|
220
|
+
req.clear
|
221
|
+
}
|
222
|
+
tmp.each_with_index { |(parser, asdf), i|
|
223
|
+
assert_equal req, parser.headers(req, "#{header} #{i}\r\n .\r\n\r\n")
|
224
|
+
assert_equal "hello #{i} .", asdf
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_invalid_continuation
|
229
|
+
parser = HttpParser.new
|
230
|
+
header = "GET / HTTP/1.1\r\n" \
|
231
|
+
" y\r\n" \
|
232
|
+
"Host: hello\r\n" \
|
233
|
+
"\r\n"
|
234
|
+
req = {}
|
235
|
+
assert_raises(HttpParserError) { parser.headers(req, header) }
|
236
|
+
end
|
237
|
+
|
238
|
+
def test_parse_ie6_urls
|
239
|
+
%w(/some/random/path"
|
240
|
+
/some/random/path>
|
241
|
+
/some/random/path<
|
242
|
+
/we/love/you/ie6?q=<"">
|
243
|
+
/url?<="&>="
|
244
|
+
/mal"formed"?
|
245
|
+
).each do |path|
|
246
|
+
parser = HttpParser.new
|
247
|
+
req = {}
|
248
|
+
sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
|
249
|
+
assert_equal req, parser.headers(req, sorta_safe)
|
250
|
+
assert_equal path, req['REQUEST_URI']
|
251
|
+
assert_equal '', sorta_safe
|
252
|
+
assert parser.keepalive?
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_parse_error
|
257
|
+
parser = HttpParser.new
|
258
|
+
req = {}
|
259
|
+
bad_http = "GET / SsUTF/1.1"
|
260
|
+
|
261
|
+
assert_raises(HttpParserError) { parser.headers(req, bad_http) }
|
262
|
+
|
263
|
+
# make sure we can recover
|
264
|
+
parser.reset
|
265
|
+
req.clear
|
266
|
+
assert_equal req, parser.headers(req, "GET / HTTP/1.0\r\n\r\n")
|
267
|
+
assert ! parser.keepalive?
|
268
|
+
end
|
269
|
+
|
270
|
+
def test_piecemeal
|
271
|
+
parser = HttpParser.new
|
272
|
+
req = {}
|
273
|
+
http = "GET"
|
274
|
+
assert_nil parser.headers(req, http)
|
275
|
+
assert_nil parser.headers(req, http)
|
276
|
+
assert_nil parser.headers(req, http << " / HTTP/1.0")
|
277
|
+
assert_equal '/', req['REQUEST_PATH']
|
278
|
+
assert_equal '/', req['REQUEST_URI']
|
279
|
+
assert_equal 'GET', req['REQUEST_METHOD']
|
280
|
+
assert_nil parser.headers(req, http << "\r\n")
|
281
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
282
|
+
assert_nil parser.headers(req, http << "\r")
|
283
|
+
assert_equal req, parser.headers(req, http << "\n")
|
284
|
+
assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
|
285
|
+
assert_nil req['FRAGMENT']
|
286
|
+
assert_equal '', req['QUERY_STRING']
|
287
|
+
assert_equal "", http
|
288
|
+
assert ! parser.keepalive?
|
289
|
+
end
|
290
|
+
|
291
|
+
# not common, but underscores do appear in practice
|
292
|
+
def test_absolute_uri_underscores
|
293
|
+
parser = HttpParser.new
|
294
|
+
req = {}
|
295
|
+
http = "GET http://under_score.example.com/foo?q=bar HTTP/1.0\r\n\r\n"
|
296
|
+
assert_equal req, parser.headers(req, http)
|
297
|
+
assert_equal 'http', req['rack.url_scheme']
|
298
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
299
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
300
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
301
|
+
|
302
|
+
assert_equal 'under_score.example.com', req['HTTP_HOST']
|
303
|
+
assert_equal 'under_score.example.com', req['SERVER_NAME']
|
304
|
+
assert_equal '80', req['SERVER_PORT']
|
305
|
+
assert_equal "", http
|
306
|
+
assert ! parser.keepalive?
|
307
|
+
end
|
308
|
+
|
309
|
+
# some dumb clients add users because they're stupid
|
310
|
+
def test_absolute_uri_w_user
|
311
|
+
parser = HttpParser.new
|
312
|
+
req = {}
|
313
|
+
http = "GET http://user%20space@example.com/foo?q=bar HTTP/1.0\r\n\r\n"
|
314
|
+
assert_equal req, parser.headers(req, http)
|
315
|
+
assert_equal 'http', req['rack.url_scheme']
|
316
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
317
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
318
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
319
|
+
|
320
|
+
assert_equal 'example.com', req['HTTP_HOST']
|
321
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
322
|
+
assert_equal '80', req['SERVER_PORT']
|
323
|
+
assert_equal "", http
|
324
|
+
assert ! parser.keepalive?
|
325
|
+
end
|
326
|
+
|
327
|
+
# since Mongrel supported anything URI.parse supported, we're stuck
|
328
|
+
# supporting everything URI.parse supports
|
329
|
+
def test_absolute_uri_uri_parse
|
330
|
+
"#{URI::REGEXP::PATTERN::UNRESERVED};:&=+$,".split(//).each do |char|
|
331
|
+
parser = HttpParser.new
|
332
|
+
req = {}
|
333
|
+
http = "GET http://#{char}@example.com/ HTTP/1.0\r\n\r\n"
|
334
|
+
assert_equal req, parser.headers(req, http)
|
335
|
+
assert_equal 'http', req['rack.url_scheme']
|
336
|
+
assert_equal '/', req['REQUEST_URI']
|
337
|
+
assert_equal '/', req['REQUEST_PATH']
|
338
|
+
assert_equal '', req['QUERY_STRING']
|
339
|
+
|
340
|
+
assert_equal 'example.com', req['HTTP_HOST']
|
341
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
342
|
+
assert_equal '80', req['SERVER_PORT']
|
343
|
+
assert_equal "", http
|
344
|
+
assert ! parser.keepalive?
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def test_absolute_uri
|
349
|
+
parser = HttpParser.new
|
350
|
+
req = {}
|
351
|
+
http = "GET http://example.com/foo?q=bar HTTP/1.0\r\n\r\n"
|
352
|
+
assert_equal req, parser.headers(req, http)
|
353
|
+
assert_equal 'http', req['rack.url_scheme']
|
354
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
355
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
356
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
357
|
+
|
358
|
+
assert_equal 'example.com', req['HTTP_HOST']
|
359
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
360
|
+
assert_equal '80', req['SERVER_PORT']
|
361
|
+
assert_equal "", http
|
362
|
+
assert ! parser.keepalive?
|
363
|
+
end
|
364
|
+
|
365
|
+
# X-Forwarded-Proto is not in rfc2616, absolute URIs are, however...
|
366
|
+
def test_absolute_uri_https
|
367
|
+
parser = HttpParser.new
|
368
|
+
req = {}
|
369
|
+
http = "GET https://example.com/foo?q=bar HTTP/1.1\r\n" \
|
370
|
+
"X-Forwarded-Proto: http\r\n\r\n"
|
371
|
+
assert_equal req, parser.headers(req, http)
|
372
|
+
assert_equal 'https', req['rack.url_scheme']
|
373
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
374
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
375
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
376
|
+
|
377
|
+
assert_equal 'example.com', req['HTTP_HOST']
|
378
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
379
|
+
assert_equal '443', req['SERVER_PORT']
|
380
|
+
assert_equal "", http
|
381
|
+
assert parser.keepalive?
|
382
|
+
end
|
383
|
+
|
384
|
+
# Host: header should be ignored for absolute URIs
|
385
|
+
def test_absolute_uri_with_port
|
386
|
+
parser = HttpParser.new
|
387
|
+
req = {}
|
388
|
+
http = "GET http://example.com:8080/foo?q=bar HTTP/1.2\r\n" \
|
389
|
+
"Host: bad.example.com\r\n\r\n"
|
390
|
+
assert_equal req, parser.headers(req, http)
|
391
|
+
assert_equal 'http', req['rack.url_scheme']
|
392
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
393
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
394
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
395
|
+
|
396
|
+
assert_equal 'example.com:8080', req['HTTP_HOST']
|
397
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
398
|
+
assert_equal '8080', req['SERVER_PORT']
|
399
|
+
assert_equal "", http
|
400
|
+
assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
|
401
|
+
end
|
402
|
+
|
403
|
+
def test_absolute_uri_with_empty_port
|
404
|
+
parser = HttpParser.new
|
405
|
+
req = {}
|
406
|
+
http = "GET https://example.com:/foo?q=bar HTTP/1.1\r\n" \
|
407
|
+
"Host: bad.example.com\r\n\r\n"
|
408
|
+
assert_equal req, parser.headers(req, http)
|
409
|
+
assert_equal 'https', req['rack.url_scheme']
|
410
|
+
assert_equal '/foo?q=bar', req['REQUEST_URI']
|
411
|
+
assert_equal '/foo', req['REQUEST_PATH']
|
412
|
+
assert_equal 'q=bar', req['QUERY_STRING']
|
413
|
+
|
414
|
+
assert_equal 'example.com:', req['HTTP_HOST']
|
415
|
+
assert_equal 'example.com', req['SERVER_NAME']
|
416
|
+
assert_equal '443', req['SERVER_PORT']
|
417
|
+
assert_equal "", http
|
418
|
+
assert parser.keepalive? # TODO: read HTTP/1.2 when it's final
|
419
|
+
end
|
420
|
+
|
421
|
+
def test_put_body_oneshot
|
422
|
+
parser = HttpParser.new
|
423
|
+
req = {}
|
424
|
+
http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
|
425
|
+
assert_equal req, parser.headers(req, http)
|
426
|
+
assert_equal '/', req['REQUEST_PATH']
|
427
|
+
assert_equal '/', req['REQUEST_URI']
|
428
|
+
assert_equal 'PUT', req['REQUEST_METHOD']
|
429
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
430
|
+
assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
|
431
|
+
assert_equal "abcde", http
|
432
|
+
assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
|
433
|
+
end
|
434
|
+
|
435
|
+
def test_put_body_later
|
436
|
+
parser = HttpParser.new
|
437
|
+
req = {}
|
438
|
+
http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
|
439
|
+
assert_equal req, parser.headers(req, http)
|
440
|
+
assert_equal '/l', req['REQUEST_PATH']
|
441
|
+
assert_equal '/l', req['REQUEST_URI']
|
442
|
+
assert_equal 'PUT', req['REQUEST_METHOD']
|
443
|
+
assert_equal 'HTTP/1.0', req['HTTP_VERSION']
|
444
|
+
assert_equal 'HTTP/1.0', req['SERVER_PROTOCOL']
|
445
|
+
assert_equal "", http
|
446
|
+
assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
|
447
|
+
end
|
448
|
+
|
449
|
+
def test_unknown_methods
|
450
|
+
%w(GETT HEADR XGET XHEAD).each { |m|
|
451
|
+
parser = HttpParser.new
|
452
|
+
req = {}
|
453
|
+
s = "#{m} /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
454
|
+
ok = false
|
455
|
+
assert_nothing_raised do
|
456
|
+
ok = parser.headers(req, s)
|
457
|
+
end
|
458
|
+
assert ok
|
459
|
+
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
460
|
+
assert_equal 'posts-17408', req['FRAGMENT']
|
461
|
+
assert_equal 'page=1', req['QUERY_STRING']
|
462
|
+
assert_equal "", s
|
463
|
+
assert_equal m, req['REQUEST_METHOD']
|
464
|
+
assert ! parser.keepalive? # TODO: read HTTP/1.2 when it's final
|
465
|
+
}
|
466
|
+
end
|
467
|
+
|
468
|
+
def test_fragment_in_uri
|
469
|
+
parser = HttpParser.new
|
470
|
+
req = {}
|
471
|
+
get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
|
472
|
+
ok = false
|
473
|
+
assert_nothing_raised do
|
474
|
+
ok = parser.headers(req, get)
|
475
|
+
end
|
476
|
+
assert ok
|
477
|
+
assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
|
478
|
+
assert_equal 'posts-17408', req['FRAGMENT']
|
479
|
+
assert_equal 'page=1', req['QUERY_STRING']
|
480
|
+
assert_equal '', get
|
481
|
+
assert parser.keepalive?
|
482
|
+
end
|
483
|
+
|
484
|
+
# lame random garbage maker
|
485
|
+
def rand_data(min, max, readable=true)
|
486
|
+
count = min + ((rand(max)+1) *10).to_i
|
487
|
+
res = count.to_s + "/"
|
488
|
+
|
489
|
+
if readable
|
490
|
+
res << Digest::SHA1.hexdigest(rand(count * 100).to_s) * (count / 40)
|
491
|
+
else
|
492
|
+
res << Digest::SHA1.digest(rand(count * 100).to_s) * (count / 20)
|
493
|
+
end
|
494
|
+
|
495
|
+
return res
|
496
|
+
end
|
497
|
+
|
498
|
+
|
499
|
+
def test_horrible_queries
|
500
|
+
parser = HttpParser.new
|
501
|
+
|
502
|
+
# then that large header names are caught
|
503
|
+
10.times do |c|
|
504
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
|
505
|
+
assert_raises Unicorn::HttpParserError do
|
506
|
+
parser.headers({}, get)
|
507
|
+
parser.reset
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
# then that large mangled field values are caught
|
512
|
+
10.times do |c|
|
513
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
514
|
+
assert_raises Unicorn::HttpParserError do
|
515
|
+
parser.headers({}, get)
|
516
|
+
parser.reset
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
520
|
+
# then large headers are rejected too
|
521
|
+
get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
|
522
|
+
get << "X-Test: test\r\n" * (80 * 1024)
|
523
|
+
assert_raises Unicorn::HttpParserError do
|
524
|
+
parser.headers({}, get)
|
525
|
+
parser.reset
|
526
|
+
end
|
527
|
+
|
528
|
+
# finally just that random garbage gets blocked all the time
|
529
|
+
10.times do |c|
|
530
|
+
get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
|
531
|
+
assert_raises Unicorn::HttpParserError do
|
532
|
+
parser.headers({}, get)
|
533
|
+
parser.reset
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
end
|
538
|
+
|
539
|
+
# so we don't care about the portability of this test
|
540
|
+
# if it doesn't leak on Linux, it won't leak anywhere else
|
541
|
+
# unless your C compiler or platform is otherwise broken
|
542
|
+
LINUX_PROC_PID_STATUS = "/proc/self/status"
|
543
|
+
def test_memory_leak
|
544
|
+
match_rss = /^VmRSS:\s+(\d+)/
|
545
|
+
if File.read(LINUX_PROC_PID_STATUS) =~ match_rss
|
546
|
+
before = $1.to_i
|
547
|
+
1000000.times { Unicorn::HttpParser.new }
|
548
|
+
File.read(LINUX_PROC_PID_STATUS) =~ match_rss
|
549
|
+
after = $1.to_i
|
550
|
+
diff = after - before
|
551
|
+
assert(diff < 10000, "memory grew more than 10M: #{diff}")
|
552
|
+
end
|
553
|
+
end if RUBY_PLATFORM =~ /linux/ && test(?r, LINUX_PROC_PID_STATUS)
|
554
|
+
|
555
|
+
end
|