rack 1.6.11 → 2.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rack might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/COPYING +1 -1
- data/HISTORY.md +138 -8
- data/README.rdoc +18 -28
- data/Rakefile +6 -14
- data/SPEC +10 -11
- data/contrib/rack_logo.svg +164 -111
- data/example/protectedlobster.rb +1 -1
- data/example/protectedlobster.ru +1 -1
- data/lib/rack.rb +70 -21
- data/lib/rack/auth/abstract/request.rb +5 -1
- data/lib/rack/auth/digest/params.rb +2 -3
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/body_proxy.rb +14 -9
- data/lib/rack/builder.rb +3 -3
- data/lib/rack/chunked.rb +5 -5
- data/lib/rack/{commonlogger.rb → common_logger.rb} +3 -3
- data/lib/rack/{conditionalget.rb → conditional_get.rb} +0 -0
- data/lib/rack/content_length.rb +2 -2
- data/lib/rack/deflater.rb +4 -39
- data/lib/rack/directory.rb +66 -54
- data/lib/rack/etag.rb +5 -4
- data/lib/rack/events.rb +154 -0
- data/lib/rack/file.rb +64 -40
- data/lib/rack/handler.rb +3 -25
- data/lib/rack/handler/cgi.rb +15 -16
- data/lib/rack/handler/fastcgi.rb +13 -14
- data/lib/rack/handler/lsws.rb +11 -11
- data/lib/rack/handler/scgi.rb +15 -15
- data/lib/rack/handler/thin.rb +3 -0
- data/lib/rack/handler/webrick.rb +24 -26
- data/lib/rack/head.rb +15 -17
- data/lib/rack/lint.rb +40 -40
- data/lib/rack/lobster.rb +1 -1
- data/lib/rack/lock.rb +15 -10
- data/lib/rack/logger.rb +2 -2
- data/lib/rack/media_type.rb +38 -0
- data/lib/rack/{methodoverride.rb → method_override.rb} +6 -6
- data/lib/rack/mime.rb +18 -5
- data/lib/rack/mock.rb +36 -54
- data/lib/rack/multipart.rb +35 -6
- data/lib/rack/multipart/generator.rb +5 -5
- data/lib/rack/multipart/parser.rb +270 -157
- data/lib/rack/multipart/uploaded_file.rb +1 -2
- data/lib/rack/{nulllogger.rb → null_logger.rb} +1 -1
- data/lib/rack/query_parser.rb +192 -0
- data/lib/rack/recursive.rb +8 -8
- data/lib/rack/request.rb +394 -305
- data/lib/rack/response.rb +130 -57
- data/lib/rack/rewindable_input.rb +1 -12
- data/lib/rack/runtime.rb +10 -18
- data/lib/rack/sendfile.rb +5 -7
- data/lib/rack/server.rb +30 -23
- data/lib/rack/session/abstract/id.rb +121 -75
- data/lib/rack/session/cookie.rb +25 -18
- data/lib/rack/session/memcache.rb +2 -2
- data/lib/rack/session/pool.rb +9 -9
- data/lib/rack/show_exceptions.rb +386 -0
- data/lib/rack/{showstatus.rb → show_status.rb} +3 -3
- data/lib/rack/static.rb +30 -5
- data/lib/rack/tempfile_reaper.rb +2 -2
- data/lib/rack/urlmap.rb +15 -14
- data/lib/rack/utils.rb +136 -211
- data/rack.gemspec +10 -9
- data/test/builder/an_underscore_app.rb +5 -0
- data/test/builder/options.ru +1 -1
- data/test/cgi/test.fcgi +1 -0
- data/test/cgi/test.gz +0 -0
- data/test/helper.rb +34 -0
- data/test/multipart/filename_with_encoded_words +7 -0
- data/test/multipart/filename_with_single_quote +7 -0
- data/test/multipart/quoted +15 -0
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/unity3d_wwwform +11 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +1 -1
- data/test/spec_auth_basic.rb +27 -19
- data/test/spec_auth_digest.rb +47 -46
- data/test/spec_body_proxy.rb +27 -27
- data/test/spec_builder.rb +51 -41
- data/test/spec_cascade.rb +24 -22
- data/test/spec_cgi.rb +49 -67
- data/test/spec_chunked.rb +37 -35
- data/test/{spec_commonlogger.rb → spec_common_logger.rb} +23 -21
- data/test/{spec_conditionalget.rb → spec_conditional_get.rb} +29 -28
- data/test/spec_config.rb +3 -2
- data/test/spec_content_length.rb +18 -17
- data/test/spec_content_type.rb +13 -12
- data/test/spec_deflater.rb +85 -49
- data/test/spec_directory.rb +87 -27
- data/test/spec_etag.rb +32 -31
- data/test/spec_events.rb +133 -0
- data/test/spec_fastcgi.rb +50 -72
- data/test/spec_file.rb +120 -77
- data/test/spec_handler.rb +19 -34
- data/test/spec_head.rb +15 -14
- data/test/spec_lint.rb +164 -199
- data/test/spec_lobster.rb +24 -23
- data/test/spec_lock.rb +79 -39
- data/test/spec_logger.rb +4 -3
- data/test/spec_media_type.rb +42 -0
- data/test/{spec_methodoverride.rb → spec_method_override.rb} +34 -35
- data/test/spec_mime.rb +19 -19
- data/test/spec_mock.rb +206 -144
- data/test/spec_multipart.rb +322 -200
- data/test/{spec_nulllogger.rb → spec_null_logger.rb} +5 -4
- data/test/spec_recursive.rb +17 -14
- data/test/spec_request.rb +780 -605
- data/test/spec_response.rb +215 -112
- data/test/spec_rewindable_input.rb +50 -40
- data/test/spec_runtime.rb +11 -10
- data/test/spec_sendfile.rb +30 -35
- data/test/spec_server.rb +78 -52
- data/test/spec_session_abstract_id.rb +11 -33
- data/test/spec_session_abstract_session_hash.rb +45 -0
- data/test/spec_session_cookie.rb +99 -67
- data/test/spec_session_memcache.rb +60 -61
- data/test/spec_session_pool.rb +45 -44
- data/test/{spec_showexceptions.rb → spec_show_exceptions.rb} +22 -27
- data/test/{spec_showstatus.rb → spec_show_status.rb} +36 -35
- data/test/spec_static.rb +71 -32
- data/test/spec_tempfile_reaper.rb +11 -10
- data/test/spec_thin.rb +55 -50
- data/test/spec_urlmap.rb +79 -78
- data/test/spec_utils.rb +441 -346
- data/test/spec_version.rb +2 -8
- data/test/spec_webrick.rb +93 -71
- data/test/static/foo.html +1 -0
- data/test/testrequest.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered.rb +1 -1
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +1 -1
- metadata +92 -71
- data/KNOWN-ISSUES +0 -44
- data/lib/rack/backports/uri/common_18.rb +0 -56
- data/lib/rack/backports/uri/common_192.rb +0 -52
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/lib/rack/handler/evented_mongrel.rb +0 -8
- data/lib/rack/handler/mongrel.rb +0 -106
- data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
- data/lib/rack/showexceptions.rb +0 -387
- data/lib/rack/utils/okjson.rb +0 -600
- data/test/spec_mongrel.rb +0 -182
@@ -1,9 +1,10 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/lint'
|
2
3
|
require 'rack/mock'
|
3
|
-
require 'rack/
|
4
|
+
require 'rack/null_logger'
|
4
5
|
|
5
6
|
describe Rack::NullLogger do
|
6
|
-
|
7
|
+
it "act as a noop logger" do
|
7
8
|
app = lambda { |env|
|
8
9
|
env['rack.logger'].warn "b00m"
|
9
10
|
[200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
|
@@ -12,9 +13,9 @@ describe Rack::NullLogger do
|
|
12
13
|
logger = Rack::Lint.new(Rack::NullLogger.new(app))
|
13
14
|
|
14
15
|
res = logger.call(Rack::MockRequest.env_for)
|
15
|
-
res[0..1].
|
16
|
+
res[0..1].must_equal [
|
16
17
|
200, {'Content-Type' => 'text/plain'}
|
17
18
|
]
|
18
|
-
res[2].to_enum.to_a.
|
19
|
+
res[2].to_enum.to_a.must_equal ["Hello, World!"]
|
19
20
|
end
|
20
21
|
end
|
data/test/spec_recursive.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'rack/lint'
|
2
3
|
require 'rack/recursive'
|
3
4
|
require 'rack/mock'
|
4
5
|
|
5
6
|
describe Rack::Recursive do
|
7
|
+
before do
|
6
8
|
@app1 = lambda { |env|
|
7
9
|
res = Rack::Response.new
|
8
10
|
res["X-Path-Info"] = env["PATH_INFO"]
|
@@ -29,44 +31,45 @@ describe Rack::Recursive do
|
|
29
31
|
@app4 = lambda { |env|
|
30
32
|
raise Rack::ForwardRequest.new("http://example.org/app1/quux?meh")
|
31
33
|
}
|
32
|
-
|
34
|
+
end
|
35
|
+
|
33
36
|
def recursive(map)
|
34
37
|
Rack::Lint.new Rack::Recursive.new(Rack::URLMap.new(map))
|
35
38
|
end
|
36
39
|
|
37
|
-
|
40
|
+
it "allow for subrequests" do
|
38
41
|
res = Rack::MockRequest.new(recursive("/app1" => @app1,
|
39
42
|
"/app2" => @app2)).
|
40
43
|
get("/app2")
|
41
44
|
|
42
|
-
res.
|
43
|
-
res.body.
|
45
|
+
res.must_be :ok?
|
46
|
+
res.body.must_equal "App2App1"
|
44
47
|
end
|
45
48
|
|
46
|
-
|
49
|
+
it "raise error on requests not below the app" do
|
47
50
|
app = Rack::URLMap.new("/app1" => @app1,
|
48
51
|
"/app" => recursive("/1" => @app1,
|
49
52
|
"/2" => @app2))
|
50
53
|
|
51
54
|
lambda {
|
52
55
|
Rack::MockRequest.new(app).get("/app/2")
|
53
|
-
}.
|
54
|
-
message.
|
56
|
+
}.must_raise(ArgumentError).
|
57
|
+
message.must_match(/can only include below/)
|
55
58
|
end
|
56
59
|
|
57
|
-
|
60
|
+
it "support forwarding" do
|
58
61
|
app = recursive("/app1" => @app1,
|
59
62
|
"/app3" => @app3,
|
60
63
|
"/app4" => @app4)
|
61
64
|
|
62
65
|
res = Rack::MockRequest.new(app).get("/app3")
|
63
|
-
res.
|
64
|
-
res.body.
|
66
|
+
res.must_be :ok?
|
67
|
+
res.body.must_equal "App1"
|
65
68
|
|
66
69
|
res = Rack::MockRequest.new(app).get("/app4")
|
67
|
-
res.
|
68
|
-
res.body.
|
69
|
-
res["X-Path-Info"].
|
70
|
-
res["X-Query-String"].
|
70
|
+
res.must_be :ok?
|
71
|
+
res.body.must_equal "App1"
|
72
|
+
res["X-Path-Info"].must_equal "/quux"
|
73
|
+
res["X-Query-String"].must_equal "meh"
|
71
74
|
end
|
72
75
|
end
|
data/test/spec_request.rb
CHANGED
@@ -1,574 +1,724 @@
|
|
1
|
+
require 'minitest/autorun'
|
1
2
|
require 'stringio'
|
2
3
|
require 'cgi'
|
3
4
|
require 'rack/request'
|
4
5
|
require 'rack/mock'
|
6
|
+
require 'rack/multipart'
|
5
7
|
require 'securerandom'
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
req =
|
9
|
+
class RackRequestTest < Minitest::Spec
|
10
|
+
it "copies the env when duping" do
|
11
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
12
|
+
refute_same req.env, req.dup.env
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can check if something has been set' do
|
16
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
17
|
+
refute req.has_header?("FOO")
|
18
|
+
end
|
19
|
+
|
20
|
+
it "can get a key from the env" do
|
21
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
22
|
+
assert_equal "example.com", req.get_header("SERVER_NAME")
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can calculate the authority' do
|
26
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
27
|
+
assert_equal "example.com:8080", req.authority
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can calculate the authority without a port' do
|
31
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com/"))
|
32
|
+
assert_equal "example.com:80", req.authority
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can calculate the authority without a port on ssl' do
|
36
|
+
req = make_request(Rack::MockRequest.env_for("https://example.com/"))
|
37
|
+
assert_equal "example.com:443", req.authority
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'yields to the block if no value has been set' do
|
41
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
42
|
+
yielded = false
|
43
|
+
req.fetch_header("FOO") do
|
44
|
+
yielded = true
|
45
|
+
req.set_header "FOO", 'bar'
|
46
|
+
end
|
47
|
+
|
48
|
+
assert yielded
|
49
|
+
assert_equal "bar", req.get_header("FOO")
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'can iterate over values' do
|
53
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
54
|
+
req.set_header 'foo', 'bar'
|
55
|
+
hash = {}
|
56
|
+
req.each_header do |k,v|
|
57
|
+
hash[k] = v
|
58
|
+
end
|
59
|
+
assert_equal 'bar', hash['foo']
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'can set values in the env' do
|
63
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
64
|
+
req.set_header("FOO", "BAR")
|
65
|
+
assert_equal "BAR", req.get_header("FOO")
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'can add to multivalued headers in the env' do
|
69
|
+
req = make_request(Rack::MockRequest.env_for('http://example.com:8080/'))
|
70
|
+
|
71
|
+
assert_equal '1', req.add_header('FOO', '1')
|
72
|
+
assert_equal '1', req.get_header('FOO')
|
73
|
+
|
74
|
+
assert_equal '1,2', req.add_header('FOO', '2')
|
75
|
+
assert_equal '1,2', req.get_header('FOO')
|
76
|
+
|
77
|
+
assert_equal '1,2', req.add_header('FOO', nil)
|
78
|
+
assert_equal '1,2', req.get_header('FOO')
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'can delete env values' do
|
82
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
83
|
+
req.set_header 'foo', 'bar'
|
84
|
+
assert req.has_header? 'foo'
|
85
|
+
req.delete_header 'foo'
|
86
|
+
refute req.has_header? 'foo'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "wrap the rack variables" do
|
90
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
10
91
|
|
11
|
-
req.body.
|
12
|
-
req.scheme.
|
13
|
-
req.request_method.
|
92
|
+
req.body.must_respond_to :gets
|
93
|
+
req.scheme.must_equal "http"
|
94
|
+
req.request_method.must_equal "GET"
|
14
95
|
|
15
|
-
req.
|
16
|
-
req.
|
17
|
-
req.
|
18
|
-
req.
|
19
|
-
req.
|
20
|
-
req.
|
96
|
+
req.must_be :get?
|
97
|
+
req.wont_be :post?
|
98
|
+
req.wont_be :put?
|
99
|
+
req.wont_be :delete?
|
100
|
+
req.wont_be :head?
|
101
|
+
req.wont_be :patch?
|
21
102
|
|
22
|
-
req.script_name.
|
23
|
-
req.path_info.
|
24
|
-
req.query_string.
|
103
|
+
req.script_name.must_equal ""
|
104
|
+
req.path_info.must_equal "/"
|
105
|
+
req.query_string.must_equal ""
|
25
106
|
|
26
|
-
req.host.
|
27
|
-
req.port.
|
107
|
+
req.host.must_equal "example.com"
|
108
|
+
req.port.must_equal 8080
|
28
109
|
|
29
|
-
req.content_length.
|
30
|
-
req.content_type.
|
110
|
+
req.content_length.must_equal "0"
|
111
|
+
req.content_type.must_be_nil
|
31
112
|
end
|
32
113
|
|
33
|
-
|
34
|
-
req =
|
114
|
+
it "figure out the correct host" do
|
115
|
+
req = make_request \
|
35
116
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
|
36
|
-
req.host.
|
117
|
+
req.host.must_equal "www2.example.org"
|
37
118
|
|
38
|
-
req =
|
119
|
+
req = make_request \
|
39
120
|
Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
|
40
|
-
req.host.
|
121
|
+
req.host.must_equal "example.org"
|
41
122
|
|
42
|
-
req =
|
123
|
+
req = make_request \
|
43
124
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
|
44
|
-
req.host.
|
125
|
+
req.host.must_equal "example.org"
|
45
126
|
|
46
127
|
env = Rack::MockRequest.env_for("/", "SERVER_ADDR" => "192.168.1.1", "SERVER_PORT" => "9292")
|
47
128
|
env.delete("SERVER_NAME")
|
48
|
-
req =
|
49
|
-
req.host.
|
129
|
+
req = make_request(env)
|
130
|
+
req.host.must_equal "192.168.1.1"
|
50
131
|
|
51
132
|
env = Rack::MockRequest.env_for("/")
|
52
133
|
env.delete("SERVER_NAME")
|
53
|
-
req =
|
54
|
-
req.host.
|
134
|
+
req = make_request(env)
|
135
|
+
req.host.must_equal ""
|
55
136
|
end
|
56
137
|
|
57
|
-
|
58
|
-
req =
|
138
|
+
it "figure out the correct port" do
|
139
|
+
req = make_request \
|
59
140
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
|
60
|
-
req.port.
|
141
|
+
req.port.must_equal 80
|
61
142
|
|
62
|
-
req =
|
143
|
+
req = make_request \
|
63
144
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org:81")
|
64
|
-
req.port.
|
145
|
+
req.port.must_equal 81
|
65
146
|
|
66
|
-
req =
|
147
|
+
req = make_request \
|
67
148
|
Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
|
68
|
-
req.port.
|
149
|
+
req.port.must_equal 9292
|
69
150
|
|
70
|
-
req =
|
151
|
+
req = make_request \
|
71
152
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
|
72
|
-
req.port.
|
153
|
+
req.port.must_equal 9292
|
73
154
|
|
74
|
-
req =
|
155
|
+
req = make_request \
|
75
156
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org")
|
76
|
-
req.port.
|
157
|
+
req.port.must_equal 80
|
77
158
|
|
78
|
-
req =
|
159
|
+
req = make_request \
|
79
160
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_SSL" => "on")
|
80
|
-
req.port.
|
161
|
+
req.port.must_equal 443
|
81
162
|
|
82
|
-
req =
|
163
|
+
req = make_request \
|
83
164
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PROTO" => "https")
|
84
|
-
req.port.
|
165
|
+
req.port.must_equal 443
|
85
166
|
|
86
|
-
req =
|
167
|
+
req = make_request \
|
87
168
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "HTTP_X_FORWARDED_PORT" => "9393")
|
88
|
-
req.port.
|
169
|
+
req.port.must_equal 9393
|
89
170
|
|
90
|
-
req =
|
171
|
+
req = make_request \
|
91
172
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9393", "SERVER_PORT" => "80")
|
92
|
-
req.port.
|
173
|
+
req.port.must_equal 9393
|
93
174
|
|
94
|
-
req =
|
175
|
+
req = make_request \
|
95
176
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
|
96
|
-
req.port.
|
177
|
+
req.port.must_equal 80
|
97
178
|
|
98
|
-
req =
|
179
|
+
req = make_request \
|
99
180
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https", "SERVER_PORT" => "80")
|
100
|
-
req.port.
|
181
|
+
req.port.must_equal 443
|
101
182
|
|
102
|
-
req =
|
183
|
+
req = make_request \
|
103
184
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost", "HTTP_X_FORWARDED_PROTO" => "https,https", "SERVER_PORT" => "80")
|
104
|
-
req.port.
|
185
|
+
req.port.must_equal 443
|
105
186
|
end
|
106
187
|
|
107
|
-
|
108
|
-
req =
|
188
|
+
it "figure out the correct host with port" do
|
189
|
+
req = make_request \
|
109
190
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "www2.example.org")
|
110
|
-
req.host_with_port.
|
191
|
+
req.host_with_port.must_equal "www2.example.org"
|
111
192
|
|
112
|
-
req =
|
193
|
+
req = make_request \
|
113
194
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81")
|
114
|
-
req.host_with_port.
|
195
|
+
req.host_with_port.must_equal "localhost:81"
|
115
196
|
|
116
|
-
req =
|
197
|
+
req = make_request \
|
117
198
|
Rack::MockRequest.env_for("/", "SERVER_NAME" => "example.org", "SERVER_PORT" => "9292")
|
118
|
-
req.host_with_port.
|
199
|
+
req.host_with_port.must_equal "example.org:9292"
|
119
200
|
|
120
|
-
req =
|
201
|
+
req = make_request \
|
121
202
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org:9292")
|
122
|
-
req.host_with_port.
|
203
|
+
req.host_with_port.must_equal "example.org:9292"
|
123
204
|
|
124
|
-
req =
|
205
|
+
req = make_request \
|
125
206
|
Rack::MockRequest.env_for("/", "HTTP_HOST" => "localhost:81", "HTTP_X_FORWARDED_HOST" => "example.org", "SERVER_PORT" => "9393")
|
126
|
-
req.host_with_port.
|
207
|
+
req.host_with_port.must_equal "example.org"
|
127
208
|
end
|
128
209
|
|
129
|
-
|
130
|
-
req =
|
131
|
-
req.query_string.
|
132
|
-
req.GET.
|
133
|
-
req.POST.
|
134
|
-
req.params.
|
210
|
+
it "parse the query string" do
|
211
|
+
req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
|
212
|
+
req.query_string.must_equal "foo=bar&quux=bla"
|
213
|
+
req.GET.must_equal "foo" => "bar", "quux" => "bla"
|
214
|
+
req.POST.must_be :empty?
|
215
|
+
req.params.must_equal "foo" => "bar", "quux" => "bla"
|
135
216
|
end
|
136
217
|
|
137
|
-
|
218
|
+
it "not truncate query strings containing semi-colons #543 only in POST" do
|
138
219
|
mr = Rack::MockRequest.env_for("/",
|
139
220
|
"REQUEST_METHOD" => 'POST',
|
140
221
|
:input => "foo=bar&quux=b;la")
|
141
|
-
req =
|
142
|
-
req.query_string.
|
143
|
-
req.GET.
|
144
|
-
req.POST.
|
145
|
-
req.params.
|
222
|
+
req = make_request mr
|
223
|
+
req.query_string.must_equal ""
|
224
|
+
req.GET.must_be :empty?
|
225
|
+
req.POST.must_equal "foo" => "bar", "quux" => "b;la"
|
226
|
+
req.params.must_equal req.GET.merge(req.POST)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should use the query_parser for query parsing" do
|
230
|
+
c = Class.new(Rack::QueryParser::Params) do
|
231
|
+
def initialize(*)
|
232
|
+
super
|
233
|
+
@params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
|
234
|
+
end
|
235
|
+
end
|
236
|
+
parser = Rack::QueryParser.new(c, 65536, 100)
|
237
|
+
c = Class.new(Rack::Request) do
|
238
|
+
define_method(:query_parser) do
|
239
|
+
parser
|
240
|
+
end
|
241
|
+
end
|
242
|
+
req = c.new(Rack::MockRequest.env_for("/?foo=bar&quux=bla"))
|
243
|
+
req.GET[:foo].must_equal "bar"
|
244
|
+
req.GET[:quux].must_equal "bla"
|
245
|
+
req.params[:foo].must_equal "bar"
|
246
|
+
req.params[:quux].must_equal "bla"
|
146
247
|
end
|
147
248
|
|
148
|
-
|
149
|
-
req =
|
150
|
-
req.query_string.
|
151
|
-
req.GET.
|
152
|
-
req.POST.
|
153
|
-
req.params.
|
249
|
+
it "use semi-colons as separators for query strings in GET" do
|
250
|
+
req = make_request(Rack::MockRequest.env_for("/?foo=bar&quux=b;la;wun=duh"))
|
251
|
+
req.query_string.must_equal "foo=bar&quux=b;la;wun=duh"
|
252
|
+
req.GET.must_equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh"
|
253
|
+
req.POST.must_be :empty?
|
254
|
+
req.params.must_equal "foo" => "bar", "quux" => "b", "la" => nil, "wun" => "duh"
|
154
255
|
end
|
155
256
|
|
156
|
-
|
257
|
+
it "limit the keys from the GET query string" do
|
157
258
|
env = Rack::MockRequest.env_for("/?foo=bar")
|
158
259
|
|
159
260
|
old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
|
160
261
|
begin
|
161
|
-
req =
|
162
|
-
lambda { req.GET }.
|
262
|
+
req = make_request(env)
|
263
|
+
lambda { req.GET }.must_raise RangeError
|
163
264
|
ensure
|
164
265
|
Rack::Utils.key_space_limit = old
|
165
266
|
end
|
166
267
|
end
|
167
268
|
|
168
|
-
|
269
|
+
it "limit the key size per nested params hash" do
|
169
270
|
nested_query = Rack::MockRequest.env_for("/?foo%5Bbar%5D%5Bbaz%5D%5Bqux%5D=1")
|
170
271
|
plain_query = Rack::MockRequest.env_for("/?foo_bar__baz__qux_=1")
|
171
272
|
|
172
273
|
old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 3
|
173
274
|
begin
|
174
|
-
|
175
|
-
|
275
|
+
exp = {"foo"=>{"bar"=>{"baz"=>{"qux"=>"1"}}}}
|
276
|
+
make_request(nested_query).GET.must_equal exp
|
277
|
+
lambda { make_request(plain_query).GET }.must_raise RangeError
|
176
278
|
ensure
|
177
279
|
Rack::Utils.key_space_limit = old
|
178
280
|
end
|
179
281
|
end
|
180
282
|
|
181
|
-
|
283
|
+
it "not unify GET and POST when calling params" do
|
182
284
|
mr = Rack::MockRequest.env_for("/?foo=quux",
|
183
285
|
"REQUEST_METHOD" => 'POST',
|
184
286
|
:input => "foo=bar&quux=bla"
|
185
287
|
)
|
186
|
-
req =
|
288
|
+
req = make_request mr
|
187
289
|
|
188
290
|
req.params
|
189
291
|
|
190
|
-
req.GET.
|
191
|
-
req.POST.
|
192
|
-
req.params.
|
292
|
+
req.GET.must_equal "foo" => "quux"
|
293
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
294
|
+
req.params.must_equal req.GET.merge(req.POST)
|
193
295
|
end
|
194
296
|
|
195
|
-
|
297
|
+
it "use the query_parser's params_class for multipart params" do
|
298
|
+
c = Class.new(Rack::QueryParser::Params) do
|
299
|
+
def initialize(*)
|
300
|
+
super
|
301
|
+
@params = Hash.new{|h,k| h[k.to_s] if k.is_a?(Symbol)}
|
302
|
+
end
|
303
|
+
end
|
304
|
+
parser = Rack::QueryParser.new(c, 65536, 100)
|
305
|
+
c = Class.new(Rack::Request) do
|
306
|
+
define_method(:query_parser) do
|
307
|
+
parser
|
308
|
+
end
|
309
|
+
end
|
310
|
+
mr = Rack::MockRequest.env_for("/?foo=quux",
|
311
|
+
"REQUEST_METHOD" => 'POST',
|
312
|
+
:input => "foo=bar&quux=bla"
|
313
|
+
)
|
314
|
+
req = c.new mr
|
315
|
+
|
316
|
+
req.params
|
317
|
+
|
318
|
+
req.GET[:foo].must_equal "quux"
|
319
|
+
req.POST[:foo].must_equal "bar"
|
320
|
+
req.POST[:quux].must_equal "bla"
|
321
|
+
req.params[:foo].must_equal "bar"
|
322
|
+
req.params[:quux].must_equal "bla"
|
323
|
+
end
|
324
|
+
|
325
|
+
it "raise if input params has invalid %-encoding" do
|
196
326
|
mr = Rack::MockRequest.env_for("/?foo=quux",
|
197
327
|
"REQUEST_METHOD" => 'POST',
|
198
328
|
:input => "a%=1"
|
199
329
|
)
|
200
|
-
req =
|
330
|
+
req = make_request mr
|
201
331
|
|
202
|
-
lambda { req.POST }.
|
203
|
-
|
204
|
-
message.should.equal "invalid %-encoding (a%)"
|
332
|
+
lambda { req.POST }.must_raise(Rack::Utils::InvalidParameterError).
|
333
|
+
message.must_equal "invalid %-encoding (a%)"
|
205
334
|
end
|
206
335
|
|
207
|
-
|
208
|
-
req =
|
209
|
-
lambda { req.POST }.
|
336
|
+
it "raise if rack.input is missing" do
|
337
|
+
req = make_request({})
|
338
|
+
lambda { req.POST }.must_raise RuntimeError
|
210
339
|
end
|
211
340
|
|
212
|
-
|
213
|
-
req =
|
341
|
+
it "parse POST data when method is POST and no Content-Type given" do
|
342
|
+
req = make_request \
|
214
343
|
Rack::MockRequest.env_for("/?foo=quux",
|
215
344
|
"REQUEST_METHOD" => 'POST',
|
216
345
|
:input => "foo=bar&quux=bla")
|
217
|
-
req.content_type.
|
218
|
-
req.media_type.
|
219
|
-
req.query_string.
|
220
|
-
req.GET.
|
221
|
-
req.POST.
|
222
|
-
req.params.
|
346
|
+
req.content_type.must_be_nil
|
347
|
+
req.media_type.must_be_nil
|
348
|
+
req.query_string.must_equal "foo=quux"
|
349
|
+
req.GET.must_equal "foo" => "quux"
|
350
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
351
|
+
req.params.must_equal "foo" => "bar", "quux" => "bla"
|
223
352
|
end
|
224
353
|
|
225
|
-
|
354
|
+
it "limit the keys from the POST form data" do
|
226
355
|
env = Rack::MockRequest.env_for("",
|
227
356
|
"REQUEST_METHOD" => 'POST',
|
228
357
|
:input => "foo=bar&quux=bla")
|
229
358
|
|
230
359
|
old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1
|
231
360
|
begin
|
232
|
-
req =
|
233
|
-
lambda { req.POST }.
|
361
|
+
req = make_request(env)
|
362
|
+
lambda { req.POST }.must_raise RangeError
|
234
363
|
ensure
|
235
364
|
Rack::Utils.key_space_limit = old
|
236
365
|
end
|
237
366
|
end
|
238
367
|
|
239
|
-
|
240
|
-
req =
|
368
|
+
it "parse POST data with explicit content type regardless of method" do
|
369
|
+
req = make_request \
|
241
370
|
Rack::MockRequest.env_for("/",
|
242
371
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
|
243
372
|
:input => "foo=bar&quux=bla")
|
244
|
-
req.content_type.
|
245
|
-
req.media_type.
|
246
|
-
req.media_type_params['foo'].
|
247
|
-
req.POST.
|
248
|
-
req.params.
|
373
|
+
req.content_type.must_equal 'application/x-www-form-urlencoded;foo=bar'
|
374
|
+
req.media_type.must_equal 'application/x-www-form-urlencoded'
|
375
|
+
req.media_type_params['foo'].must_equal 'bar'
|
376
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
377
|
+
req.params.must_equal "foo" => "bar", "quux" => "bla"
|
249
378
|
end
|
250
379
|
|
251
|
-
|
252
|
-
req =
|
380
|
+
it "not parse POST data when media type is not form-data" do
|
381
|
+
req = make_request \
|
253
382
|
Rack::MockRequest.env_for("/?foo=quux",
|
254
383
|
"REQUEST_METHOD" => 'POST',
|
255
384
|
"CONTENT_TYPE" => 'text/plain;charset=utf-8',
|
256
385
|
:input => "foo=bar&quux=bla")
|
257
|
-
req.content_type.
|
258
|
-
req.media_type.
|
259
|
-
req.media_type_params['charset'].
|
260
|
-
req.POST.
|
261
|
-
req.params.
|
262
|
-
req.body.read.
|
386
|
+
req.content_type.must_equal 'text/plain;charset=utf-8'
|
387
|
+
req.media_type.must_equal 'text/plain'
|
388
|
+
req.media_type_params['charset'].must_equal 'utf-8'
|
389
|
+
req.POST.must_be :empty?
|
390
|
+
req.params.must_equal "foo" => "quux"
|
391
|
+
req.body.read.must_equal "foo=bar&quux=bla"
|
263
392
|
end
|
264
393
|
|
265
|
-
|
266
|
-
req =
|
394
|
+
it "parse POST data on PUT when media type is form-data" do
|
395
|
+
req = make_request \
|
267
396
|
Rack::MockRequest.env_for("/?foo=quux",
|
268
397
|
"REQUEST_METHOD" => 'PUT',
|
269
398
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded',
|
270
399
|
:input => "foo=bar&quux=bla")
|
271
|
-
req.POST.
|
272
|
-
req.body.read.
|
400
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
401
|
+
req.body.read.must_equal "foo=bar&quux=bla"
|
273
402
|
end
|
274
403
|
|
275
|
-
|
404
|
+
it "rewind input after parsing POST data" do
|
276
405
|
input = StringIO.new("foo=bar&quux=bla")
|
277
|
-
req =
|
406
|
+
req = make_request \
|
278
407
|
Rack::MockRequest.env_for("/",
|
279
408
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded;foo=bar',
|
280
409
|
:input => input)
|
281
|
-
req.params.
|
282
|
-
input.read.
|
410
|
+
req.params.must_equal "foo" => "bar", "quux" => "bla"
|
411
|
+
input.read.must_equal "foo=bar&quux=bla"
|
283
412
|
end
|
284
413
|
|
285
|
-
|
286
|
-
|
414
|
+
it "safely accepts POST requests with empty body" do
|
415
|
+
mr = Rack::MockRequest.env_for("/",
|
416
|
+
"REQUEST_METHOD" => "POST",
|
417
|
+
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
418
|
+
"CONTENT_LENGTH" => '0',
|
419
|
+
:input => nil)
|
420
|
+
|
421
|
+
req = make_request mr
|
422
|
+
req.query_string.must_equal ""
|
423
|
+
req.GET.must_be :empty?
|
424
|
+
req.POST.must_be :empty?
|
425
|
+
req.params.must_equal({})
|
426
|
+
end
|
427
|
+
|
428
|
+
it "clean up Safari's ajax POST body" do
|
429
|
+
req = make_request \
|
287
430
|
Rack::MockRequest.env_for("/",
|
288
431
|
'REQUEST_METHOD' => 'POST', :input => "foo=bar&quux=bla\0")
|
289
|
-
req.POST.
|
432
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
290
433
|
end
|
291
434
|
|
292
|
-
|
293
|
-
req =
|
435
|
+
it "get value by key from params with #[]" do
|
436
|
+
req = make_request \
|
294
437
|
Rack::MockRequest.env_for("?foo=quux")
|
295
|
-
req['foo'].
|
296
|
-
req[:foo].
|
438
|
+
req['foo'].must_equal 'quux'
|
439
|
+
req[:foo].must_equal 'quux'
|
297
440
|
end
|
298
441
|
|
299
|
-
|
300
|
-
req =
|
442
|
+
it "set value to key on params with #[]=" do
|
443
|
+
req = make_request \
|
301
444
|
Rack::MockRequest.env_for("?foo=duh")
|
302
|
-
req['foo'].
|
303
|
-
req[:foo].
|
304
|
-
req.params.
|
445
|
+
req['foo'].must_equal 'duh'
|
446
|
+
req[:foo].must_equal 'duh'
|
447
|
+
req.params.must_equal 'foo' => 'duh'
|
448
|
+
|
449
|
+
if req.delegate?
|
450
|
+
skip "delegate requests don't cache params, so mutations have no impact"
|
451
|
+
end
|
305
452
|
|
306
453
|
req['foo'] = 'bar'
|
307
|
-
req.params.
|
308
|
-
req['foo'].
|
309
|
-
req[:foo].
|
454
|
+
req.params.must_equal 'foo' => 'bar'
|
455
|
+
req['foo'].must_equal 'bar'
|
456
|
+
req[:foo].must_equal 'bar'
|
310
457
|
|
311
458
|
req[:foo] = 'jaz'
|
312
|
-
req.params.
|
313
|
-
req['foo'].
|
314
|
-
req[:foo].
|
459
|
+
req.params.must_equal 'foo' => 'jaz'
|
460
|
+
req['foo'].must_equal 'jaz'
|
461
|
+
req[:foo].must_equal 'jaz'
|
315
462
|
end
|
316
463
|
|
317
|
-
|
318
|
-
req =
|
464
|
+
it "return values for the keys in the order given from values_at" do
|
465
|
+
req = make_request \
|
319
466
|
Rack::MockRequest.env_for("?foo=baz&wun=der&bar=ful")
|
320
|
-
req.values_at('foo').
|
321
|
-
req.values_at('foo', 'wun').
|
322
|
-
req.values_at('bar', 'foo', 'wun').
|
467
|
+
req.values_at('foo').must_equal ['baz']
|
468
|
+
req.values_at('foo', 'wun').must_equal ['baz', 'der']
|
469
|
+
req.values_at('bar', 'foo', 'wun').must_equal ['ful', 'baz', 'der']
|
323
470
|
end
|
324
471
|
|
325
|
-
|
326
|
-
req =
|
472
|
+
it "extract referrer correctly" do
|
473
|
+
req = make_request \
|
327
474
|
Rack::MockRequest.env_for("/", "HTTP_REFERER" => "/some/path")
|
328
|
-
req.referer.
|
475
|
+
req.referer.must_equal "/some/path"
|
329
476
|
|
330
|
-
req =
|
477
|
+
req = make_request \
|
331
478
|
Rack::MockRequest.env_for("/")
|
332
|
-
req.referer.
|
479
|
+
req.referer.must_be_nil
|
333
480
|
end
|
334
481
|
|
335
|
-
|
336
|
-
req =
|
482
|
+
it "extract user agent correctly" do
|
483
|
+
req = make_request \
|
337
484
|
Rack::MockRequest.env_for("/", "HTTP_USER_AGENT" => "Mozilla/4.0 (compatible)")
|
338
|
-
req.user_agent.
|
485
|
+
req.user_agent.must_equal "Mozilla/4.0 (compatible)"
|
339
486
|
|
340
|
-
req =
|
487
|
+
req = make_request \
|
341
488
|
Rack::MockRequest.env_for("/")
|
342
|
-
req.user_agent.
|
489
|
+
req.user_agent.must_be_nil
|
343
490
|
end
|
344
491
|
|
345
|
-
|
346
|
-
req =
|
492
|
+
it "treat missing content type as nil" do
|
493
|
+
req = make_request \
|
347
494
|
Rack::MockRequest.env_for("/")
|
348
|
-
req.content_type.
|
495
|
+
req.content_type.must_be_nil
|
349
496
|
end
|
350
497
|
|
351
|
-
|
352
|
-
req =
|
498
|
+
it "treat empty content type as nil" do
|
499
|
+
req = make_request \
|
353
500
|
Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
|
354
|
-
req.content_type.
|
501
|
+
req.content_type.must_be_nil
|
355
502
|
end
|
356
503
|
|
357
|
-
|
358
|
-
req =
|
504
|
+
it "return nil media type for empty content type" do
|
505
|
+
req = make_request \
|
359
506
|
Rack::MockRequest.env_for("/", "CONTENT_TYPE" => "")
|
360
|
-
req.media_type.
|
507
|
+
req.media_type.must_be_nil
|
361
508
|
end
|
362
509
|
|
363
|
-
|
364
|
-
req =
|
510
|
+
it "cache, but invalidates the cache" do
|
511
|
+
req = make_request \
|
365
512
|
Rack::MockRequest.env_for("/?foo=quux",
|
366
513
|
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
|
367
514
|
:input => "foo=bar&quux=bla")
|
368
|
-
req.GET.
|
369
|
-
req.GET.
|
370
|
-
req.
|
371
|
-
req.GET.
|
372
|
-
req.GET.
|
515
|
+
req.GET.must_equal "foo" => "quux"
|
516
|
+
req.GET.must_equal "foo" => "quux"
|
517
|
+
req.set_header("QUERY_STRING", "bla=foo")
|
518
|
+
req.GET.must_equal "bla" => "foo"
|
519
|
+
req.GET.must_equal "bla" => "foo"
|
373
520
|
|
374
|
-
req.POST.
|
375
|
-
req.POST.
|
376
|
-
req.
|
377
|
-
req.POST.
|
378
|
-
req.POST.
|
521
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
522
|
+
req.POST.must_equal "foo" => "bar", "quux" => "bla"
|
523
|
+
req.set_header("rack.input", StringIO.new("foo=bla&quux=bar"))
|
524
|
+
req.POST.must_equal "foo" => "bla", "quux" => "bar"
|
525
|
+
req.POST.must_equal "foo" => "bla", "quux" => "bar"
|
379
526
|
end
|
380
527
|
|
381
|
-
|
382
|
-
req =
|
383
|
-
req.
|
528
|
+
it "figure out if called via XHR" do
|
529
|
+
req = make_request(Rack::MockRequest.env_for(""))
|
530
|
+
req.wont_be :xhr?
|
384
531
|
|
385
|
-
req =
|
532
|
+
req = make_request \
|
386
533
|
Rack::MockRequest.env_for("", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
|
387
|
-
req.
|
534
|
+
req.must_be :xhr?
|
388
535
|
end
|
389
536
|
|
390
|
-
|
391
|
-
request =
|
392
|
-
request.scheme.
|
393
|
-
request.
|
537
|
+
it "ssl detection" do
|
538
|
+
request = make_request(Rack::MockRequest.env_for("/"))
|
539
|
+
request.scheme.must_equal "http"
|
540
|
+
request.wont_be :ssl?
|
394
541
|
|
395
|
-
request =
|
396
|
-
request.scheme.
|
397
|
-
request.
|
542
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTPS' => 'on'))
|
543
|
+
request.scheme.must_equal "https"
|
544
|
+
request.must_be :ssl?
|
398
545
|
|
399
|
-
request =
|
400
|
-
request.scheme.
|
401
|
-
request.
|
546
|
+
request = make_request(Rack::MockRequest.env_for("/", 'rack.url_scheme' => 'https'))
|
547
|
+
request.scheme.must_equal "https"
|
548
|
+
request.must_be :ssl?
|
402
549
|
|
403
|
-
request =
|
404
|
-
request.scheme.
|
405
|
-
request.
|
550
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8080'))
|
551
|
+
request.scheme.must_equal "http"
|
552
|
+
request.wont_be :ssl?
|
406
553
|
|
407
|
-
request =
|
408
|
-
request.scheme.
|
409
|
-
request.
|
554
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTPS' => 'on'))
|
555
|
+
request.scheme.must_equal "https"
|
556
|
+
request.must_be :ssl?
|
410
557
|
|
411
|
-
request =
|
412
|
-
request.scheme.
|
413
|
-
request.
|
558
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_HOST' => 'www.example.org:8443', 'HTTP_X_FORWARDED_SSL' => 'on'))
|
559
|
+
request.scheme.must_equal "https"
|
560
|
+
request.must_be :ssl?
|
414
561
|
|
415
|
-
request =
|
416
|
-
request.scheme.
|
417
|
-
request.
|
562
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'https'))
|
563
|
+
request.scheme.must_equal "https"
|
564
|
+
request.must_be :ssl?
|
418
565
|
|
419
|
-
request =
|
420
|
-
request.scheme.
|
421
|
-
request.
|
566
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https'))
|
567
|
+
request.scheme.must_equal "https"
|
568
|
+
request.must_be :ssl?
|
422
569
|
|
423
|
-
request =
|
424
|
-
request.scheme.
|
425
|
-
request.
|
570
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_PROTO' => 'https, http, http'))
|
571
|
+
request.scheme.must_equal "https"
|
572
|
+
request.must_be :ssl?
|
426
573
|
end
|
427
574
|
|
428
|
-
|
429
|
-
request =
|
430
|
-
request.scheme.
|
575
|
+
it "prevents scheme abuse" do
|
576
|
+
request = make_request(Rack::MockRequest.env_for("/", 'HTTP_X_FORWARDED_SCHEME' => 'a."><script>alert(1)</script>'))
|
577
|
+
request.scheme.must_equal 'http'
|
431
578
|
end
|
432
579
|
|
433
|
-
|
434
|
-
req =
|
580
|
+
it "parse cookies" do
|
581
|
+
req = make_request \
|
435
582
|
Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
|
436
|
-
req.cookies.
|
437
|
-
req.cookies.
|
438
|
-
req.
|
439
|
-
req.cookies.
|
583
|
+
req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
|
584
|
+
req.cookies.must_equal "foo" => "bar", "quux" => "h&m"
|
585
|
+
req.delete_header("HTTP_COOKIE")
|
586
|
+
req.cookies.must_equal({})
|
440
587
|
end
|
441
588
|
|
442
|
-
|
443
|
-
req =
|
589
|
+
it "always return the same hash object" do
|
590
|
+
req = make_request \
|
444
591
|
Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=bar;quux=h&m")
|
445
592
|
hash = req.cookies
|
446
593
|
req.env.delete("HTTP_COOKIE")
|
447
|
-
req.cookies.
|
594
|
+
req.cookies.must_equal hash
|
448
595
|
req.env["HTTP_COOKIE"] = "zoo=m"
|
449
|
-
req.cookies.
|
596
|
+
req.cookies.must_equal hash
|
450
597
|
end
|
451
598
|
|
452
|
-
|
453
|
-
req =
|
454
|
-
req.cookies.
|
599
|
+
it "modify the cookies hash in place" do
|
600
|
+
req = make_request(Rack::MockRequest.env_for(""))
|
601
|
+
req.cookies.must_equal({})
|
455
602
|
req.cookies['foo'] = 'bar'
|
456
|
-
req.cookies.
|
603
|
+
req.cookies.must_equal 'foo' => 'bar'
|
457
604
|
end
|
458
605
|
|
459
|
-
|
606
|
+
it "not modify the params hash in place" do
|
460
607
|
e = Rack::MockRequest.env_for("")
|
461
|
-
req1 =
|
462
|
-
req1.
|
608
|
+
req1 = make_request(e)
|
609
|
+
if req1.delegate?
|
610
|
+
skip "delegate requests don't cache params, so mutations have no impact"
|
611
|
+
end
|
612
|
+
req1.params.must_equal({})
|
463
613
|
req1.params['foo'] = 'bar'
|
464
|
-
req1.params.
|
465
|
-
req2 =
|
466
|
-
req2.params.
|
614
|
+
req1.params.must_equal 'foo' => 'bar'
|
615
|
+
req2 = make_request(e)
|
616
|
+
req2.params.must_equal({})
|
467
617
|
end
|
468
618
|
|
469
|
-
|
619
|
+
it "modify params hash if param is in GET" do
|
470
620
|
e = Rack::MockRequest.env_for("?foo=duh")
|
471
|
-
req1 =
|
472
|
-
req1.params.
|
621
|
+
req1 = make_request(e)
|
622
|
+
req1.params.must_equal 'foo' => 'duh'
|
473
623
|
req1.update_param 'foo', 'bar'
|
474
|
-
req1.params.
|
475
|
-
req2 =
|
476
|
-
req2.params.
|
624
|
+
req1.params.must_equal 'foo' => 'bar'
|
625
|
+
req2 = make_request(e)
|
626
|
+
req2.params.must_equal 'foo' => 'bar'
|
477
627
|
end
|
478
628
|
|
479
|
-
|
629
|
+
it "modify params hash if param is in POST" do
|
480
630
|
e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=duh')
|
481
|
-
req1 =
|
482
|
-
req1.params.
|
631
|
+
req1 = make_request(e)
|
632
|
+
req1.params.must_equal 'foo' => 'duh'
|
483
633
|
req1.update_param 'foo', 'bar'
|
484
|
-
req1.params.
|
485
|
-
req2 =
|
486
|
-
req2.params.
|
634
|
+
req1.params.must_equal 'foo' => 'bar'
|
635
|
+
req2 = make_request(e)
|
636
|
+
req2.params.must_equal 'foo' => 'bar'
|
487
637
|
end
|
488
638
|
|
489
|
-
|
639
|
+
it "modify params hash, even if param didn't exist before" do
|
490
640
|
e = Rack::MockRequest.env_for("")
|
491
|
-
req1 =
|
492
|
-
req1.params.
|
641
|
+
req1 = make_request(e)
|
642
|
+
req1.params.must_equal({})
|
493
643
|
req1.update_param 'foo', 'bar'
|
494
|
-
req1.params.
|
495
|
-
req2 =
|
496
|
-
req2.params.
|
644
|
+
req1.params.must_equal 'foo' => 'bar'
|
645
|
+
req2 = make_request(e)
|
646
|
+
req2.params.must_equal 'foo' => 'bar'
|
497
647
|
end
|
498
648
|
|
499
|
-
|
649
|
+
it "modify params hash by changing only GET" do
|
500
650
|
e = Rack::MockRequest.env_for("?foo=duhget")
|
501
|
-
req =
|
502
|
-
req.GET.
|
503
|
-
req.POST.
|
651
|
+
req = make_request(e)
|
652
|
+
req.GET.must_equal 'foo' => 'duhget'
|
653
|
+
req.POST.must_equal({})
|
504
654
|
req.update_param 'foo', 'bar'
|
505
|
-
req.GET.
|
506
|
-
req.POST.
|
655
|
+
req.GET.must_equal 'foo' => 'bar'
|
656
|
+
req.POST.must_equal({})
|
507
657
|
end
|
508
658
|
|
509
|
-
|
659
|
+
it "modify params hash by changing only POST" do
|
510
660
|
e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
|
511
|
-
req =
|
512
|
-
req.GET.
|
513
|
-
req.POST.
|
661
|
+
req = make_request(e)
|
662
|
+
req.GET.must_equal({})
|
663
|
+
req.POST.must_equal 'foo' => 'duhpost'
|
514
664
|
req.update_param 'foo', 'bar'
|
515
|
-
req.GET.
|
516
|
-
req.POST.
|
665
|
+
req.GET.must_equal({})
|
666
|
+
req.POST.must_equal 'foo' => 'bar'
|
517
667
|
end
|
518
668
|
|
519
|
-
|
669
|
+
it "modify params hash, even if param is defined in both POST and GET" do
|
520
670
|
e = Rack::MockRequest.env_for("?foo=duhget", "REQUEST_METHOD" => 'POST', :input => "foo=duhpost")
|
521
|
-
req1 =
|
522
|
-
req1.GET.
|
523
|
-
req1.POST.
|
524
|
-
req1.params.
|
671
|
+
req1 = make_request(e)
|
672
|
+
req1.GET.must_equal 'foo' => 'duhget'
|
673
|
+
req1.POST.must_equal 'foo' => 'duhpost'
|
674
|
+
req1.params.must_equal 'foo' => 'duhpost'
|
525
675
|
req1.update_param 'foo', 'bar'
|
526
|
-
req1.GET.
|
527
|
-
req1.POST.
|
528
|
-
req1.params.
|
529
|
-
req2 =
|
530
|
-
req2.GET.
|
531
|
-
req2.POST.
|
532
|
-
req2.params.
|
533
|
-
req2.params.
|
676
|
+
req1.GET.must_equal 'foo' => 'bar'
|
677
|
+
req1.POST.must_equal 'foo' => 'bar'
|
678
|
+
req1.params.must_equal 'foo' => 'bar'
|
679
|
+
req2 = make_request(e)
|
680
|
+
req2.GET.must_equal 'foo' => 'bar'
|
681
|
+
req2.POST.must_equal 'foo' => 'bar'
|
682
|
+
req2.params.must_equal 'foo' => 'bar'
|
683
|
+
req2.params.must_equal 'foo' => 'bar'
|
534
684
|
end
|
535
685
|
|
536
|
-
|
686
|
+
it "allow deleting from params hash if param is in GET" do
|
537
687
|
e = Rack::MockRequest.env_for("?foo=bar")
|
538
|
-
req1 =
|
539
|
-
req1.params.
|
540
|
-
req1.delete_param('foo').
|
541
|
-
req1.params.
|
542
|
-
req2 =
|
543
|
-
req2.params.
|
688
|
+
req1 = make_request(e)
|
689
|
+
req1.params.must_equal 'foo' => 'bar'
|
690
|
+
req1.delete_param('foo').must_equal 'bar'
|
691
|
+
req1.params.must_equal({})
|
692
|
+
req2 = make_request(e)
|
693
|
+
req2.params.must_equal({})
|
544
694
|
end
|
545
695
|
|
546
|
-
|
696
|
+
it "allow deleting from params hash if param is in POST" do
|
547
697
|
e = Rack::MockRequest.env_for("", "REQUEST_METHOD" => 'POST', :input => 'foo=bar')
|
548
|
-
req1 =
|
549
|
-
req1.params.
|
550
|
-
req1.delete_param('foo').
|
551
|
-
req1.params.
|
552
|
-
req2 =
|
553
|
-
req2.params.
|
698
|
+
req1 = make_request(e)
|
699
|
+
req1.params.must_equal 'foo' => 'bar'
|
700
|
+
req1.delete_param('foo').must_equal 'bar'
|
701
|
+
req1.params.must_equal({})
|
702
|
+
req2 = make_request(e)
|
703
|
+
req2.params.must_equal({})
|
554
704
|
end
|
555
705
|
|
556
|
-
|
557
|
-
req =
|
558
|
-
req.cookies["foo"].
|
706
|
+
it "pass through non-uri escaped cookies as-is" do
|
707
|
+
req = make_request Rack::MockRequest.env_for("", "HTTP_COOKIE" => "foo=%")
|
708
|
+
req.cookies["foo"].must_equal "%"
|
559
709
|
end
|
560
710
|
|
561
|
-
|
562
|
-
req =
|
711
|
+
it "parse cookies according to RFC 2109" do
|
712
|
+
req = make_request \
|
563
713
|
Rack::MockRequest.env_for('', 'HTTP_COOKIE' => 'foo=bar;foo=car')
|
564
|
-
req.cookies.
|
714
|
+
req.cookies.must_equal 'foo' => 'bar'
|
565
715
|
end
|
566
716
|
|
567
|
-
|
568
|
-
req =
|
717
|
+
it 'parse cookies with quotes' do
|
718
|
+
req = make_request Rack::MockRequest.env_for('', {
|
569
719
|
'HTTP_COOKIE' => '$Version="1"; Customer="WILE_E_COYOTE"; $Path="/acme"; Part_Number="Rocket_Launcher_0001"; $Path="/acme"'
|
570
720
|
})
|
571
|
-
req.cookies.
|
721
|
+
req.cookies.must_equal({
|
572
722
|
'$Version' => '"1"',
|
573
723
|
'Customer' => '"WILE_E_COYOTE"',
|
574
724
|
'$Path' => '"/acme"',
|
@@ -576,88 +726,88 @@ describe Rack::Request do
|
|
576
726
|
})
|
577
727
|
end
|
578
728
|
|
579
|
-
|
580
|
-
req =
|
581
|
-
req.script_name.
|
729
|
+
it "provide setters" do
|
730
|
+
req = make_request(e=Rack::MockRequest.env_for(""))
|
731
|
+
req.script_name.must_equal ""
|
582
732
|
req.script_name = "/foo"
|
583
|
-
req.script_name.
|
584
|
-
e["SCRIPT_NAME"].
|
733
|
+
req.script_name.must_equal "/foo"
|
734
|
+
e["SCRIPT_NAME"].must_equal "/foo"
|
585
735
|
|
586
|
-
req.path_info.
|
736
|
+
req.path_info.must_equal "/"
|
587
737
|
req.path_info = "/foo"
|
588
|
-
req.path_info.
|
589
|
-
e["PATH_INFO"].
|
590
|
-
end
|
591
|
-
|
592
|
-
|
593
|
-
req =
|
594
|
-
req.env.
|
595
|
-
end
|
596
|
-
|
597
|
-
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
end
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
end
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
636
|
-
|
637
|
-
|
638
|
-
|
639
|
-
|
640
|
-
|
641
|
-
end
|
642
|
-
|
643
|
-
|
644
|
-
req =
|
738
|
+
req.path_info.must_equal "/foo"
|
739
|
+
e["PATH_INFO"].must_equal "/foo"
|
740
|
+
end
|
741
|
+
|
742
|
+
it "provide the original env" do
|
743
|
+
req = make_request(e = Rack::MockRequest.env_for(""))
|
744
|
+
req.env.must_equal e
|
745
|
+
end
|
746
|
+
|
747
|
+
it "restore the base URL" do
|
748
|
+
make_request(Rack::MockRequest.env_for("")).base_url.
|
749
|
+
must_equal "http://example.org"
|
750
|
+
make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).base_url.
|
751
|
+
must_equal "http://example.org"
|
752
|
+
end
|
753
|
+
|
754
|
+
it "restore the URL" do
|
755
|
+
make_request(Rack::MockRequest.env_for("")).url.
|
756
|
+
must_equal "http://example.org/"
|
757
|
+
make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).url.
|
758
|
+
must_equal "http://example.org/foo/"
|
759
|
+
make_request(Rack::MockRequest.env_for("/foo")).url.
|
760
|
+
must_equal "http://example.org/foo"
|
761
|
+
make_request(Rack::MockRequest.env_for("?foo")).url.
|
762
|
+
must_equal "http://example.org/?foo"
|
763
|
+
make_request(Rack::MockRequest.env_for("http://example.org:8080/")).url.
|
764
|
+
must_equal "http://example.org:8080/"
|
765
|
+
make_request(Rack::MockRequest.env_for("https://example.org/")).url.
|
766
|
+
must_equal "https://example.org/"
|
767
|
+
make_request(Rack::MockRequest.env_for("coffee://example.org/")).url.
|
768
|
+
must_equal "coffee://example.org/"
|
769
|
+
make_request(Rack::MockRequest.env_for("coffee://example.org:443/")).url.
|
770
|
+
must_equal "coffee://example.org:443/"
|
771
|
+
make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).url.
|
772
|
+
must_equal "https://example.com:8080/foo?foo"
|
773
|
+
end
|
774
|
+
|
775
|
+
it "restore the full path" do
|
776
|
+
make_request(Rack::MockRequest.env_for("")).fullpath.
|
777
|
+
must_equal "/"
|
778
|
+
make_request(Rack::MockRequest.env_for("", "SCRIPT_NAME" => "/foo")).fullpath.
|
779
|
+
must_equal "/foo/"
|
780
|
+
make_request(Rack::MockRequest.env_for("/foo")).fullpath.
|
781
|
+
must_equal "/foo"
|
782
|
+
make_request(Rack::MockRequest.env_for("?foo")).fullpath.
|
783
|
+
must_equal "/?foo"
|
784
|
+
make_request(Rack::MockRequest.env_for("http://example.org:8080/")).fullpath.
|
785
|
+
must_equal "/"
|
786
|
+
make_request(Rack::MockRequest.env_for("https://example.org/")).fullpath.
|
787
|
+
must_equal "/"
|
788
|
+
|
789
|
+
make_request(Rack::MockRequest.env_for("https://example.com:8080/foo?foo")).fullpath.
|
790
|
+
must_equal "/foo?foo"
|
791
|
+
end
|
792
|
+
|
793
|
+
it "handle multiple media type parameters" do
|
794
|
+
req = make_request \
|
645
795
|
Rack::MockRequest.env_for("/",
|
646
796
|
"CONTENT_TYPE" => 'text/plain; foo=BAR,baz=bizzle dizzle;BLING=bam;blong="boo";zump="zoo\"o";weird=lol"')
|
647
|
-
req.
|
648
|
-
req.media_type_params.
|
649
|
-
req.media_type_params['foo'].
|
650
|
-
req.media_type_params.
|
651
|
-
req.media_type_params['baz'].
|
652
|
-
req.media_type_params.
|
653
|
-
req.media_type_params.
|
654
|
-
req.media_type_params['bling'].
|
655
|
-
req.media_type_params['blong'].
|
656
|
-
req.media_type_params['zump'].
|
657
|
-
req.media_type_params['weird'].
|
658
|
-
end
|
659
|
-
|
660
|
-
|
797
|
+
req.wont_be :form_data?
|
798
|
+
req.media_type_params.must_include 'foo'
|
799
|
+
req.media_type_params['foo'].must_equal 'BAR'
|
800
|
+
req.media_type_params.must_include 'baz'
|
801
|
+
req.media_type_params['baz'].must_equal 'bizzle dizzle'
|
802
|
+
req.media_type_params.wont_include 'BLING'
|
803
|
+
req.media_type_params.must_include 'bling'
|
804
|
+
req.media_type_params['bling'].must_equal 'bam'
|
805
|
+
req.media_type_params['blong'].must_equal 'boo'
|
806
|
+
req.media_type_params['zump'].must_equal 'zoo\"o'
|
807
|
+
req.media_type_params['weird'].must_equal 'lol"'
|
808
|
+
end
|
809
|
+
|
810
|
+
it "parse with junk before boundary" do
|
661
811
|
# Adapted from RFC 1867.
|
662
812
|
input = <<EOF
|
663
813
|
blah blah\r
|
@@ -674,31 +824,31 @@ Content-Transfer-Encoding: base64\r
|
|
674
824
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
|
675
825
|
--AaB03x--\r
|
676
826
|
EOF
|
677
|
-
req =
|
827
|
+
req = make_request Rack::MockRequest.env_for("/",
|
678
828
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
679
829
|
"CONTENT_LENGTH" => input.size,
|
680
830
|
:input => input)
|
681
831
|
|
682
|
-
req.POST.
|
683
|
-
req.POST.
|
832
|
+
req.POST.must_include "fileupload"
|
833
|
+
req.POST.must_include "reply"
|
684
834
|
|
685
|
-
req.
|
686
|
-
req.content_length.
|
687
|
-
req.media_type.
|
688
|
-
req.media_type_params.
|
689
|
-
req.media_type_params['boundary'].
|
835
|
+
req.must_be :form_data?
|
836
|
+
req.content_length.must_equal input.size
|
837
|
+
req.media_type.must_equal 'multipart/form-data'
|
838
|
+
req.media_type_params.must_include 'boundary'
|
839
|
+
req.media_type_params['boundary'].must_equal 'AaB03x'
|
690
840
|
|
691
|
-
req.POST["reply"].
|
841
|
+
req.POST["reply"].must_equal "yes"
|
692
842
|
|
693
843
|
f = req.POST["fileupload"]
|
694
|
-
f.
|
695
|
-
f[:type].
|
696
|
-
f[:filename].
|
697
|
-
f.
|
698
|
-
f[:tempfile].size.
|
844
|
+
f.must_be_kind_of Hash
|
845
|
+
f[:type].must_equal "image/jpeg"
|
846
|
+
f[:filename].must_equal "dj.jpg"
|
847
|
+
f.must_include :tempfile
|
848
|
+
f[:tempfile].size.must_equal 76
|
699
849
|
end
|
700
850
|
|
701
|
-
|
851
|
+
it "not infinite loop with a malformed HTTP request" do
|
702
852
|
# Adapted from RFC 1867.
|
703
853
|
input = <<EOF
|
704
854
|
--AaB03x
|
@@ -713,16 +863,16 @@ Content-Transfer-Encoding: base64
|
|
713
863
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
|
714
864
|
--AaB03x--
|
715
865
|
EOF
|
716
|
-
req =
|
866
|
+
req = make_request Rack::MockRequest.env_for("/",
|
717
867
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
718
868
|
"CONTENT_LENGTH" => input.size,
|
719
869
|
:input => input)
|
720
870
|
|
721
|
-
lambda{req.POST}.
|
871
|
+
lambda{req.POST}.must_raise EOFError
|
722
872
|
end
|
723
873
|
|
724
874
|
|
725
|
-
|
875
|
+
it "parse multipart form data" do
|
726
876
|
# Adapted from RFC 1867.
|
727
877
|
input = <<EOF
|
728
878
|
--AaB03x\r
|
@@ -737,47 +887,73 @@ Content-Transfer-Encoding: base64\r
|
|
737
887
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
|
738
888
|
--AaB03x--\r
|
739
889
|
EOF
|
740
|
-
req =
|
890
|
+
req = make_request Rack::MockRequest.env_for("/",
|
741
891
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
742
892
|
"CONTENT_LENGTH" => input.size,
|
743
893
|
:input => input)
|
744
894
|
|
745
|
-
req.POST.
|
746
|
-
req.POST.
|
895
|
+
req.POST.must_include "fileupload"
|
896
|
+
req.POST.must_include "reply"
|
747
897
|
|
748
|
-
req.
|
749
|
-
req.content_length.
|
750
|
-
req.media_type.
|
751
|
-
req.media_type_params.
|
752
|
-
req.media_type_params['boundary'].
|
898
|
+
req.must_be :form_data?
|
899
|
+
req.content_length.must_equal input.size
|
900
|
+
req.media_type.must_equal 'multipart/form-data'
|
901
|
+
req.media_type_params.must_include 'boundary'
|
902
|
+
req.media_type_params['boundary'].must_equal 'AaB03x'
|
753
903
|
|
754
|
-
req.POST["reply"].
|
904
|
+
req.POST["reply"].must_equal "yes"
|
755
905
|
|
756
906
|
f = req.POST["fileupload"]
|
757
|
-
f.
|
758
|
-
f[:type].
|
759
|
-
f[:filename].
|
760
|
-
f.
|
761
|
-
f[:tempfile].size.
|
907
|
+
f.must_be_kind_of Hash
|
908
|
+
f[:type].must_equal "image/jpeg"
|
909
|
+
f[:filename].must_equal "dj.jpg"
|
910
|
+
f.must_include :tempfile
|
911
|
+
f[:tempfile].size.must_equal 76
|
912
|
+
end
|
913
|
+
|
914
|
+
it "MultipartPartLimitError when request has too many multipart parts if limit set" do
|
915
|
+
begin
|
916
|
+
data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
|
917
|
+
data += "--AaB03x--\r"
|
918
|
+
|
919
|
+
options = {
|
920
|
+
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
921
|
+
"CONTENT_LENGTH" => data.length.to_s,
|
922
|
+
:input => StringIO.new(data)
|
923
|
+
}
|
924
|
+
|
925
|
+
request = make_request Rack::MockRequest.env_for("/", options)
|
926
|
+
lambda { request.POST }.must_raise Rack::Multipart::MultipartPartLimitError
|
927
|
+
end
|
762
928
|
end
|
763
929
|
|
764
|
-
|
930
|
+
it 'closes tempfiles it created in the case of too many created' do
|
765
931
|
begin
|
766
932
|
data = 10000.times.map { "--AaB03x\r\nContent-Type: text/plain\r\nContent-Disposition: attachment; name=#{SecureRandom.hex(10)}; filename=#{SecureRandom.hex(10)}\r\n\r\ncontents\r\n" }.join("\r\n")
|
767
933
|
data += "--AaB03x--\r"
|
768
934
|
|
935
|
+
files = []
|
769
936
|
options = {
|
770
937
|
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
771
938
|
"CONTENT_LENGTH" => data.length.to_s,
|
939
|
+
Rack::RACK_MULTIPART_TEMPFILE_FACTORY => lambda { |filename, content_type|
|
940
|
+
file = Tempfile.new(["RackMultipart", ::File.extname(filename)])
|
941
|
+
files << file
|
942
|
+
file
|
943
|
+
},
|
772
944
|
:input => StringIO.new(data)
|
773
945
|
}
|
774
946
|
|
775
|
-
request =
|
776
|
-
|
947
|
+
request = make_request Rack::MockRequest.env_for("/", options)
|
948
|
+
assert_raises(Rack::Multipart::MultipartPartLimitError) do
|
949
|
+
request.POST
|
950
|
+
end
|
951
|
+
refute_predicate files, :empty?
|
952
|
+
files.each { |f| assert_predicate f, :closed? }
|
777
953
|
end
|
778
954
|
end
|
779
955
|
|
780
|
-
|
956
|
+
it "parse big multipart form data" do
|
781
957
|
input = <<EOF
|
782
958
|
--AaB03x\r
|
783
959
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
@@ -789,17 +965,17 @@ content-disposition: form-data; name="mean"; filename="mean"\r
|
|
789
965
|
--AaB03xha\r
|
790
966
|
--AaB03x--\r
|
791
967
|
EOF
|
792
|
-
req =
|
968
|
+
req = make_request Rack::MockRequest.env_for("/",
|
793
969
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
794
970
|
"CONTENT_LENGTH" => input.size,
|
795
971
|
:input => input)
|
796
972
|
|
797
|
-
req.POST["huge"][:tempfile].size.
|
798
|
-
req.POST["mean"][:tempfile].size.
|
799
|
-
req.POST["mean"][:tempfile].read.
|
973
|
+
req.POST["huge"][:tempfile].size.must_equal 32768
|
974
|
+
req.POST["mean"][:tempfile].size.must_equal 10
|
975
|
+
req.POST["mean"][:tempfile].read.must_equal "--AaB03xha"
|
800
976
|
end
|
801
977
|
|
802
|
-
|
978
|
+
it "record tempfiles from multipart form data in env[rack.tempfiles]" do
|
803
979
|
input = <<EOF
|
804
980
|
--AaB03x\r
|
805
981
|
content-disposition: form-data; name="fileupload"; filename="foo.jpg"\r
|
@@ -819,22 +995,22 @@ EOF
|
|
819
995
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
820
996
|
"CONTENT_LENGTH" => input.size,
|
821
997
|
:input => input)
|
822
|
-
req =
|
998
|
+
req = make_request(env)
|
823
999
|
req.params
|
824
|
-
env['rack.tempfiles'].size.
|
1000
|
+
env['rack.tempfiles'].size.must_equal 2
|
825
1001
|
end
|
826
1002
|
|
827
|
-
|
1003
|
+
it "detect invalid multipart form data" do
|
828
1004
|
input = <<EOF
|
829
1005
|
--AaB03x\r
|
830
1006
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
831
1007
|
EOF
|
832
|
-
req =
|
1008
|
+
req = make_request Rack::MockRequest.env_for("/",
|
833
1009
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
834
1010
|
"CONTENT_LENGTH" => input.size,
|
835
1011
|
:input => input)
|
836
1012
|
|
837
|
-
lambda { req.POST }.
|
1013
|
+
lambda { req.POST }.must_raise EOFError
|
838
1014
|
|
839
1015
|
input = <<EOF
|
840
1016
|
--AaB03x\r
|
@@ -842,12 +1018,12 @@ content-disposition: form-data; name="huge"; filename="huge"\r
|
|
842
1018
|
\r
|
843
1019
|
foo\r
|
844
1020
|
EOF
|
845
|
-
req =
|
1021
|
+
req = make_request Rack::MockRequest.env_for("/",
|
846
1022
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
847
1023
|
"CONTENT_LENGTH" => input.size,
|
848
1024
|
:input => input)
|
849
1025
|
|
850
|
-
lambda { req.POST }.
|
1026
|
+
lambda { req.POST }.must_raise EOFError
|
851
1027
|
|
852
1028
|
input = <<EOF
|
853
1029
|
--AaB03x\r
|
@@ -855,29 +1031,29 @@ content-disposition: form-data; name="huge"; filename="huge"\r
|
|
855
1031
|
\r
|
856
1032
|
foo\r
|
857
1033
|
EOF
|
858
|
-
req =
|
1034
|
+
req = make_request Rack::MockRequest.env_for("/",
|
859
1035
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
860
1036
|
"CONTENT_LENGTH" => input.size,
|
861
1037
|
:input => input)
|
862
1038
|
|
863
|
-
lambda { req.POST }.
|
1039
|
+
lambda { req.POST }.must_raise EOFError
|
864
1040
|
end
|
865
1041
|
|
866
|
-
|
1042
|
+
it "consistently raise EOFError on bad multipart form data" do
|
867
1043
|
input = <<EOF
|
868
1044
|
--AaB03x\r
|
869
1045
|
content-disposition: form-data; name="huge"; filename="huge"\r
|
870
1046
|
EOF
|
871
|
-
req =
|
1047
|
+
req = make_request Rack::MockRequest.env_for("/",
|
872
1048
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
873
1049
|
"CONTENT_LENGTH" => input.size,
|
874
1050
|
:input => input)
|
875
1051
|
|
876
|
-
lambda { req.POST }.
|
877
|
-
lambda { req.POST }.
|
1052
|
+
lambda { req.POST }.must_raise EOFError
|
1053
|
+
lambda { req.POST }.must_raise EOFError
|
878
1054
|
end
|
879
1055
|
|
880
|
-
|
1056
|
+
it "correctly parse the part name from Content-Id header" do
|
881
1057
|
input = <<EOF
|
882
1058
|
--AaB03x\r
|
883
1059
|
Content-Type: text/xml; charset=utf-8\r
|
@@ -887,20 +1063,15 @@ Content-Transfer-Encoding: 7bit\r
|
|
887
1063
|
foo\r
|
888
1064
|
--AaB03x--\r
|
889
1065
|
EOF
|
890
|
-
req =
|
1066
|
+
req = make_request Rack::MockRequest.env_for("/",
|
891
1067
|
"CONTENT_TYPE" => "multipart/related, boundary=AaB03x",
|
892
1068
|
"CONTENT_LENGTH" => input.size,
|
893
1069
|
:input => input)
|
894
1070
|
|
895
|
-
req.params.keys.
|
1071
|
+
req.params.keys.must_equal ["<soap-start>"]
|
896
1072
|
end
|
897
1073
|
|
898
|
-
|
899
|
-
if /regexp/.respond_to?(:kcode) # < 1.9
|
900
|
-
begin
|
901
|
-
original_kcode = $KCODE
|
902
|
-
$KCODE='UTF8'
|
903
|
-
|
1074
|
+
it "not try to interpret binary as utf8" do
|
904
1075
|
input = <<EOF
|
905
1076
|
--AaB03x\r
|
906
1077
|
content-disposition: form-data; name="fileupload"; filename="junk.a"\r
|
@@ -910,77 +1081,33 @@ content-type: application/octet-stream\r
|
|
910
1081
|
--AaB03x--\r
|
911
1082
|
EOF
|
912
1083
|
|
913
|
-
req =
|
1084
|
+
req = make_request Rack::MockRequest.env_for("/",
|
914
1085
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
915
1086
|
"CONTENT_LENGTH" => input.size,
|
916
1087
|
:input => input)
|
917
1088
|
|
918
|
-
|
919
|
-
req.POST["fileupload"][:tempfile].size.should.equal 4
|
920
|
-
ensure
|
921
|
-
$KCODE = original_kcode
|
922
|
-
end
|
923
|
-
else # >= 1.9
|
924
|
-
input = <<EOF
|
925
|
-
--AaB03x\r
|
926
|
-
content-disposition: form-data; name="fileupload"; filename="junk.a"\r
|
927
|
-
content-type: application/octet-stream\r
|
928
|
-
\r
|
929
|
-
#{[0x36,0xCF,0x0A,0xF8].pack('c*')}\r
|
930
|
-
--AaB03x--\r
|
931
|
-
EOF
|
932
|
-
|
933
|
-
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
934
|
-
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
935
|
-
"CONTENT_LENGTH" => input.size,
|
936
|
-
:input => input)
|
937
|
-
|
938
|
-
lambda{req.POST}.should.not.raise(EOFError)
|
939
|
-
req.POST["fileupload"][:tempfile].size.should.equal 4
|
940
|
-
end
|
1089
|
+
req.POST["fileupload"][:tempfile].size.must_equal 4
|
941
1090
|
end
|
942
1091
|
|
943
|
-
|
944
|
-
input = <<EOF
|
945
|
-
--AaB03x\r
|
946
|
-
content-disposition: form-data; name="huge"; filename="huge"\r
|
947
|
-
\r
|
948
|
-
foo\r
|
949
|
-
--AaB03x--
|
950
|
-
EOF
|
951
|
-
|
952
|
-
rack_input = Tempfile.new("rackspec")
|
953
|
-
rack_input.write(input)
|
954
|
-
rack_input.rewind
|
955
|
-
|
956
|
-
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
957
|
-
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
958
|
-
"CONTENT_LENGTH" => input.size,
|
959
|
-
:input => rack_input)
|
960
|
-
|
961
|
-
lambda{ req.POST }.should.not.raise
|
962
|
-
lambda{ req.POST }.should.not.raise("input re-processed!")
|
963
|
-
end
|
964
|
-
|
965
|
-
should "use form_hash when form_input is a Tempfile" do
|
1092
|
+
it "use form_hash when form_input is a Tempfile" do
|
966
1093
|
input = "{foo: 'bar'}"
|
967
1094
|
|
968
1095
|
rack_input = Tempfile.new("rackspec")
|
969
1096
|
rack_input.write(input)
|
970
1097
|
rack_input.rewind
|
971
1098
|
|
972
|
-
req =
|
1099
|
+
req = make_request Rack::MockRequest.env_for("/",
|
973
1100
|
"rack.request.form_hash" => {'foo' => 'bar'},
|
974
1101
|
"rack.request.form_input" => rack_input,
|
975
1102
|
:input => rack_input)
|
976
1103
|
|
977
|
-
req.POST.
|
1104
|
+
req.POST.must_equal req.env['rack.request.form_hash']
|
978
1105
|
end
|
979
1106
|
|
980
|
-
|
1107
|
+
it "conform to the Rack spec" do
|
981
1108
|
app = lambda { |env|
|
982
|
-
content =
|
983
|
-
size = content.
|
1109
|
+
content = make_request(env).POST["file"].inspect
|
1110
|
+
size = content.bytesize
|
984
1111
|
[200, {"Content-Type" => "text/html", "Content-Length" => size.to_s}, [content]]
|
985
1112
|
}
|
986
1113
|
|
@@ -997,149 +1124,151 @@ Content-Transfer-Encoding: base64\r
|
|
997
1124
|
/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r
|
998
1125
|
--AaB03x--\r
|
999
1126
|
EOF
|
1000
|
-
input.force_encoding(
|
1127
|
+
input.force_encoding(Encoding::ASCII_8BIT)
|
1001
1128
|
res = Rack::MockRequest.new(Rack::Lint.new(app)).get "/",
|
1002
1129
|
"CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
|
1003
1130
|
"CONTENT_LENGTH" => input.size.to_s, "rack.input" => StringIO.new(input)
|
1004
1131
|
|
1005
|
-
res.
|
1132
|
+
res.must_be :ok?
|
1006
1133
|
end
|
1007
1134
|
|
1008
|
-
|
1135
|
+
it "parse Accept-Encoding correctly" do
|
1009
1136
|
parser = lambda do |x|
|
1010
|
-
|
1137
|
+
make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => x)).accept_encoding
|
1011
1138
|
end
|
1012
1139
|
|
1013
|
-
parser.call(nil).
|
1140
|
+
parser.call(nil).must_equal []
|
1014
1141
|
|
1015
|
-
parser.call("compress, gzip").
|
1016
|
-
parser.call("").
|
1017
|
-
parser.call("*").
|
1018
|
-
parser.call("compress;q=0.5, gzip;q=1.0").
|
1019
|
-
parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").
|
1142
|
+
parser.call("compress, gzip").must_equal [["compress", 1.0], ["gzip", 1.0]]
|
1143
|
+
parser.call("").must_equal []
|
1144
|
+
parser.call("*").must_equal [["*", 1.0]]
|
1145
|
+
parser.call("compress;q=0.5, gzip;q=1.0").must_equal [["compress", 0.5], ["gzip", 1.0]]
|
1146
|
+
parser.call("gzip;q=1.0, identity; q=0.5, *;q=0").must_equal [["gzip", 1.0], ["identity", 0.5], ["*", 0] ]
|
1020
1147
|
|
1021
|
-
parser.call("gzip ; q=0.9").
|
1022
|
-
parser.call("gzip ; deflate").
|
1148
|
+
parser.call("gzip ; q=0.9").must_equal [["gzip", 0.9]]
|
1149
|
+
parser.call("gzip ; deflate").must_equal [["gzip", 1.0]]
|
1023
1150
|
end
|
1024
1151
|
|
1025
|
-
|
1152
|
+
it "parse Accept-Language correctly" do
|
1026
1153
|
parser = lambda do |x|
|
1027
|
-
|
1154
|
+
make_request(Rack::MockRequest.env_for("", "HTTP_ACCEPT_LANGUAGE" => x)).accept_language
|
1028
1155
|
end
|
1029
1156
|
|
1030
|
-
parser.call(nil).
|
1157
|
+
parser.call(nil).must_equal []
|
1031
1158
|
|
1032
|
-
parser.call("fr, en").
|
1033
|
-
parser.call("").
|
1034
|
-
parser.call("*").
|
1035
|
-
parser.call("fr;q=0.5, en;q=1.0").
|
1036
|
-
parser.call("fr;q=1.0, en; q=0.5, *;q=0").
|
1159
|
+
parser.call("fr, en").must_equal [["fr", 1.0], ["en", 1.0]]
|
1160
|
+
parser.call("").must_equal []
|
1161
|
+
parser.call("*").must_equal [["*", 1.0]]
|
1162
|
+
parser.call("fr;q=0.5, en;q=1.0").must_equal [["fr", 0.5], ["en", 1.0]]
|
1163
|
+
parser.call("fr;q=1.0, en; q=0.5, *;q=0").must_equal [["fr", 1.0], ["en", 0.5], ["*", 0] ]
|
1037
1164
|
|
1038
|
-
parser.call("fr ; q=0.9").
|
1039
|
-
parser.call("fr").
|
1165
|
+
parser.call("fr ; q=0.9").must_equal [["fr", 0.9]]
|
1166
|
+
parser.call("fr").must_equal [["fr", 1.0]]
|
1040
1167
|
end
|
1041
1168
|
|
1042
|
-
ip_app
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1169
|
+
def ip_app
|
1170
|
+
lambda { |env|
|
1171
|
+
request = make_request(env)
|
1172
|
+
response = Rack::Response.new
|
1173
|
+
response.write request.ip
|
1174
|
+
response.finish
|
1175
|
+
}
|
1176
|
+
end
|
1048
1177
|
|
1049
|
-
|
1178
|
+
it 'provide ip information' do
|
1050
1179
|
mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
|
1051
1180
|
|
1052
1181
|
res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4'
|
1053
|
-
res.body.
|
1182
|
+
res.body.must_equal '1.2.3.4'
|
1054
1183
|
|
1055
1184
|
res = mock.get '/', 'REMOTE_ADDR' => 'fe80::202:b3ff:fe1e:8329'
|
1056
|
-
res.body.
|
1185
|
+
res.body.must_equal 'fe80::202:b3ff:fe1e:8329'
|
1057
1186
|
|
1058
1187
|
res = mock.get '/', 'REMOTE_ADDR' => '1.2.3.4,3.4.5.6'
|
1059
|
-
res.body.
|
1188
|
+
res.body.must_equal '1.2.3.4'
|
1060
1189
|
end
|
1061
1190
|
|
1062
|
-
|
1191
|
+
it 'deals with proxies' do
|
1063
1192
|
mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
|
1064
1193
|
|
1065
1194
|
res = mock.get '/',
|
1066
1195
|
'REMOTE_ADDR' => '1.2.3.4',
|
1067
1196
|
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
1068
|
-
res.body.
|
1197
|
+
res.body.must_equal '1.2.3.4'
|
1069
1198
|
|
1070
1199
|
res = mock.get '/',
|
1071
1200
|
'REMOTE_ADDR' => '1.2.3.4',
|
1072
1201
|
'HTTP_X_FORWARDED_FOR' => 'unknown'
|
1073
|
-
res.body.
|
1202
|
+
res.body.must_equal '1.2.3.4'
|
1074
1203
|
|
1075
1204
|
res = mock.get '/',
|
1076
1205
|
'REMOTE_ADDR' => '127.0.0.1',
|
1077
1206
|
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
1078
|
-
res.body.
|
1207
|
+
res.body.must_equal '3.4.5.6'
|
1079
1208
|
|
1080
1209
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,3.4.5.6'
|
1081
|
-
res.body.
|
1210
|
+
res.body.must_equal '3.4.5.6'
|
1082
1211
|
|
1083
1212
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '192.168.0.1,3.4.5.6'
|
1084
|
-
res.body.
|
1213
|
+
res.body.must_equal '3.4.5.6'
|
1085
1214
|
|
1086
1215
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1,3.4.5.6'
|
1087
|
-
res.body.
|
1216
|
+
res.body.must_equal '3.4.5.6'
|
1088
1217
|
|
1089
1218
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '10.0.0.1, 10.0.0.1, 3.4.5.6'
|
1090
|
-
res.body.
|
1219
|
+
res.body.must_equal '3.4.5.6'
|
1091
1220
|
|
1092
1221
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '127.0.0.1, 3.4.5.6'
|
1093
|
-
res.body.
|
1222
|
+
res.body.must_equal '3.4.5.6'
|
1094
1223
|
|
1095
1224
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,192.168.0.1'
|
1096
|
-
res.body.
|
1225
|
+
res.body.must_equal 'unknown'
|
1097
1226
|
|
1098
1227
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'other,unknown,192.168.0.1'
|
1099
|
-
res.body.
|
1228
|
+
res.body.must_equal 'unknown'
|
1100
1229
|
|
1101
1230
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'unknown,localhost,192.168.0.1'
|
1102
|
-
res.body.
|
1231
|
+
res.body.must_equal 'unknown'
|
1103
1232
|
|
1104
1233
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '9.9.9.9, 3.4.5.6, 10.0.0.1, 172.31.4.4'
|
1105
|
-
res.body.
|
1234
|
+
res.body.must_equal '3.4.5.6'
|
1106
1235
|
|
1107
1236
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '::1,2620:0:1c00:0:812c:9583:754b:ca11'
|
1108
|
-
res.body.
|
1237
|
+
res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
|
1109
1238
|
|
1110
1239
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,::1'
|
1111
|
-
res.body.
|
1240
|
+
res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
|
1112
1241
|
|
1113
1242
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => 'fd5b:982e:9130:247f:0000:0000:0000:0000,2620:0:1c00:0:812c:9583:754b:ca11'
|
1114
|
-
res.body.
|
1243
|
+
res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
|
1115
1244
|
|
1116
1245
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '2620:0:1c00:0:812c:9583:754b:ca11,fd5b:982e:9130:247f:0000:0000:0000:0000'
|
1117
|
-
res.body.
|
1246
|
+
res.body.must_equal '2620:0:1c00:0:812c:9583:754b:ca11'
|
1118
1247
|
|
1119
1248
|
res = mock.get '/',
|
1120
1249
|
'HTTP_X_FORWARDED_FOR' => '1.1.1.1, 127.0.0.1',
|
1121
1250
|
'HTTP_CLIENT_IP' => '1.1.1.1'
|
1122
|
-
res.body.
|
1251
|
+
res.body.must_equal '1.1.1.1'
|
1123
1252
|
|
1124
1253
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, 9.9.9.9'
|
1125
|
-
res.body.
|
1254
|
+
res.body.must_equal '9.9.9.9'
|
1126
1255
|
|
1127
1256
|
res = mock.get '/', 'HTTP_X_FORWARDED_FOR' => '8.8.8.8, fe80::202:b3ff:fe1e:8329'
|
1128
|
-
res.body.
|
1257
|
+
res.body.must_equal 'fe80::202:b3ff:fe1e:8329'
|
1129
1258
|
|
1130
1259
|
# Unix Sockets
|
1131
1260
|
res = mock.get '/',
|
1132
1261
|
'REMOTE_ADDR' => 'unix',
|
1133
1262
|
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
1134
|
-
res.body.
|
1263
|
+
res.body.must_equal '3.4.5.6'
|
1135
1264
|
|
1136
1265
|
res = mock.get '/',
|
1137
1266
|
'REMOTE_ADDR' => 'unix:/tmp/foo',
|
1138
1267
|
'HTTP_X_FORWARDED_FOR' => '3.4.5.6'
|
1139
|
-
res.body.
|
1268
|
+
res.body.must_equal '3.4.5.6'
|
1140
1269
|
end
|
1141
1270
|
|
1142
|
-
|
1271
|
+
it "not allow IP spoofing via Client-IP and X-Forwarded-For headers" do
|
1143
1272
|
mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
|
1144
1273
|
|
1145
1274
|
# IP Spoofing attempt:
|
@@ -1154,31 +1283,45 @@ EOF
|
|
1154
1283
|
res = mock.get '/',
|
1155
1284
|
'HTTP_X_FORWARDED_FOR' => '6.6.6.6, 2.2.2.3, 192.168.0.7',
|
1156
1285
|
'HTTP_CLIENT_IP' => '6.6.6.6'
|
1157
|
-
res.body.
|
1158
|
-
end
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
req.
|
1171
|
-
req.trusted_proxy?('
|
1172
|
-
req.trusted_proxy?('
|
1173
|
-
req.trusted_proxy?('
|
1174
|
-
|
1175
|
-
req.trusted_proxy?(
|
1176
|
-
req.trusted_proxy?(
|
1177
|
-
req.trusted_proxy?(
|
1178
|
-
req.trusted_proxy?(
|
1179
|
-
req.trusted_proxy?(
|
1180
|
-
req.trusted_proxy?(
|
1181
|
-
req.trusted_proxy?(
|
1286
|
+
res.body.must_equal '2.2.2.3'
|
1287
|
+
end
|
1288
|
+
|
1289
|
+
it "preserves ip for trusted proxy chain" do
|
1290
|
+
mock = Rack::MockRequest.new(Rack::Lint.new(ip_app))
|
1291
|
+
res = mock.get '/',
|
1292
|
+
'HTTP_X_FORWARDED_FOR' => '192.168.0.11, 192.168.0.7',
|
1293
|
+
'HTTP_CLIENT_IP' => '127.0.0.1'
|
1294
|
+
res.body.must_equal '192.168.0.11'
|
1295
|
+
|
1296
|
+
end
|
1297
|
+
|
1298
|
+
it "regards local addresses as proxies" do
|
1299
|
+
req = make_request(Rack::MockRequest.env_for("/"))
|
1300
|
+
req.trusted_proxy?('127.0.0.1').must_equal 0
|
1301
|
+
req.trusted_proxy?('10.0.0.1').must_equal 0
|
1302
|
+
req.trusted_proxy?('172.16.0.1').must_equal 0
|
1303
|
+
req.trusted_proxy?('172.20.0.1').must_equal 0
|
1304
|
+
req.trusted_proxy?('172.30.0.1').must_equal 0
|
1305
|
+
req.trusted_proxy?('172.31.0.1').must_equal 0
|
1306
|
+
req.trusted_proxy?('192.168.0.1').must_equal 0
|
1307
|
+
req.trusted_proxy?('::1').must_equal 0
|
1308
|
+
req.trusted_proxy?('fd00::').must_equal 0
|
1309
|
+
req.trusted_proxy?('localhost').must_equal 0
|
1310
|
+
req.trusted_proxy?('unix').must_equal 0
|
1311
|
+
req.trusted_proxy?('unix:/tmp/sock').must_equal 0
|
1312
|
+
|
1313
|
+
req.trusted_proxy?("unix.example.org").must_be_nil
|
1314
|
+
req.trusted_proxy?("example.org\n127.0.0.1").must_be_nil
|
1315
|
+
req.trusted_proxy?("127.0.0.1\nexample.org").must_be_nil
|
1316
|
+
req.trusted_proxy?("11.0.0.1").must_be_nil
|
1317
|
+
req.trusted_proxy?("172.15.0.1").must_be_nil
|
1318
|
+
req.trusted_proxy?("172.32.0.1").must_be_nil
|
1319
|
+
req.trusted_proxy?("2001:470:1f0b:18f8::1").must_be_nil
|
1320
|
+
end
|
1321
|
+
|
1322
|
+
it "sets the default session to an empty hash" do
|
1323
|
+
req = make_request(Rack::MockRequest.env_for("http://example.com:8080/"))
|
1324
|
+
assert_equal Hash.new, req.session
|
1182
1325
|
end
|
1183
1326
|
|
1184
1327
|
class MyRequest < Rack::Request
|
@@ -1187,46 +1330,78 @@ EOF
|
|
1187
1330
|
end
|
1188
1331
|
end
|
1189
1332
|
|
1190
|
-
|
1333
|
+
it "allow subclass request to be instantiated after parent request" do
|
1191
1334
|
env = Rack::MockRequest.env_for("/?foo=bar")
|
1192
1335
|
|
1193
|
-
req1 =
|
1194
|
-
req1.GET.
|
1195
|
-
req1.params.
|
1336
|
+
req1 = make_request(env)
|
1337
|
+
req1.GET.must_equal "foo" => "bar"
|
1338
|
+
req1.params.must_equal "foo" => "bar"
|
1196
1339
|
|
1197
1340
|
req2 = MyRequest.new(env)
|
1198
|
-
req2.GET.
|
1199
|
-
req2.params.
|
1341
|
+
req2.GET.must_equal "foo" => "bar"
|
1342
|
+
req2.params.must_equal :foo => "bar"
|
1200
1343
|
end
|
1201
1344
|
|
1202
|
-
|
1345
|
+
it "allow parent request to be instantiated after subclass request" do
|
1203
1346
|
env = Rack::MockRequest.env_for("/?foo=bar")
|
1204
1347
|
|
1205
1348
|
req1 = MyRequest.new(env)
|
1206
|
-
req1.GET.
|
1207
|
-
req1.params.
|
1349
|
+
req1.GET.must_equal "foo" => "bar"
|
1350
|
+
req1.params.must_equal :foo => "bar"
|
1208
1351
|
|
1209
|
-
req2 =
|
1210
|
-
req2.GET.
|
1211
|
-
req2.params.
|
1352
|
+
req2 = make_request(env)
|
1353
|
+
req2.GET.must_equal "foo" => "bar"
|
1354
|
+
req2.params.must_equal "foo" => "bar"
|
1212
1355
|
end
|
1213
1356
|
|
1214
|
-
|
1357
|
+
it "raise TypeError every time if request parameters are broken" do
|
1215
1358
|
broken_query = Rack::MockRequest.env_for("/?foo%5B%5D=0&foo%5Bbar%5D=1")
|
1216
|
-
req =
|
1217
|
-
lambda{req.GET}.
|
1218
|
-
lambda{req.params}.
|
1359
|
+
req = make_request(broken_query)
|
1360
|
+
lambda{req.GET}.must_raise TypeError
|
1361
|
+
lambda{req.params}.must_raise TypeError
|
1219
1362
|
end
|
1220
1363
|
|
1221
1364
|
(0x20...0x7E).collect { |a|
|
1222
1365
|
b = a.chr
|
1223
1366
|
c = CGI.escape(b)
|
1224
|
-
|
1367
|
+
it "not strip '#{a}' => '#{c}' => '#{b}' escaped character from parameters when accessed as string" do
|
1225
1368
|
url = "/?foo=#{c}bar#{c}"
|
1226
1369
|
env = Rack::MockRequest.env_for(url)
|
1227
|
-
req2 =
|
1228
|
-
req2.GET.
|
1229
|
-
req2.params.
|
1370
|
+
req2 = make_request(env)
|
1371
|
+
req2.GET.must_equal "foo" => "#{b}bar#{b}"
|
1372
|
+
req2.params.must_equal "foo" => "#{b}bar#{b}"
|
1230
1373
|
end
|
1231
1374
|
}
|
1375
|
+
|
1376
|
+
class NonDelegate < Rack::Request
|
1377
|
+
def delegate?; false; end
|
1378
|
+
end
|
1379
|
+
|
1380
|
+
def make_request(env)
|
1381
|
+
NonDelegate.new env
|
1382
|
+
end
|
1383
|
+
|
1384
|
+
class TestProxyRequest < RackRequestTest
|
1385
|
+
class DelegateRequest
|
1386
|
+
include Rack::Request::Helpers
|
1387
|
+
extend Forwardable
|
1388
|
+
|
1389
|
+
def_delegators :@req, :has_header?, :get_header, :fetch_header,
|
1390
|
+
:each_header, :set_header, :add_header, :delete_header
|
1391
|
+
|
1392
|
+
def_delegators :@req, :[], :[]=, :values_at
|
1393
|
+
|
1394
|
+
def initialize(req)
|
1395
|
+
@req = req
|
1396
|
+
end
|
1397
|
+
|
1398
|
+
def delegate?; true; end
|
1399
|
+
|
1400
|
+
def env; @req.env.dup; end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
def make_request(env)
|
1404
|
+
DelegateRequest.new super(env)
|
1405
|
+
end
|
1406
|
+
end
|
1232
1407
|
end
|