rack 1.3.10 → 1.4.0
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.
- data/COPYING +1 -1
- data/KNOWN-ISSUES +0 -9
- data/README.rdoc +4 -118
- data/Rakefile +15 -0
- data/SPEC +3 -5
- data/lib/rack.rb +0 -12
- data/lib/rack/auth/abstract/request.rb +1 -5
- data/lib/rack/auth/basic.rb +1 -1
- data/lib/rack/auth/digest/nonce.rb +1 -1
- data/lib/rack/backports/uri/common_18.rb +28 -14
- data/lib/rack/backports/uri/common_192.rb +17 -14
- data/lib/rack/body_proxy.rb +0 -10
- data/lib/rack/builder.rb +26 -18
- data/lib/rack/cascade.rb +1 -12
- data/lib/rack/chunked.rb +2 -0
- data/lib/rack/content_type.rb +7 -1
- data/lib/rack/deflater.rb +1 -5
- data/lib/rack/directory.rb +5 -1
- data/lib/rack/file.rb +26 -9
- data/lib/rack/handler.rb +2 -2
- data/lib/rack/head.rb +0 -1
- data/lib/rack/lint.rb +3 -5
- data/lib/rack/methodoverride.rb +10 -4
- data/lib/rack/mime.rb +606 -171
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/multipart.rb +2 -2
- data/lib/rack/multipart/parser.rb +3 -10
- data/lib/rack/reloader.rb +1 -1
- data/lib/rack/request.rb +45 -13
- data/lib/rack/response.rb +15 -14
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +4 -30
- data/lib/rack/session/abstract/id.rb +25 -6
- data/lib/rack/session/cookie.rb +12 -16
- data/lib/rack/static.rb +21 -8
- data/lib/rack/urlmap.rb +28 -13
- data/lib/rack/utils.rb +22 -28
- data/rack.gemspec +5 -5
- data/test/builder/end.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -0
- data/test/cgi/sample_rackup.ru +1 -1
- data/test/cgi/test+directory/test+file +1 -0
- data/test/cgi/test.ru +1 -1
- data/test/gemloader.rb +6 -2
- data/test/spec_auth_basic.rb +4 -9
- data/test/spec_auth_digest.rb +3 -16
- data/test/spec_body_proxy.rb +0 -4
- data/test/spec_builder.rb +63 -20
- data/test/spec_cascade.rb +10 -13
- data/test/spec_cgi.rb +1 -1
- data/test/spec_chunked.rb +39 -12
- data/test/spec_commonlogger.rb +4 -3
- data/test/spec_conditionalget.rb +16 -12
- data/test/spec_content_length.rb +1 -1
- data/test/spec_content_type.rb +6 -0
- data/test/spec_deflater.rb +2 -2
- data/test/spec_directory.rb +12 -0
- data/test/spec_fastcgi.rb +1 -1
- data/test/spec_file.rb +58 -8
- data/test/spec_head.rb +6 -18
- data/test/spec_lint.rb +2 -2
- data/test/spec_methodoverride.rb +15 -0
- data/test/spec_mock.rb +6 -2
- data/test/spec_mongrel.rb +8 -8
- data/test/spec_multipart.rb +10 -63
- data/test/spec_request.rb +94 -21
- data/test/spec_response.rb +22 -24
- data/test/spec_sendfile.rb +3 -0
- data/test/spec_server.rb +2 -49
- data/test/spec_session_cookie.rb +58 -22
- data/test/spec_session_memcache.rb +31 -1
- data/test/spec_session_pool.rb +10 -4
- data/test/spec_static.rb +8 -0
- data/test/spec_thin.rb +2 -2
- data/test/spec_utils.rb +38 -35
- data/test/spec_webrick.rb +5 -3
- data/test/static/index.html +1 -0
- metadata +13 -18
- data/contrib/rack.png +0 -0
- data/contrib/rack.svg +0 -150
- data/lib/rack/backports/uri/common_193.rb +0 -29
- data/test/builder/line.ru +0 -1
- data/test/spec_auth.rb +0 -57
data/test/spec_sendfile.rb
CHANGED
@@ -40,6 +40,7 @@ describe Rack::Sendfile do
|
|
40
40
|
request 'HTTP_X_SENDFILE_TYPE' => 'X-Sendfile' do |response|
|
41
41
|
response.should.be.ok
|
42
42
|
response.body.should.be.empty
|
43
|
+
response.headers['Content-Length'].should == '0'
|
43
44
|
response.headers['X-Sendfile'].should.equal '/tmp/hello.txt'
|
44
45
|
end
|
45
46
|
end
|
@@ -48,6 +49,7 @@ describe Rack::Sendfile do
|
|
48
49
|
request 'HTTP_X_SENDFILE_TYPE' => 'X-Lighttpd-Send-File' do |response|
|
49
50
|
response.should.be.ok
|
50
51
|
response.body.should.be.empty
|
52
|
+
response.headers['Content-Length'].should == '0'
|
51
53
|
response.headers['X-Lighttpd-Send-File'].should.equal '/tmp/hello.txt'
|
52
54
|
end
|
53
55
|
end
|
@@ -60,6 +62,7 @@ describe Rack::Sendfile do
|
|
60
62
|
request headers do |response|
|
61
63
|
response.should.be.ok
|
62
64
|
response.body.should.be.empty
|
65
|
+
response.headers['Content-Length'].should == '0'
|
63
66
|
response.headers['X-Accel-Redirect'].should.equal '/foo/bar/hello.txt'
|
64
67
|
end
|
65
68
|
end
|
data/test/spec_server.rb
CHANGED
@@ -10,13 +10,6 @@ describe Rack::Server do
|
|
10
10
|
lambda { |env| [200, {'Content-Type' => 'text/plain'}, ['success']] }
|
11
11
|
end
|
12
12
|
|
13
|
-
def with_stderr
|
14
|
-
old, $stderr = $stderr, StringIO.new
|
15
|
-
yield $stderr
|
16
|
-
ensure
|
17
|
-
$stderr = old
|
18
|
-
end
|
19
|
-
|
20
13
|
it "overrides :config if :app is passed in" do
|
21
14
|
server = Rack::Server.new(:app => "FOO")
|
22
15
|
server.app.should == "FOO"
|
@@ -68,8 +61,8 @@ describe Rack::Server do
|
|
68
61
|
:daemonize => false,
|
69
62
|
:server => 'webrick'
|
70
63
|
)
|
71
|
-
t = Thread.new { server.start }
|
72
|
-
|
64
|
+
t = Thread.new { server.start { |s| Thread.current[:server] = s } }
|
65
|
+
t.join(0.01) until t[:server] && t[:server].status != :Stop
|
73
66
|
body = open("http://127.0.0.1:#{server.options[:Port]}/") { |f| f.read }
|
74
67
|
body.should.eql('success')
|
75
68
|
|
@@ -78,44 +71,4 @@ describe Rack::Server do
|
|
78
71
|
open(pidfile) { |f| f.read.should.eql $$.to_s }
|
79
72
|
end
|
80
73
|
|
81
|
-
should "check pid file presence and running process" do
|
82
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write($$); break f }.path
|
83
|
-
server = Rack::Server.new(:pid => pidfile)
|
84
|
-
server.send(:pidfile_process_status).should.eql :running
|
85
|
-
end
|
86
|
-
|
87
|
-
should "check pid file presence and dead process" do
|
88
|
-
dead_pid = `echo $$`.to_i
|
89
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(dead_pid); break f }.path
|
90
|
-
server = Rack::Server.new(:pid => pidfile)
|
91
|
-
server.send(:pidfile_process_status).should.eql :dead
|
92
|
-
end
|
93
|
-
|
94
|
-
should "check pid file presence and exited process" do
|
95
|
-
pidfile = Tempfile.open('pidfile') { |f| break f }.path
|
96
|
-
::File.delete(pidfile)
|
97
|
-
server = Rack::Server.new(:pid => pidfile)
|
98
|
-
server.send(:pidfile_process_status).should.eql :exited
|
99
|
-
end
|
100
|
-
|
101
|
-
should "check pid file presence and not owned process" do
|
102
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
|
103
|
-
server = Rack::Server.new(:pid => pidfile)
|
104
|
-
server.send(:pidfile_process_status).should.eql :not_owned
|
105
|
-
end
|
106
|
-
|
107
|
-
should "inform the user about existing pidfiles with running processes" do
|
108
|
-
pidfile = Tempfile.open('pidfile') { |f| f.write(1); break f }.path
|
109
|
-
server = Rack::Server.new(:pid => pidfile)
|
110
|
-
with_stderr do |err|
|
111
|
-
should.raise(SystemExit) do
|
112
|
-
server.start
|
113
|
-
end
|
114
|
-
err.rewind
|
115
|
-
output = err.read
|
116
|
-
output.should.match(/already running/)
|
117
|
-
output.should.include? pidfile
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
74
|
end
|
data/test/spec_session_cookie.rb
CHANGED
@@ -11,7 +11,7 @@ describe Rack::Session::Cookie do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
session_id = lambda do |env|
|
14
|
-
Rack::Response.new(env["rack.session"].inspect).to_a
|
14
|
+
Rack::Response.new(env["rack.session"].to_hash.inspect).to_a
|
15
15
|
end
|
16
16
|
|
17
17
|
session_option = lambda do |opt|
|
@@ -24,17 +24,6 @@ describe Rack::Session::Cookie do
|
|
24
24
|
Rack::Response.new("Nothing").to_a
|
25
25
|
end
|
26
26
|
|
27
|
-
before do
|
28
|
-
@warnings = warnings = []
|
29
|
-
Rack::Session::Cookie.class_eval do
|
30
|
-
define_method(:warn) { |m| warnings << m }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
after do
|
35
|
-
Rack::Session::Cookie.class_eval { remove_method :warn }
|
36
|
-
end
|
37
|
-
|
38
27
|
describe 'Base64' do
|
39
28
|
it 'uses base64 to encode' do
|
40
29
|
coder = Rack::Session::Cookie::Base64.new
|
@@ -68,14 +57,6 @@ describe Rack::Session::Cookie do
|
|
68
57
|
end
|
69
58
|
end
|
70
59
|
|
71
|
-
it "warns if no secret is given" do
|
72
|
-
cookie = Rack::Session::Cookie.new(incrementor)
|
73
|
-
@warnings.first.should =~ /no secret/i
|
74
|
-
@warnings.clear
|
75
|
-
cookie = Rack::Session::Cookie.new(incrementor, :secret => 'abc')
|
76
|
-
@warnings.should.be.empty?
|
77
|
-
end
|
78
|
-
|
79
60
|
it 'uses a coder' do
|
80
61
|
identity = Class.new {
|
81
62
|
attr_reader :calls
|
@@ -118,18 +99,23 @@ describe Rack::Session::Cookie do
|
|
118
99
|
end
|
119
100
|
|
120
101
|
only_session_id = lambda do |env|
|
121
|
-
Rack::Response.new(env["rack.session"]["session_id"]).to_a
|
102
|
+
Rack::Response.new(env["rack.session"]["session_id"].to_s).to_a
|
122
103
|
end
|
123
104
|
|
124
105
|
it "renew session id" do
|
125
106
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor)).get("/")
|
126
107
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)).
|
127
108
|
get("/", "HTTP_COOKIE" => res["Set-Cookie"])
|
109
|
+
|
110
|
+
res.body.should.not.equal ""
|
128
111
|
old_session_id = res.body
|
112
|
+
|
129
113
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(renewer)).
|
130
114
|
get("/", "HTTP_COOKIE" => res["Set-Cookie"])
|
131
115
|
res = Rack::MockRequest.new(Rack::Session::Cookie.new(only_session_id)).
|
132
116
|
get("/", "HTTP_COOKIE" => res["Set-Cookie"])
|
117
|
+
|
118
|
+
res.body.should.not.equal ""
|
133
119
|
res.body.should.not.equal old_session_id
|
134
120
|
end
|
135
121
|
|
@@ -163,6 +149,18 @@ describe Rack::Session::Cookie do
|
|
163
149
|
res.body.should.equal '{"counter"=>3}'
|
164
150
|
end
|
165
151
|
|
152
|
+
it "loads from a cookie wih accept-only integrity hash for graceful key rotation" do
|
153
|
+
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test')).get("/")
|
154
|
+
cookie = res["Set-Cookie"]
|
155
|
+
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test2', :old_secret => 'test')).
|
156
|
+
get("/", "HTTP_COOKIE" => cookie)
|
157
|
+
res.body.should.equal '{"counter"=>2}'
|
158
|
+
cookie = res["Set-Cookie"]
|
159
|
+
res = Rack::MockRequest.new(Rack::Session::Cookie.new(incrementor, :secret => 'test3', :old_secret => 'test2')).
|
160
|
+
get("/", "HTTP_COOKIE" => cookie)
|
161
|
+
res.body.should.equal '{"counter"=>3}'
|
162
|
+
end
|
163
|
+
|
166
164
|
it "ignores tampered with session cookies" do
|
167
165
|
app = Rack::Session::Cookie.new(incrementor, :secret => 'test')
|
168
166
|
response1 = Rack::MockRequest.new(app).get("/")
|
@@ -177,6 +175,38 @@ describe Rack::Session::Cookie do
|
|
177
175
|
response2.body.should.equal '{"counter"=>1}'
|
178
176
|
end
|
179
177
|
|
178
|
+
describe "1.9 bugs relating to inspecting yet-to-be-loaded from cookie data: Rack::Session::Abstract::SessionHash" do
|
179
|
+
|
180
|
+
it "can handle Rack::Lint middleware" do
|
181
|
+
app = Rack::Session::Cookie.new(incrementor)
|
182
|
+
res = Rack::MockRequest.new(app).get("/")
|
183
|
+
|
184
|
+
app = Rack::Session::Cookie.new(Rack::Lint.new(session_id))
|
185
|
+
res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
|
186
|
+
res.body.should.not.be.nil
|
187
|
+
end
|
188
|
+
|
189
|
+
it "can handle a middleware that inspects the env" do
|
190
|
+
class TestEnvInspector
|
191
|
+
def initialize(app)
|
192
|
+
@app = app
|
193
|
+
end
|
194
|
+
def call(env)
|
195
|
+
env.inspect
|
196
|
+
@app.call(env)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
app = Rack::Session::Cookie.new(incrementor)
|
201
|
+
res = Rack::MockRequest.new(app).get("/")
|
202
|
+
|
203
|
+
app = Rack::Session::Cookie.new(TestEnvInspector.new(session_id))
|
204
|
+
res = Rack::MockRequest.new(app).get("/", "HTTP_COOKIE" => res["Set-Cookie"])
|
205
|
+
res.body.should.not.be.nil
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
180
210
|
it "returns the session id in the session hash" do
|
181
211
|
app = Rack::Session::Cookie.new(incrementor)
|
182
212
|
res = Rack::MockRequest.new(app).get("/")
|
@@ -211,10 +241,16 @@ describe Rack::Session::Cookie do
|
|
211
241
|
|
212
242
|
it "returns even if not read/written if :expire_after is set" do
|
213
243
|
app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
|
214
|
-
res = Rack::MockRequest.new(app).get("/")
|
244
|
+
res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'})
|
215
245
|
res["Set-Cookie"].should.not.be.nil
|
216
246
|
end
|
217
247
|
|
248
|
+
it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
|
249
|
+
app = Rack::Session::Cookie.new(nothing, :expire_after => 3600)
|
250
|
+
res = Rack::MockRequest.new(app).get("/")
|
251
|
+
res["Set-Cookie"].should.be.nil
|
252
|
+
end
|
253
|
+
|
218
254
|
it "exposes :secret in env['rack.session.option']" do
|
219
255
|
app = Rack::Session::Cookie.new(session_option[:secret], :secret => "foo")
|
220
256
|
res = Rack::MockRequest.new(app).get("/")
|
@@ -23,6 +23,10 @@ begin
|
|
23
23
|
env['rack.session.options'][:defer] = true
|
24
24
|
incrementor.call(env)
|
25
25
|
end
|
26
|
+
skip_session = proc do |env|
|
27
|
+
env['rack.session.options'][:skip] = true
|
28
|
+
incrementor.call(env)
|
29
|
+
end
|
26
30
|
|
27
31
|
# test memcache connection
|
28
32
|
Rack::Session::Memcache.new(incrementor)
|
@@ -168,14 +172,40 @@ begin
|
|
168
172
|
res4.body.should.equal '{"counter"=>1}'
|
169
173
|
end
|
170
174
|
|
171
|
-
it "omits cookie with :defer option" do
|
175
|
+
it "omits cookie with :defer option but still updates the state" do
|
172
176
|
pool = Rack::Session::Memcache.new(incrementor)
|
177
|
+
count = Rack::Utils::Context.new(pool, incrementor)
|
173
178
|
defer = Rack::Utils::Context.new(pool, defer_session)
|
174
179
|
dreq = Rack::MockRequest.new(defer)
|
180
|
+
creq = Rack::MockRequest.new(count)
|
175
181
|
|
176
182
|
res0 = dreq.get("/")
|
177
183
|
res0["Set-Cookie"].should.equal nil
|
178
184
|
res0.body.should.equal '{"counter"=>1}'
|
185
|
+
|
186
|
+
res0 = creq.get("/")
|
187
|
+
res1 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
188
|
+
res1.body.should.equal '{"counter"=>2}'
|
189
|
+
res2 = dreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
190
|
+
res2.body.should.equal '{"counter"=>3}'
|
191
|
+
end
|
192
|
+
|
193
|
+
it "omits cookie and state update with :skip option" do
|
194
|
+
pool = Rack::Session::Memcache.new(incrementor)
|
195
|
+
count = Rack::Utils::Context.new(pool, incrementor)
|
196
|
+
skip = Rack::Utils::Context.new(pool, skip_session)
|
197
|
+
sreq = Rack::MockRequest.new(skip)
|
198
|
+
creq = Rack::MockRequest.new(count)
|
199
|
+
|
200
|
+
res0 = sreq.get("/")
|
201
|
+
res0["Set-Cookie"].should.equal nil
|
202
|
+
res0.body.should.equal '{"counter"=>1}'
|
203
|
+
|
204
|
+
res0 = creq.get("/")
|
205
|
+
res1 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
206
|
+
res1.body.should.equal '{"counter"=>2}'
|
207
|
+
res2 = sreq.get("/", "HTTP_COOKIE" => res0["Set-Cookie"])
|
208
|
+
res2.body.should.equal '{"counter"=>2}'
|
179
209
|
end
|
180
210
|
|
181
211
|
it "updates deep hashes correctly" do
|
data/test/spec_session_pool.rb
CHANGED
@@ -181,20 +181,26 @@ describe Rack::Session::Pool do
|
|
181
181
|
end
|
182
182
|
|
183
183
|
it "does not return a cookie if cookie was not read/written" do
|
184
|
-
app = Rack::Session::
|
184
|
+
app = Rack::Session::Pool.new(nothing)
|
185
185
|
res = Rack::MockRequest.new(app).get("/")
|
186
186
|
res["Set-Cookie"].should.be.nil
|
187
187
|
end
|
188
188
|
|
189
189
|
it "does not return a cookie if cookie was not written (only read)" do
|
190
|
-
app = Rack::Session::
|
190
|
+
app = Rack::Session::Pool.new(session_id)
|
191
191
|
res = Rack::MockRequest.new(app).get("/")
|
192
192
|
res["Set-Cookie"].should.be.nil
|
193
193
|
end
|
194
194
|
|
195
195
|
it "returns even if not read/written if :expire_after is set" do
|
196
|
-
app = Rack::Session::
|
197
|
-
res = Rack::MockRequest.new(app).get("/")
|
196
|
+
app = Rack::Session::Pool.new(nothing, :expire_after => 3600)
|
197
|
+
res = Rack::MockRequest.new(app).get("/", 'rack.session' => {'not' => 'empty'})
|
198
198
|
res["Set-Cookie"].should.not.be.nil
|
199
199
|
end
|
200
|
+
|
201
|
+
it "returns no cookie if no data was written and no session was created previously, even if :expire_after is set" do
|
202
|
+
app = Rack::Session::Pool.new(nothing, :expire_after => 3600)
|
203
|
+
res = Rack::MockRequest.new(app).get("/")
|
204
|
+
res["Set-Cookie"].should.be.nil
|
205
|
+
end
|
200
206
|
end
|
data/test/spec_static.rb
CHANGED
@@ -11,9 +11,11 @@ describe Rack::Static do
|
|
11
11
|
root = File.expand_path(File.dirname(__FILE__))
|
12
12
|
|
13
13
|
OPTIONS = {:urls => ["/cgi"], :root => root}
|
14
|
+
STATIC_OPTIONS = {:urls => [""], :root => root, :index => 'static/index.html'}
|
14
15
|
HASH_OPTIONS = {:urls => {"/cgi/sekret" => 'cgi/test'}, :root => root}
|
15
16
|
|
16
17
|
@request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS))
|
18
|
+
@static_request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, STATIC_OPTIONS))
|
17
19
|
@hash_request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, HASH_OPTIONS))
|
18
20
|
|
19
21
|
it "serves files" do
|
@@ -33,6 +35,12 @@ describe Rack::Static do
|
|
33
35
|
res.body.should == "Hello World"
|
34
36
|
end
|
35
37
|
|
38
|
+
it "calls index file when requesting root" do
|
39
|
+
res = @static_request.get("/")
|
40
|
+
res.should.be.ok
|
41
|
+
res.body.should =~ /index!/
|
42
|
+
end
|
43
|
+
|
36
44
|
it "serves hidden files" do
|
37
45
|
res = @hash_request.get("/cgi/sekret")
|
38
46
|
res.should.be.ok
|
data/test/spec_thin.rb
CHANGED
@@ -11,7 +11,7 @@ describe Rack::Handler::Thin do
|
|
11
11
|
Thin::Logging.silent = true
|
12
12
|
|
13
13
|
@thread = Thread.new do
|
14
|
-
Rack::Handler::Thin.run(@app, :Host => @host='
|
14
|
+
Rack::Handler::Thin.run(@app, :Host => @host='127.0.0.1', :Port => @port=9204) do |server|
|
15
15
|
@server = server
|
16
16
|
end
|
17
17
|
end
|
@@ -30,7 +30,7 @@ describe Rack::Handler::Thin do
|
|
30
30
|
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
31
31
|
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
32
32
|
response["SERVER_PORT"].should.equal "9204"
|
33
|
-
response["SERVER_NAME"].should.equal "
|
33
|
+
response["SERVER_NAME"].should.equal "127.0.0.1"
|
34
34
|
end
|
35
35
|
|
36
36
|
should "have rack headers" do
|
data/test/spec_utils.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
require 'rack/utils'
|
3
3
|
require 'rack/mock'
|
4
|
-
require 'timeout'
|
5
4
|
|
6
5
|
describe Rack::Utils do
|
7
6
|
def kcodeu
|
@@ -12,6 +11,16 @@ describe Rack::Utils do
|
|
12
11
|
$KCODE = default_kcode if one8
|
13
12
|
end
|
14
13
|
|
14
|
+
should "round trip binary data" do
|
15
|
+
r = [218, 0].pack 'CC'
|
16
|
+
if defined?(::Encoding)
|
17
|
+
z = Rack::Utils.unescape(Rack::Utils.escape(r), Encoding::BINARY)
|
18
|
+
else
|
19
|
+
z = Rack::Utils.unescape(Rack::Utils.escape(r))
|
20
|
+
end
|
21
|
+
r.should.equal z
|
22
|
+
end
|
23
|
+
|
15
24
|
should "escape correctly" do
|
16
25
|
Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
|
17
26
|
Rack::Utils.escape("a space").should.equal "a+space"
|
@@ -43,7 +52,7 @@ describe Rack::Utils do
|
|
43
52
|
should "unescape multibyte characters correctly if $KCODE is set to 'U'" do
|
44
53
|
kcodeu do
|
45
54
|
Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
|
46
|
-
|
55
|
+
"\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
|
47
56
|
end
|
48
57
|
end
|
49
58
|
end
|
@@ -94,14 +103,6 @@ describe Rack::Utils do
|
|
94
103
|
Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
|
95
104
|
should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
|
96
105
|
Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
|
97
|
-
Rack::Utils.parse_query("=").should.equal "" => ""
|
98
|
-
Rack::Utils.parse_query("=value").should.equal "" => "value"
|
99
|
-
Rack::Utils.parse_query("key=").should.equal "key" => ""
|
100
|
-
Rack::Utils.parse_query("&key&").should.equal "key" => nil
|
101
|
-
Rack::Utils.parse_query(";key;", ";,").should.equal "key" => nil
|
102
|
-
Rack::Utils.parse_query(",key,", ";,").should.equal "key" => nil
|
103
|
-
Rack::Utils.parse_query(";foo=bar,;", ";,").should.equal "foo" => "bar"
|
104
|
-
Rack::Utils.parse_query(",foo=bar;,", ";,").should.equal "foo" => "bar"
|
105
106
|
end
|
106
107
|
|
107
108
|
should "parse nested query strings correctly" do
|
@@ -216,22 +217,22 @@ describe Rack::Utils do
|
|
216
217
|
# unordered hash. Test that build_nested_query performs the inverse
|
217
218
|
# function of parse_nested_query.
|
218
219
|
[{"foo" => nil, "bar" => ""},
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
220
|
+
{"foo" => "bar", "baz" => ""},
|
221
|
+
{"foo" => ["1", "2"]},
|
222
|
+
{"foo" => "bar", "baz" => ["1", "2", "3"]},
|
223
|
+
{"foo" => ["bar"], "baz" => ["1", "2", "3"]},
|
224
|
+
{"foo" => ["1", "2"]},
|
225
|
+
{"foo" => "bar", "baz" => ["1", "2", "3"]},
|
226
|
+
{"x" => {"y" => {"z" => "1"}}},
|
227
|
+
{"x" => {"y" => {"z" => ["1"]}}},
|
228
|
+
{"x" => {"y" => {"z" => ["1", "2"]}}},
|
229
|
+
{"x" => {"y" => [{"z" => "1"}]}},
|
230
|
+
{"x" => {"y" => [{"z" => ["1"]}]}},
|
231
|
+
{"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
|
232
|
+
{"x" => {"y" => [{"v" => {"w" => "1"}}]}},
|
233
|
+
{"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
|
234
|
+
{"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
|
235
|
+
{"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
|
235
236
|
].each { |params|
|
236
237
|
qs = Rack::Utils.build_nested_query(params)
|
237
238
|
Rack::Utils.parse_nested_query(qs).should.equal params
|
@@ -242,6 +243,17 @@ describe Rack::Utils do
|
|
242
243
|
message.should.equal "value must be a Hash"
|
243
244
|
end
|
244
245
|
|
246
|
+
should "parse query strings that have a non-existent value" do
|
247
|
+
key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
|
248
|
+
Rack::Utils.parse_query(key).should.equal Rack::Utils.unescape(key) => nil
|
249
|
+
end
|
250
|
+
|
251
|
+
should "build query strings without = with non-existent values" do
|
252
|
+
key = "post/2011/08/27/Deux-%22rat%C3%A9s%22-de-l-Universit"
|
253
|
+
key = Rack::Utils.unescape(key)
|
254
|
+
Rack::Utils.build_query(key => nil).should.equal Rack::Utils.escape(key)
|
255
|
+
end
|
256
|
+
|
245
257
|
should "escape html entities [&><'\"/]" do
|
246
258
|
Rack::Utils.escape_html("foo").should.equal "foo"
|
247
259
|
Rack::Utils.escape_html("f&o").should.equal "f&o"
|
@@ -301,11 +313,6 @@ describe Rack::Utils do
|
|
301
313
|
Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
|
302
314
|
end
|
303
315
|
|
304
|
-
should "should perform constant time string comparison" do
|
305
|
-
Rack::Utils.secure_compare('a', 'a').should.equal true
|
306
|
-
Rack::Utils.secure_compare('a', 'b').should.equal false
|
307
|
-
end
|
308
|
-
|
309
316
|
should "return status code for integer" do
|
310
317
|
Rack::Utils.status_code(200).should.equal 200
|
311
318
|
end
|
@@ -340,10 +347,6 @@ describe Rack::Utils, "byte_range" do
|
|
340
347
|
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)]
|
341
348
|
end
|
342
349
|
|
343
|
-
should "parse several byte ranges" do
|
344
|
-
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-600,601-999"},1000).should.equal [(500..600),(601..999)]
|
345
|
-
end
|
346
|
-
|
347
350
|
should "truncate byte ranges" do
|
348
351
|
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)]
|
349
352
|
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal []
|