rack 1.5.5 → 1.6.0.beta
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 +4 -4
- data/KNOWN-ISSUES +14 -0
- data/README.rdoc +10 -6
- data/Rakefile +3 -4
- data/SPEC +59 -23
- data/lib/rack.rb +2 -1
- data/lib/rack/auth/abstract/request.rb +1 -1
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/md5.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +1 -1
- data/lib/rack/builder.rb +19 -4
- data/lib/rack/cascade.rb +2 -2
- data/lib/rack/chunked.rb +12 -1
- data/lib/rack/commonlogger.rb +13 -5
- data/lib/rack/conditionalget.rb +14 -2
- data/lib/rack/content_length.rb +5 -1
- data/lib/rack/deflater.rb +52 -13
- data/lib/rack/directory.rb +8 -2
- data/lib/rack/etag.rb +14 -6
- data/lib/rack/file.rb +10 -14
- data/lib/rack/handler.rb +2 -0
- data/lib/rack/handler/fastcgi.rb +4 -1
- data/lib/rack/handler/mongrel.rb +8 -2
- data/lib/rack/handler/scgi.rb +4 -1
- data/lib/rack/handler/thin.rb +8 -2
- data/lib/rack/handler/webrick.rb +46 -6
- data/lib/rack/head.rb +7 -2
- data/lib/rack/lint.rb +73 -25
- data/lib/rack/lobster.rb +8 -3
- data/lib/rack/methodoverride.rb +14 -3
- data/lib/rack/mime.rb +1 -15
- data/lib/rack/mock.rb +15 -7
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/multipart/parser.rb +107 -53
- data/lib/rack/multipart/uploaded_file.rb +2 -2
- data/lib/rack/nulllogger.rb +21 -2
- data/lib/rack/request.rb +38 -24
- data/lib/rack/response.rb +5 -0
- data/lib/rack/sendfile.rb +10 -5
- data/lib/rack/server.rb +45 -17
- data/lib/rack/session/abstract/id.rb +7 -6
- data/lib/rack/session/cookie.rb +17 -7
- data/lib/rack/session/memcache.rb +4 -4
- data/lib/rack/session/pool.rb +3 -6
- data/lib/rack/showexceptions.rb +20 -11
- data/lib/rack/showstatus.rb +1 -1
- data/lib/rack/static.rb +27 -30
- data/lib/rack/tempfile_reaper.rb +22 -0
- data/lib/rack/urlmap.rb +17 -3
- data/lib/rack/utils.rb +78 -47
- data/lib/rack/utils/okjson.rb +90 -91
- data/rack.gemspec +3 -3
- data/test/multipart/filename_and_no_name +6 -0
- data/test/multipart/invalid_character +6 -0
- data/test/spec_builder.rb +13 -4
- data/test/spec_chunked.rb +16 -0
- data/test/spec_commonlogger.rb +36 -0
- data/test/spec_content_length.rb +3 -1
- data/test/spec_deflater.rb +283 -148
- data/test/spec_etag.rb +11 -2
- data/test/spec_file.rb +11 -3
- data/test/spec_head.rb +2 -0
- data/test/spec_lobster.rb +1 -1
- data/test/spec_mock.rb +8 -0
- data/test/spec_multipart.rb +111 -49
- data/test/spec_request.rb +109 -25
- data/test/spec_response.rb +30 -0
- data/test/spec_server.rb +20 -5
- data/test/spec_session_cookie.rb +45 -2
- data/test/spec_session_memcache.rb +1 -1
- data/test/spec_showexceptions.rb +29 -36
- data/test/spec_showstatus.rb +19 -0
- data/test/spec_tempfile_reaper.rb +63 -0
- data/test/spec_urlmap.rb +23 -0
- data/test/spec_utils.rb +60 -10
- data/test/spec_webrick.rb +41 -0
- metadata +12 -9
- data/test/cgi/lighttpd.errors +0 -1
- data/test/multipart/three_files_three_fields +0 -31
data/test/spec_response.rb
CHANGED
@@ -85,6 +85,18 @@ describe Rack::Response do
|
|
85
85
|
response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
|
86
86
|
end
|
87
87
|
|
88
|
+
it "can set http only cookies with :http_only" do
|
89
|
+
response = Rack::Response.new
|
90
|
+
response.set_cookie "foo", {:value => "bar", :http_only => true}
|
91
|
+
response["Set-Cookie"].should.equal "foo=bar; HttpOnly"
|
92
|
+
end
|
93
|
+
|
94
|
+
it "can set prefers :httponly for http only cookie setting when :httponly and :http_only provided" do
|
95
|
+
response = Rack::Response.new
|
96
|
+
response.set_cookie "foo", {:value => "bar", :httponly => false, :http_only => true}
|
97
|
+
response["Set-Cookie"].should.equal "foo=bar"
|
98
|
+
end
|
99
|
+
|
88
100
|
it "can delete cookies" do
|
89
101
|
response = Rack::Response.new
|
90
102
|
response.set_cookie "foo", "bar"
|
@@ -211,11 +223,24 @@ describe Rack::Response do
|
|
211
223
|
res.should.be.successful
|
212
224
|
res.should.be.ok
|
213
225
|
|
226
|
+
res.status = 201
|
227
|
+
res.should.be.successful
|
228
|
+
res.should.be.created
|
229
|
+
|
230
|
+
res.status = 202
|
231
|
+
res.should.be.successful
|
232
|
+
res.should.be.accepted
|
233
|
+
|
214
234
|
res.status = 400
|
215
235
|
res.should.not.be.successful
|
216
236
|
res.should.be.client_error
|
217
237
|
res.should.be.bad_request
|
218
238
|
|
239
|
+
res.status = 401
|
240
|
+
res.should.not.be.successful
|
241
|
+
res.should.be.client_error
|
242
|
+
res.should.be.unauthorized
|
243
|
+
|
219
244
|
res.status = 404
|
220
245
|
res.should.not.be.successful
|
221
246
|
res.should.be.client_error
|
@@ -226,6 +251,11 @@ describe Rack::Response do
|
|
226
251
|
res.should.be.client_error
|
227
252
|
res.should.be.method_not_allowed
|
228
253
|
|
254
|
+
res.status = 418
|
255
|
+
res.should.not.be.successful
|
256
|
+
res.should.be.client_error
|
257
|
+
res.should.be.i_m_a_teapot
|
258
|
+
|
229
259
|
res.status = 422
|
230
260
|
res.should.not.be.successful
|
231
261
|
res.should.be.client_error
|
data/test/spec_server.rb
CHANGED
@@ -30,14 +30,24 @@ describe Rack::Server do
|
|
30
30
|
|
31
31
|
should "not include Rack::Lint in deployment or none environments" do
|
32
32
|
server = Rack::Server.new(:app => 'foo')
|
33
|
-
server.
|
34
|
-
server.
|
33
|
+
server.default_middleware_by_environment['deployment'].flatten.should.not.include(Rack::Lint)
|
34
|
+
server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::Lint)
|
35
35
|
end
|
36
36
|
|
37
37
|
should "not include Rack::ShowExceptions in deployment or none environments" do
|
38
38
|
server = Rack::Server.new(:app => 'foo')
|
39
|
-
server.
|
40
|
-
server.
|
39
|
+
server.default_middleware_by_environment['deployment'].flatten.should.not.include(Rack::ShowExceptions)
|
40
|
+
server.default_middleware_by_environment['none'].flatten.should.not.include(Rack::ShowExceptions)
|
41
|
+
end
|
42
|
+
|
43
|
+
should "always return an empty array for unknown environments" do
|
44
|
+
server = Rack::Server.new(:app => 'foo')
|
45
|
+
server.default_middleware_by_environment['production'].should.equal []
|
46
|
+
end
|
47
|
+
|
48
|
+
should "include Rack::TempfileReaper in deployment environment" do
|
49
|
+
server = Rack::Server.new(:app => 'foo')
|
50
|
+
server.middleware['deployment'].flatten.should.include(Rack::TempfileReaper)
|
41
51
|
end
|
42
52
|
|
43
53
|
should "support CGI" do
|
@@ -51,9 +61,14 @@ describe Rack::Server do
|
|
51
61
|
end
|
52
62
|
end
|
53
63
|
|
64
|
+
should "be quiet if said so" do
|
65
|
+
server = Rack::Server.new(:app => "FOO", :quiet => true)
|
66
|
+
Rack::Server.logging_middleware.call(server).should.eql(nil)
|
67
|
+
end
|
68
|
+
|
54
69
|
should "not force any middleware under the none configuration" do
|
55
70
|
server = Rack::Server.new(:app => 'foo')
|
56
|
-
server.
|
71
|
+
server.default_middleware_by_environment['none'].should.be.empty
|
57
72
|
end
|
58
73
|
|
59
74
|
should "use a full path to the pidfile" do
|
data/test/spec_session_cookie.rb
CHANGED
@@ -119,13 +119,35 @@ describe Rack::Session::Cookie do
|
|
119
119
|
coder.decode('lulz').should.equal nil
|
120
120
|
end
|
121
121
|
end
|
122
|
+
|
123
|
+
describe 'ZipJSON' do
|
124
|
+
it 'jsons, deflates, and base64 encodes' do
|
125
|
+
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
126
|
+
obj = %w[fuuuuu]
|
127
|
+
json = Rack::Utils::OkJson.encode(obj)
|
128
|
+
coder.encode(obj).should.equal [Zlib::Deflate.deflate(json)].pack('m')
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'base64 decodes, inflates, and decodes json' do
|
132
|
+
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
133
|
+
obj = %w[fuuuuu]
|
134
|
+
json = Rack::Utils::OkJson.encode(obj)
|
135
|
+
b64 = [Zlib::Deflate.deflate(json)].pack('m')
|
136
|
+
coder.decode(b64).should.equal obj
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'rescues failures on decode' do
|
140
|
+
coder = Rack::Session::Cookie::Base64::ZipJSON.new
|
141
|
+
coder.decode('lulz').should.equal nil
|
142
|
+
end
|
143
|
+
end
|
122
144
|
end
|
123
145
|
|
124
146
|
it "warns if no secret is given" do
|
125
|
-
|
147
|
+
Rack::Session::Cookie.new(incrementor)
|
126
148
|
@warnings.first.should =~ /no secret/i
|
127
149
|
@warnings.clear
|
128
|
-
|
150
|
+
Rack::Session::Cookie.new(incrementor, :secret => 'abc')
|
129
151
|
@warnings.should.be.empty?
|
130
152
|
end
|
131
153
|
|
@@ -364,4 +386,25 @@ describe Rack::Session::Cookie do
|
|
364
386
|
response.body.should.match(/counter/)
|
365
387
|
response.body.should.match(/foo/)
|
366
388
|
end
|
389
|
+
|
390
|
+
it "allows more than one '--' in the cookie when calculating digests" do
|
391
|
+
@counter = 0
|
392
|
+
app = lambda do |env|
|
393
|
+
env["rack.session"]["message"] ||= ""
|
394
|
+
env["rack.session"]["message"] << "#{(@counter += 1).to_s}--"
|
395
|
+
hash = env["rack.session"].dup
|
396
|
+
hash.delete("session_id")
|
397
|
+
Rack::Response.new(hash["message"]).to_a
|
398
|
+
end
|
399
|
+
# another example of an unsafe coder is Base64.urlsafe_encode64
|
400
|
+
unsafe_coder = Class.new {
|
401
|
+
def encode(hash); hash.inspect end
|
402
|
+
def decode(str); eval(str) if str; end
|
403
|
+
}.new
|
404
|
+
_app = [ app, { :secret => "test", :coder => unsafe_coder } ]
|
405
|
+
response = response_for(:app => _app)
|
406
|
+
response.body.should.equal "1--"
|
407
|
+
response = response_for(:app => _app, :cookie => response)
|
408
|
+
response.body.should.equal "1--2--"
|
409
|
+
end
|
367
410
|
end
|
@@ -274,7 +274,7 @@ begin
|
|
274
274
|
session['counter'].should.equal 2 # meeeh
|
275
275
|
|
276
276
|
tnum = rand(7).to_i+5
|
277
|
-
r = Array.new(tnum) do
|
277
|
+
r = Array.new(tnum) do
|
278
278
|
app = Rack::Utils::Context.new pool, time_delta
|
279
279
|
req = Rack::MockRequest.new app
|
280
280
|
Thread.new(req) do |run|
|
data/test/spec_showexceptions.rb
CHANGED
@@ -16,7 +16,7 @@ describe Rack::ShowExceptions do
|
|
16
16
|
))
|
17
17
|
|
18
18
|
lambda{
|
19
|
-
res = req.get("/")
|
19
|
+
res = req.get("/", "HTTP_ACCEPT" => "text/html")
|
20
20
|
}.should.not.raise
|
21
21
|
|
22
22
|
res.should.be.a.server_error
|
@@ -26,7 +26,7 @@ describe Rack::ShowExceptions do
|
|
26
26
|
res.should =~ /ShowExceptions/
|
27
27
|
end
|
28
28
|
|
29
|
-
it "responds with
|
29
|
+
it "responds with HTML only to requests accepting HTML" do
|
30
30
|
res = nil
|
31
31
|
|
32
32
|
req = Rack::MockRequest.new(
|
@@ -34,39 +34,32 @@ describe Rack::ShowExceptions do
|
|
34
34
|
lambda{|env| raise RuntimeError, "It was never supposed to work" }
|
35
35
|
))
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
res.status.should.equal 500
|
64
|
-
|
65
|
-
res.content_type.should.equal "text/html"
|
66
|
-
|
67
|
-
res.body.should.include "RuntimeError"
|
68
|
-
res.body.should.include "It was never supposed to work"
|
69
|
-
res.body.should.include Rack::Utils.escape_html(__FILE__)
|
37
|
+
[
|
38
|
+
# Serve text/html when the client accepts text/html
|
39
|
+
["text/html", ["/", {"HTTP_ACCEPT" => "text/html"}]],
|
40
|
+
["text/html", ["/", {"HTTP_ACCEPT" => "*/*"}]],
|
41
|
+
# Serve text/plain when the client does not accept text/html
|
42
|
+
["text/plain", ["/"]],
|
43
|
+
["text/plain", ["/", {"HTTP_ACCEPT" => "application/json"}]]
|
44
|
+
].each do |exmime, rargs|
|
45
|
+
lambda{
|
46
|
+
res = req.get(*rargs)
|
47
|
+
}.should.not.raise
|
48
|
+
|
49
|
+
res.should.be.a.server_error
|
50
|
+
res.status.should.equal 500
|
51
|
+
|
52
|
+
res.content_type.should.equal exmime
|
53
|
+
|
54
|
+
res.body.should.include "RuntimeError"
|
55
|
+
res.body.should.include "It was never supposed to work"
|
56
|
+
|
57
|
+
if exmime == "text/html"
|
58
|
+
res.body.should.include '</html>'
|
59
|
+
else
|
60
|
+
res.body.should.not.include '</html>'
|
61
|
+
end
|
62
|
+
end
|
70
63
|
end
|
71
64
|
|
72
65
|
it "handles exceptions without a backtrace" do
|
@@ -79,7 +72,7 @@ describe Rack::ShowExceptions do
|
|
79
72
|
)
|
80
73
|
|
81
74
|
lambda{
|
82
|
-
res = req.get("/")
|
75
|
+
res = req.get("/", "HTTP_ACCEPT" => "text/html")
|
83
76
|
}.should.not.raise
|
84
77
|
|
85
78
|
res.should.be.a.server_error
|
data/test/spec_showstatus.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'rack/showstatus'
|
2
2
|
require 'rack/lint'
|
3
3
|
require 'rack/mock'
|
4
|
+
require 'rack/utils'
|
4
5
|
|
5
6
|
describe Rack::ShowStatus do
|
6
7
|
def show_status(app)
|
@@ -40,6 +41,24 @@ describe Rack::ShowStatus do
|
|
40
41
|
res.should =~ /too meta/
|
41
42
|
end
|
42
43
|
|
44
|
+
should "escape error" do
|
45
|
+
detail = "<script>alert('hi \"')</script>"
|
46
|
+
req = Rack::MockRequest.new(
|
47
|
+
show_status(
|
48
|
+
lambda{|env|
|
49
|
+
env["rack.showstatus.detail"] = detail
|
50
|
+
[500, {"Content-Type" => "text/plain", "Content-Length" => "0"}, []]
|
51
|
+
}))
|
52
|
+
|
53
|
+
res = req.get("/", :lint => true)
|
54
|
+
res.should.be.not.empty
|
55
|
+
|
56
|
+
res["Content-Type"].should.equal("text/html")
|
57
|
+
res.should =~ /500/
|
58
|
+
res.should.not.include detail
|
59
|
+
res.body.should.include Rack::Utils.escape_html(detail)
|
60
|
+
end
|
61
|
+
|
43
62
|
should "not replace existing messages" do
|
44
63
|
req = Rack::MockRequest.new(
|
45
64
|
show_status(
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rack/tempfile_reaper'
|
2
|
+
require 'rack/lint'
|
3
|
+
require 'rack/mock'
|
4
|
+
|
5
|
+
describe Rack::TempfileReaper do
|
6
|
+
class MockTempfile
|
7
|
+
attr_reader :closed
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@closed = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def close!
|
14
|
+
@closed = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before do
|
19
|
+
@env = Rack::MockRequest.env_for
|
20
|
+
end
|
21
|
+
|
22
|
+
def call(app)
|
23
|
+
Rack::Lint.new(Rack::TempfileReaper.new(app)).call(@env)
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'do nothing (i.e. not bomb out) without env[rack.tempfiles]' do
|
27
|
+
app = lambda { |_| [200, {}, ['Hello, World!']] }
|
28
|
+
response = call(app)
|
29
|
+
response[2].close
|
30
|
+
response[0].should.equal(200)
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'close env[rack.tempfiles] when body is closed' do
|
34
|
+
tempfile1, tempfile2 = MockTempfile.new, MockTempfile.new
|
35
|
+
@env['rack.tempfiles'] = [ tempfile1, tempfile2 ]
|
36
|
+
app = lambda { |_| [200, {}, ['Hello, World!']] }
|
37
|
+
call(app)[2].close
|
38
|
+
tempfile1.closed.should.equal true
|
39
|
+
tempfile2.closed.should.equal true
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'initialize env[rack.tempfiles] when not already present' do
|
43
|
+
tempfile = MockTempfile.new
|
44
|
+
app = lambda do |env|
|
45
|
+
env['rack.tempfiles'] << tempfile
|
46
|
+
[200, {}, ['Hello, World!']]
|
47
|
+
end
|
48
|
+
call(app)[2].close
|
49
|
+
tempfile.closed.should.equal true
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'append env[rack.tempfiles] when already present' do
|
53
|
+
tempfile1, tempfile2 = MockTempfile.new, MockTempfile.new
|
54
|
+
@env['rack.tempfiles'] = [ tempfile1 ]
|
55
|
+
app = lambda do |env|
|
56
|
+
env['rack.tempfiles'] << tempfile2
|
57
|
+
[200, {}, ['Hello, World!']]
|
58
|
+
end
|
59
|
+
call(app)[2].close
|
60
|
+
tempfile1.closed.should.equal true
|
61
|
+
tempfile2.closed.should.equal true
|
62
|
+
end
|
63
|
+
end
|
data/test/spec_urlmap.rb
CHANGED
@@ -210,4 +210,27 @@ describe Rack::URLMap do
|
|
210
210
|
res["X-PathInfo"].should.equal "/http://example.org/bar"
|
211
211
|
res["X-ScriptName"].should.equal ""
|
212
212
|
end
|
213
|
+
|
214
|
+
should "not be case sensitive with hosts" do
|
215
|
+
map = Rack::Lint.new(Rack::URLMap.new("http://example.org/" => lambda { |env|
|
216
|
+
[200,
|
217
|
+
{ "Content-Type" => "text/plain",
|
218
|
+
"X-Position" => "root",
|
219
|
+
"X-PathInfo" => env["PATH_INFO"],
|
220
|
+
"X-ScriptName" => env["SCRIPT_NAME"]
|
221
|
+
}, [""]]}
|
222
|
+
))
|
223
|
+
|
224
|
+
res = Rack::MockRequest.new(map).get("http://example.org/")
|
225
|
+
res.should.be.ok
|
226
|
+
res["X-Position"].should.equal "root"
|
227
|
+
res["X-PathInfo"].should.equal "/"
|
228
|
+
res["X-ScriptName"].should.equal ""
|
229
|
+
|
230
|
+
res = Rack::MockRequest.new(map).get("http://EXAMPLE.ORG/")
|
231
|
+
res.should.be.ok
|
232
|
+
res["X-Position"].should.equal "root"
|
233
|
+
res["X-PathInfo"].should.equal "/"
|
234
|
+
res["X-ScriptName"].should.equal ""
|
235
|
+
end
|
213
236
|
end
|
data/test/spec_utils.rb
CHANGED
@@ -123,15 +123,14 @@ describe Rack::Utils do
|
|
123
123
|
Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
|
124
124
|
end
|
125
125
|
|
126
|
-
should "
|
127
|
-
|
126
|
+
should "not create infinite loops with cycle structures" do
|
127
|
+
ex = { "foo" => nil }
|
128
|
+
ex["foo"] = ex
|
128
129
|
|
130
|
+
params = Rack::Utils::KeySpaceConstrainedParams.new
|
131
|
+
params['foo'] = params
|
129
132
|
lambda {
|
130
|
-
|
131
|
-
}.should.raise(RangeError)
|
132
|
-
|
133
|
-
lambda {
|
134
|
-
Rack::Utils.parse_nested_query("foo#{"[a]" * (len - 1)}=bar")
|
133
|
+
params.to_params_hash.to_s.should.equal ex.to_s
|
135
134
|
}.should.not.raise
|
136
135
|
end
|
137
136
|
|
@@ -169,6 +168,16 @@ describe Rack::Utils do
|
|
169
168
|
should.equal "foo" => [""]
|
170
169
|
Rack::Utils.parse_nested_query("foo[]=bar").
|
171
170
|
should.equal "foo" => ["bar"]
|
171
|
+
Rack::Utils.parse_nested_query("foo[]=bar&foo").
|
172
|
+
should.equal "foo" => nil
|
173
|
+
Rack::Utils.parse_nested_query("foo[]=bar&foo[").
|
174
|
+
should.equal "foo" => ["bar"], "foo[" => nil
|
175
|
+
Rack::Utils.parse_nested_query("foo[]=bar&foo[=baz").
|
176
|
+
should.equal "foo" => ["bar"], "foo[" => "baz"
|
177
|
+
Rack::Utils.parse_nested_query("foo[]=bar&foo[]").
|
178
|
+
should.equal "foo" => ["bar", nil]
|
179
|
+
Rack::Utils.parse_nested_query("foo[]=bar&foo[]=").
|
180
|
+
should.equal "foo" => ["bar", ""]
|
172
181
|
|
173
182
|
Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
|
174
183
|
should.equal "foo" => ["1", "2"]
|
@@ -204,16 +213,22 @@ describe Rack::Utils do
|
|
204
213
|
should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
|
205
214
|
|
206
215
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
|
207
|
-
should.raise(
|
216
|
+
should.raise(Rack::Utils::ParameterTypeError).
|
208
217
|
message.should.equal "expected Hash (got String) for param `y'"
|
209
218
|
|
210
219
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
|
211
|
-
should.raise(
|
220
|
+
should.raise(Rack::Utils::ParameterTypeError).
|
212
221
|
message.should.match(/expected Array \(got [^)]*\) for param `x'/)
|
213
222
|
|
214
223
|
lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
|
215
|
-
should.raise(
|
224
|
+
should.raise(Rack::Utils::ParameterTypeError).
|
216
225
|
message.should.equal "expected Array (got String) for param `y'"
|
226
|
+
|
227
|
+
if RUBY_VERSION.to_f > 1.9
|
228
|
+
lambda { Rack::Utils.parse_nested_query("foo%81E=1") }.
|
229
|
+
should.raise(Rack::Utils::InvalidParameterError).
|
230
|
+
message.should.equal "invalid byte sequence in UTF-8"
|
231
|
+
end
|
217
232
|
end
|
218
233
|
|
219
234
|
should "build query strings correctly" do
|
@@ -233,6 +248,8 @@ describe Rack::Utils do
|
|
233
248
|
|
234
249
|
Rack::Utils.build_nested_query("foo" => "1", "bar" => "2").
|
235
250
|
should.be equal_query_to("foo=1&bar=2")
|
251
|
+
Rack::Utils.build_nested_query("foo" => 1, "bar" => 2).
|
252
|
+
should.be equal_query_to("foo=1&bar=2")
|
236
253
|
Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?").
|
237
254
|
should.be equal_query_to("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F")
|
238
255
|
|
@@ -242,6 +259,14 @@ describe Rack::Utils do
|
|
242
259
|
should.equal "foo[]="
|
243
260
|
Rack::Utils.build_nested_query("foo" => ["bar"]).
|
244
261
|
should.equal "foo[]=bar"
|
262
|
+
Rack::Utils.build_nested_query('foo' => []).
|
263
|
+
should.equal ''
|
264
|
+
Rack::Utils.build_nested_query('foo' => {}).
|
265
|
+
should.equal ''
|
266
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => []).
|
267
|
+
should.equal 'foo=bar'
|
268
|
+
Rack::Utils.build_nested_query('foo' => 'bar', 'baz' => {}).
|
269
|
+
should.equal 'foo=bar'
|
245
270
|
|
246
271
|
# The ordering of the output query string is unpredictable with 1.8's
|
247
272
|
# unordered hash. Test that build_nested_query performs the inverse
|
@@ -302,9 +327,15 @@ describe Rack::Utils do
|
|
302
327
|
# Higher quality matches are preferred
|
303
328
|
Rack::Utils.best_q_match("text/*;q=0.5,text/plain;q=1.0", %w[text/plain text/html]).should.equal "text/plain"
|
304
329
|
|
330
|
+
# Respect requested content type
|
331
|
+
Rack::Utils.best_q_match("application/json", %w[application/vnd.lotus-1-2-3 application/json]).should.equal "application/json"
|
332
|
+
|
305
333
|
# All else equal, the available mimes are preferred in order
|
306
334
|
Rack::Utils.best_q_match("text/*", %w[text/html text/plain]).should.equal "text/html"
|
307
335
|
Rack::Utils.best_q_match("text/plain,text/html", %w[text/html text/plain]).should.equal "text/html"
|
336
|
+
|
337
|
+
# When there are no matches, return nil:
|
338
|
+
Rack::Utils.best_q_match("application/json", %w[text/html text/plain]).should.equal nil
|
308
339
|
end
|
309
340
|
|
310
341
|
should "escape html entities [&><'\"/]" do
|
@@ -390,6 +421,25 @@ describe Rack::Utils do
|
|
390
421
|
should "return rfc2109 format from rfc2109 helper" do
|
391
422
|
Rack::Utils.rfc2109(Time.at(0).gmtime).should == "Thu, 01-Jan-1970 00:00:00 GMT"
|
392
423
|
end
|
424
|
+
|
425
|
+
should "clean directory traversal" do
|
426
|
+
Rack::Utils.clean_path_info("/cgi/../cgi/test").should.equal "/cgi/test"
|
427
|
+
Rack::Utils.clean_path_info(".").should.empty
|
428
|
+
Rack::Utils.clean_path_info("test/..").should.empty
|
429
|
+
end
|
430
|
+
|
431
|
+
should "clean unsafe directory traversal to safe path" do
|
432
|
+
Rack::Utils.clean_path_info("/../README.rdoc").should.equal "/README.rdoc"
|
433
|
+
Rack::Utils.clean_path_info("../test/spec_utils.rb").should.equal "test/spec_utils.rb"
|
434
|
+
end
|
435
|
+
|
436
|
+
should "not clean directory traversal with encoded periods" do
|
437
|
+
Rack::Utils.clean_path_info("/%2E%2E/README").should.equal "/%2E%2E/README"
|
438
|
+
end
|
439
|
+
|
440
|
+
should "clean slash only paths" do
|
441
|
+
Rack::Utils.clean_path_info("/").should.equal "/"
|
442
|
+
end
|
393
443
|
end
|
394
444
|
|
395
445
|
describe Rack::Utils, "byte_range" do
|