rack 1.1.6 → 1.2.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/README +9 -205
- data/SPEC +3 -3
- data/lib/rack.rb +1 -24
- data/lib/rack/auth/abstract/request.rb +1 -5
- data/lib/rack/auth/digest/md5.rb +1 -2
- data/lib/rack/auth/digest/params.rb +1 -1
- data/lib/rack/content_length.rb +1 -1
- data/lib/rack/etag.rb +15 -6
- data/lib/rack/file.rb +3 -1
- data/lib/rack/handler/cgi.rb +8 -7
- data/lib/rack/handler/fastcgi.rb +1 -1
- data/lib/rack/handler/lsws.rb +1 -1
- data/lib/rack/handler/mongrel.rb +1 -1
- data/lib/rack/handler/scgi.rb +1 -4
- data/lib/rack/handler/webrick.rb +10 -6
- data/lib/rack/lint.rb +29 -37
- data/lib/rack/mime.rb +2 -0
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/recursive.rb +4 -0
- data/lib/rack/request.rb +8 -6
- data/lib/rack/response.rb +1 -0
- data/lib/rack/rewindable_input.rb +13 -10
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +68 -9
- data/lib/rack/session/cookie.rb +1 -10
- data/lib/rack/session/memcache.rb +1 -1
- data/lib/rack/urlmap.rb +6 -7
- data/lib/rack/utils.rb +40 -71
- data/rack.gemspec +7 -11
- data/spec/cgi/lighttpd.conf +25 -0
- data/spec/cgi/rackup_stub.rb +6 -0
- data/spec/cgi/sample_rackup.ru +5 -0
- data/spec/cgi/test +9 -0
- data/spec/cgi/test.fcgi +8 -0
- data/spec/cgi/test.ru +5 -0
- data/spec/multipart/bad_robots +259 -0
- data/spec/multipart/binary +0 -0
- data/spec/multipart/empty +10 -0
- data/spec/multipart/fail_16384_nofile +814 -0
- data/spec/multipart/file1.txt +1 -0
- data/spec/multipart/filename_and_modification_param +7 -0
- data/spec/multipart/filename_with_escaped_quotes +6 -0
- data/spec/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/spec/multipart/filename_with_percent_escaped_quotes +6 -0
- data/spec/multipart/filename_with_unescaped_quotes +6 -0
- data/spec/multipart/ie +6 -0
- data/spec/multipart/nested +10 -0
- data/spec/multipart/none +9 -0
- data/spec/multipart/semicolon +6 -0
- data/spec/multipart/text +10 -0
- data/spec/rackup/config.ru +31 -0
- data/{test/spec_rack_auth_basic.rb → spec/spec_auth_basic.rb} +11 -14
- data/{test/spec_rack_auth_digest.rb → spec/spec_auth_digest.rb} +18 -27
- data/{test/spec_rack_builder.rb → spec/spec_builder.rb} +49 -10
- data/{test/spec_rack_cascade.rb → spec/spec_cascade.rb} +7 -10
- data/{test/spec_rack_cgi.rb → spec/spec_cgi.rb} +34 -32
- data/{test/spec_rack_chunked.rb → spec/spec_chunked.rb} +8 -10
- data/{test/spec_rack_commonlogger.rb → spec/spec_commonlogger.rb} +10 -15
- data/{test/spec_rack_conditionalget.rb → spec/spec_conditionalget.rb} +5 -7
- data/{test/spec_rack_config.rb → spec/spec_config.rb} +6 -7
- data/{test/spec_rack_content_length.rb → spec/spec_content_length.rb} +7 -8
- data/{test/spec_rack_content_type.rb → spec/spec_content_type.rb} +5 -6
- data/{test/spec_rack_deflater.rb → spec/spec_deflater.rb} +11 -13
- data/{test/spec_rack_directory.rb → spec/spec_directory.rb} +6 -10
- data/{test/spec_rack_etag.rb → spec/spec_etag.rb} +3 -5
- data/{test/spec_rack_fastcgi.rb → spec/spec_fastcgi.rb} +36 -29
- data/{test/spec_rack_file.rb → spec/spec_file.rb} +9 -13
- data/{test/spec_rack_handler.rb → spec/spec_handler.rb} +10 -12
- data/{test/spec_rack_head.rb → spec/spec_head.rb} +3 -3
- data/{test/spec_rack_lint.rb → spec/spec_lint.rb} +19 -32
- data/{test/spec_rack_lobster.rb → spec/spec_lobster.rb} +9 -11
- data/{test/spec_rack_lock.rb → spec/spec_lock.rb} +15 -17
- data/{test/spec_rack_logger.rb → spec/spec_logger.rb} +6 -7
- data/{test/spec_rack_methodoverride.rb → spec/spec_methodoverride.rb} +15 -17
- data/{test/spec_rack_mock.rb → spec/spec_mock.rb} +30 -32
- data/{test/spec_rack_mongrel.rb → spec/spec_mongrel.rb} +40 -46
- data/{test/spec_rack_nulllogger.rb → spec/spec_nulllogger.rb} +4 -5
- data/{test/spec_rack_recursive.rb → spec/spec_recursive.rb} +28 -36
- data/{test/spec_rack_request.rb → spec/spec_request.rb} +84 -98
- data/{test/spec_rack_response.rb → spec/spec_response.rb} +46 -27
- data/spec/spec_rewindable_input.rb +118 -0
- data/{test/spec_rack_runtime.rb → spec/spec_runtime.rb} +15 -11
- data/{test/spec_rack_sendfile.rb → spec/spec_sendfile.rb} +11 -14
- data/{test/spec_rack_session_cookie.rb → spec/spec_session_cookie.rb} +14 -36
- data/{test/spec_rack_session_memcache.rb → spec/spec_session_memcache.rb} +32 -26
- data/{test/spec_rack_session_pool.rb → spec/spec_session_pool.rb} +36 -31
- data/spec/spec_showexceptions.rb +23 -0
- data/spec/spec_showstatus.rb +79 -0
- data/{test/spec_rack_static.rb → spec/spec_static.rb} +5 -9
- data/{test/spec_rack_thin.rb → spec/spec_thin.rb} +30 -35
- data/{test/spec_rack_urlmap.rb → spec/spec_urlmap.rb} +6 -8
- data/{test/spec_rack_utils.rb → spec/spec_utils.rb} +134 -74
- data/{test/spec_rack_webrick.rb → spec/spec_webrick.rb} +28 -36
- data/spec/testrequest.rb +77 -0
- data/spec/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/spec/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +176 -191
- data/RDOX +0 -0
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_auth.rb +0 -57
- data/test/spec_rack_camping.rb +0 -55
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rackup.rb +0 -164
@@ -1,34 +1,31 @@
|
|
1
|
-
require 'test/spec'
|
2
|
-
|
3
1
|
begin
|
4
2
|
require 'rack/handler/thin'
|
5
|
-
require 'testrequest'
|
3
|
+
require File.expand_path('../testrequest', __FILE__)
|
6
4
|
require 'timeout'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
5
|
+
|
6
|
+
describe Rack::Handler::Thin do
|
7
|
+
extend TestRequest::Helpers
|
8
|
+
|
9
|
+
@app = Rack::Lint.new(TestRequest.new)
|
10
|
+
@server = nil
|
11
|
+
Thin::Logging.silent = true
|
12
|
+
|
13
|
+
@thread = Thread.new do
|
14
|
+
Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server|
|
15
|
+
@server = server
|
19
16
|
end
|
20
|
-
Thread.pass until @server && @server.running?
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
Thread.pass until @server && @server.running?
|
20
|
+
|
21
|
+
should "respond" do
|
22
|
+
GET("/")
|
23
|
+
response.should.not.be.nil
|
27
24
|
end
|
28
25
|
|
29
|
-
|
26
|
+
should "be a Thin" do
|
30
27
|
GET("/")
|
31
|
-
status.should.
|
28
|
+
status.should.equal 200
|
32
29
|
response["SERVER_SOFTWARE"].should =~ /thin/
|
33
30
|
response["HTTP_VERSION"].should.equal "HTTP/1.1"
|
34
31
|
response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
|
@@ -36,15 +33,15 @@ context "Rack::Handler::Thin" do
|
|
36
33
|
response["SERVER_NAME"].should.equal "0.0.0.0"
|
37
34
|
end
|
38
35
|
|
39
|
-
|
36
|
+
should "have rack headers" do
|
40
37
|
GET("/")
|
41
|
-
response["rack.version"].should.equal [0
|
42
|
-
response["rack.multithread"].should.
|
43
|
-
response["rack.multiprocess"].should.
|
44
|
-
response["rack.run_once"].should.
|
38
|
+
response["rack.version"].should.equal [1,0]
|
39
|
+
response["rack.multithread"].should.equal false
|
40
|
+
response["rack.multiprocess"].should.equal false
|
41
|
+
response["rack.run_once"].should.equal false
|
45
42
|
end
|
46
43
|
|
47
|
-
|
44
|
+
should "have CGI headers on GET" do
|
48
45
|
GET("/")
|
49
46
|
response["REQUEST_METHOD"].should.equal "GET"
|
50
47
|
response["REQUEST_PATH"].should.equal "/"
|
@@ -59,7 +56,7 @@ context "Rack::Handler::Thin" do
|
|
59
56
|
response["QUERY_STRING"].should.equal "quux=1"
|
60
57
|
end
|
61
58
|
|
62
|
-
|
59
|
+
should "have CGI headers on POST" do
|
63
60
|
POST("/", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
|
64
61
|
status.should.equal 200
|
65
62
|
response["REQUEST_METHOD"].should.equal "POST"
|
@@ -69,21 +66,19 @@ context "Rack::Handler::Thin" do
|
|
69
66
|
response["test.postdata"].should.equal "rack-form-data=23"
|
70
67
|
end
|
71
68
|
|
72
|
-
|
69
|
+
should "support HTTP auth" do
|
73
70
|
GET("/test", {:user => "ruth", :passwd => "secret"})
|
74
71
|
response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
|
75
72
|
end
|
76
73
|
|
77
|
-
|
74
|
+
should "set status" do
|
78
75
|
GET("/test?secret")
|
79
76
|
status.should.equal 403
|
80
77
|
response["rack.url_scheme"].should.equal "http"
|
81
78
|
end
|
82
79
|
|
83
|
-
|
84
|
-
|
85
|
-
@thread.kill
|
86
|
-
end
|
80
|
+
@server.stop!
|
81
|
+
@thread.kill
|
87
82
|
end
|
88
83
|
|
89
84
|
rescue LoadError
|
@@ -1,10 +1,8 @@
|
|
1
|
-
require 'test/spec'
|
2
|
-
|
3
1
|
require 'rack/urlmap'
|
4
2
|
require 'rack/mock'
|
5
3
|
|
6
|
-
|
7
|
-
|
4
|
+
describe Rack::URLMap do
|
5
|
+
it "dispatches paths correctly" do
|
8
6
|
app = lambda { |env|
|
9
7
|
[200, {
|
10
8
|
'X-ScriptName' => env['SCRIPT_NAME'],
|
@@ -67,7 +65,7 @@ context "Rack::URLMap" do
|
|
67
65
|
end
|
68
66
|
|
69
67
|
|
70
|
-
|
68
|
+
it "dispatches hosts correctly" do
|
71
69
|
map = Rack::URLMap.new("http://foo.org/" => lambda { |env|
|
72
70
|
[200,
|
73
71
|
{ "Content-Type" => "text/plain",
|
@@ -125,7 +123,7 @@ context "Rack::URLMap" do
|
|
125
123
|
res["X-Position"].should.equal "default.org"
|
126
124
|
end
|
127
125
|
|
128
|
-
|
126
|
+
should "be nestable" do
|
129
127
|
map = Rack::URLMap.new("/foo" =>
|
130
128
|
Rack::URLMap.new("/bar" =>
|
131
129
|
Rack::URLMap.new("/quux" => lambda { |env|
|
@@ -147,7 +145,7 @@ context "Rack::URLMap" do
|
|
147
145
|
res["X-ScriptName"].should.equal "/foo/bar/quux"
|
148
146
|
end
|
149
147
|
|
150
|
-
|
148
|
+
should "route root apps correctly" do
|
151
149
|
map = Rack::URLMap.new("/" => lambda { |env|
|
152
150
|
[200,
|
153
151
|
{ "Content-Type" => "text/plain",
|
@@ -189,7 +187,7 @@ context "Rack::URLMap" do
|
|
189
187
|
res["X-ScriptName"].should.equal ""
|
190
188
|
end
|
191
189
|
|
192
|
-
|
190
|
+
should "not squeeze slashes" do
|
193
191
|
map = Rack::URLMap.new("/" => lambda { |env|
|
194
192
|
[200,
|
195
193
|
{ "Content-Type" => "text/plain",
|
@@ -1,18 +1,15 @@
|
|
1
|
-
require 'test/spec'
|
2
|
-
|
3
1
|
require 'rack/utils'
|
4
|
-
require 'rack/lint'
|
5
2
|
require 'rack/mock'
|
6
3
|
|
7
|
-
|
8
|
-
|
4
|
+
describe Rack::Utils do
|
5
|
+
should "escape correctly" do
|
9
6
|
Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
|
10
7
|
Rack::Utils.escape("a space").should.equal "a+space"
|
11
8
|
Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
|
12
9
|
should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
|
13
10
|
end
|
14
11
|
|
15
|
-
|
12
|
+
should "escape correctly for multibyte characters" do
|
16
13
|
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
|
17
14
|
matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
|
18
15
|
Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
|
@@ -21,7 +18,7 @@ context "Rack::Utils" do
|
|
21
18
|
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
22
19
|
end
|
23
20
|
|
24
|
-
|
21
|
+
should "unescape correctly" do
|
25
22
|
Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
|
26
23
|
Rack::Utils.unescape("a+space").should.equal "a space"
|
27
24
|
Rack::Utils.unescape("a%20space").should.equal "a space"
|
@@ -29,7 +26,7 @@ context "Rack::Utils" do
|
|
29
26
|
should.equal "q1!2\"'w$5&7/z8)?\\"
|
30
27
|
end
|
31
28
|
|
32
|
-
|
29
|
+
should "parse query strings correctly" do
|
33
30
|
Rack::Utils.parse_query("foo=bar").
|
34
31
|
should.equal "foo" => "bar"
|
35
32
|
Rack::Utils.parse_query("foo=\"bar\"").
|
@@ -43,7 +40,7 @@ context "Rack::Utils" do
|
|
43
40
|
Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
|
44
41
|
end
|
45
42
|
|
46
|
-
|
43
|
+
should "parse nested query strings correctly" do
|
47
44
|
Rack::Utils.parse_nested_query("foo").
|
48
45
|
should.equal "foo" => nil
|
49
46
|
Rack::Utils.parse_nested_query("foo=").
|
@@ -121,7 +118,7 @@ context "Rack::Utils" do
|
|
121
118
|
message.should.equal "expected Array (got String) for param `y'"
|
122
119
|
end
|
123
120
|
|
124
|
-
|
121
|
+
should "build query strings correctly" do
|
125
122
|
Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
|
126
123
|
Rack::Utils.build_query("foo" => ["bar", "quux"]).
|
127
124
|
should.equal "foo=bar&foo=quux"
|
@@ -131,7 +128,7 @@ context "Rack::Utils" do
|
|
131
128
|
should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
|
132
129
|
end
|
133
130
|
|
134
|
-
|
131
|
+
should "build nested query strings correctly" do
|
135
132
|
Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
|
136
133
|
Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
|
137
134
|
Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
|
@@ -178,7 +175,7 @@ context "Rack::Utils" do
|
|
178
175
|
message.should.equal "value must be a Hash"
|
179
176
|
end
|
180
177
|
|
181
|
-
|
178
|
+
should "figure out which encodings are acceptable" do
|
182
179
|
helper = lambda do |a, b|
|
183
180
|
request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
|
184
181
|
Rack::Utils.select_best_encoding(a, b)
|
@@ -201,48 +198,43 @@ context "Rack::Utils" do
|
|
201
198
|
helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity")
|
202
199
|
end
|
203
200
|
|
204
|
-
|
201
|
+
should "return the bytesize of String" do
|
205
202
|
Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
|
206
203
|
end
|
207
204
|
|
208
|
-
|
209
|
-
Rack::Utils.secure_compare('a', 'a').should.equal true
|
210
|
-
Rack::Utils.secure_compare('a', 'b').should.equal false
|
211
|
-
end
|
212
|
-
|
213
|
-
specify "should return status code for integer" do
|
205
|
+
should "return status code for integer" do
|
214
206
|
Rack::Utils.status_code(200).should.equal 200
|
215
207
|
end
|
216
208
|
|
217
|
-
|
209
|
+
should "return status code for string" do
|
218
210
|
Rack::Utils.status_code("200").should.equal 200
|
219
211
|
end
|
220
212
|
|
221
|
-
|
213
|
+
should "return status code for symbol" do
|
222
214
|
Rack::Utils.status_code(:ok).should.equal 200
|
223
215
|
end
|
224
216
|
end
|
225
217
|
|
226
|
-
|
227
|
-
|
218
|
+
describe Rack::Utils::HeaderHash do
|
219
|
+
should "retain header case" do
|
228
220
|
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
229
221
|
h['ETag'] = 'Boo!'
|
230
222
|
h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
|
231
223
|
end
|
232
224
|
|
233
|
-
|
225
|
+
should "check existence of keys case insensitively" do
|
234
226
|
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
235
227
|
h.should.include 'content-md5'
|
236
228
|
h.should.not.include 'ETag'
|
237
229
|
end
|
238
230
|
|
239
|
-
|
231
|
+
should "merge case-insensitively" do
|
240
232
|
h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
|
241
233
|
merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
|
242
234
|
merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
|
243
235
|
end
|
244
236
|
|
245
|
-
|
237
|
+
should "overwrite case insensitively and assume the new key's case" do
|
246
238
|
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
|
247
239
|
h["foo-bar"] = "bizzle"
|
248
240
|
h["FOO-BAR"].should.equal "bizzle"
|
@@ -250,55 +242,55 @@ context "Rack::Utils::HeaderHash" do
|
|
250
242
|
h.to_hash.should.equal "foo-bar" => "bizzle"
|
251
243
|
end
|
252
244
|
|
253
|
-
|
245
|
+
should "be converted to real Hash" do
|
254
246
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
255
247
|
h.to_hash.should.be.instance_of Hash
|
256
248
|
end
|
257
249
|
|
258
|
-
|
250
|
+
should "convert Array values to Strings when converting to Hash" do
|
259
251
|
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
|
260
252
|
h.to_hash.should.equal({ "foo" => "bar\nbaz" })
|
261
253
|
end
|
262
254
|
|
263
|
-
|
255
|
+
should "replace hashes correctly" do
|
264
256
|
h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
|
265
257
|
j = {"foo" => "bar"}
|
266
258
|
h.replace(j)
|
267
259
|
h["foo"].should.equal "bar"
|
268
260
|
end
|
269
261
|
|
270
|
-
|
262
|
+
should "be able to delete the given key case-sensitively" do
|
271
263
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
272
264
|
h.delete("foo")
|
273
265
|
h["foo"].should.be.nil
|
274
266
|
h["FOO"].should.be.nil
|
275
267
|
end
|
276
268
|
|
277
|
-
|
269
|
+
should "be able to delete the given key case-insensitively" do
|
278
270
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
279
271
|
h.delete("FOO")
|
280
272
|
h["foo"].should.be.nil
|
281
273
|
h["FOO"].should.be.nil
|
282
274
|
end
|
283
275
|
|
284
|
-
|
276
|
+
should "return the deleted value when #delete is called on an existing key" do
|
285
277
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
286
278
|
h.delete("Foo").should.equal("bar")
|
287
279
|
end
|
288
280
|
|
289
|
-
|
281
|
+
should "return nil when #delete is called on a non-existant key" do
|
290
282
|
h = Rack::Utils::HeaderHash.new("foo" => "bar")
|
291
283
|
h.delete("Hello").should.be.nil
|
292
284
|
end
|
293
285
|
|
294
|
-
|
286
|
+
should "avoid unnecessary object creation if possible" do
|
295
287
|
a = Rack::Utils::HeaderHash.new("foo" => "bar")
|
296
288
|
b = Rack::Utils::HeaderHash.new(a)
|
297
289
|
b.object_id.should.equal(a.object_id)
|
298
290
|
b.should.equal(a)
|
299
291
|
end
|
300
292
|
|
301
|
-
|
293
|
+
should "convert Array values to Strings when responding to #each" do
|
302
294
|
h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
|
303
295
|
h.each do |k,v|
|
304
296
|
k.should.equal("foo")
|
@@ -308,7 +300,7 @@ context "Rack::Utils::HeaderHash" do
|
|
308
300
|
|
309
301
|
end
|
310
302
|
|
311
|
-
|
303
|
+
describe Rack::Utils::Context do
|
312
304
|
class ContextTest
|
313
305
|
attr_reader :app
|
314
306
|
def initialize app; @app=app; end
|
@@ -321,7 +313,7 @@ context "Rack::Utils::Context" do
|
|
321
313
|
test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
|
322
314
|
test_app = ContextTest.new test_target4
|
323
315
|
|
324
|
-
|
316
|
+
should "set context correctly" do
|
325
317
|
test_app.app.should.equal test_target4
|
326
318
|
c1 = Rack::Utils::Context.new(test_app, test_target1)
|
327
319
|
c1.for.should.equal test_app
|
@@ -331,7 +323,7 @@ context "Rack::Utils::Context" do
|
|
331
323
|
c2.app.should.equal test_target2
|
332
324
|
end
|
333
325
|
|
334
|
-
|
326
|
+
should "alter app on recontexting" do
|
335
327
|
c1 = Rack::Utils::Context.new(test_app, test_target1)
|
336
328
|
c2 = c1.recontext(test_target2)
|
337
329
|
c2.for.should.equal test_app
|
@@ -341,7 +333,7 @@ context "Rack::Utils::Context" do
|
|
341
333
|
c3.app.should.equal test_target3
|
342
334
|
end
|
343
335
|
|
344
|
-
|
336
|
+
should "run different apps" do
|
345
337
|
c1 = Rack::Utils::Context.new test_app, test_target1
|
346
338
|
c2 = c1.recontext test_target2
|
347
339
|
c3 = c2.recontext test_target3
|
@@ -355,21 +347,37 @@ context "Rack::Utils::Context" do
|
|
355
347
|
r3 = c3.call(:misc_symbol)
|
356
348
|
r3.should.be.nil
|
357
349
|
r4 = Rack::MockRequest.new(a4).get('/')
|
358
|
-
r4.status.should.
|
350
|
+
r4.status.should.equal 200
|
359
351
|
r5 = Rack::MockRequest.new(a5).get('/')
|
360
|
-
r5.status.should.
|
352
|
+
r5.status.should.equal 200
|
361
353
|
r4.body.should.equal r5.body
|
362
354
|
end
|
363
355
|
end
|
364
356
|
|
365
|
-
|
366
|
-
|
357
|
+
describe Rack::Utils::Multipart do
|
358
|
+
def multipart_fixture(name)
|
359
|
+
file = multipart_file(name)
|
360
|
+
data = File.open(file, 'rb') { |io| io.read }
|
361
|
+
|
362
|
+
type = "multipart/form-data; boundary=AaB03x"
|
363
|
+
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
364
|
+
|
365
|
+
{ "CONTENT_TYPE" => type,
|
366
|
+
"CONTENT_LENGTH" => length.to_s,
|
367
|
+
:input => StringIO.new(data) }
|
368
|
+
end
|
369
|
+
|
370
|
+
def multipart_file(name)
|
371
|
+
File.join(File.dirname(__FILE__), "multipart", name.to_s)
|
372
|
+
end
|
373
|
+
|
374
|
+
should "return nil if content type is not multipart" do
|
367
375
|
env = Rack::MockRequest.env_for("/",
|
368
376
|
"CONTENT_TYPE" => 'application/x-www-form-urlencoded')
|
369
377
|
Rack::Utils::Multipart.parse_multipart(env).should.equal nil
|
370
378
|
end
|
371
379
|
|
372
|
-
|
380
|
+
should "parse multipart upload with text file" do
|
373
381
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
374
382
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
375
383
|
params["submit-name"].should.equal "Larry"
|
@@ -382,7 +390,7 @@ context "Rack::Utils::Multipart" do
|
|
382
390
|
params["files"][:tempfile].read.should.equal "contents"
|
383
391
|
end
|
384
392
|
|
385
|
-
|
393
|
+
should "parse multipart upload with nested parameters" do
|
386
394
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
|
387
395
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
388
396
|
params["foo"]["submit-name"].should.equal "Larry"
|
@@ -395,7 +403,7 @@ context "Rack::Utils::Multipart" do
|
|
395
403
|
params["foo"]["files"][:tempfile].read.should.equal "contents"
|
396
404
|
end
|
397
405
|
|
398
|
-
|
406
|
+
should "parse multipart upload with binary file" do
|
399
407
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
|
400
408
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
401
409
|
params["submit-name"].should.equal "Larry"
|
@@ -408,7 +416,7 @@ context "Rack::Utils::Multipart" do
|
|
408
416
|
params["files"][:tempfile].read.length.should.equal 26473
|
409
417
|
end
|
410
418
|
|
411
|
-
|
419
|
+
should "parse multipart upload with empty file" do
|
412
420
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
|
413
421
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
414
422
|
params["submit-name"].should.equal "Larry"
|
@@ -421,7 +429,7 @@ context "Rack::Utils::Multipart" do
|
|
421
429
|
params["files"][:tempfile].read.should.equal ""
|
422
430
|
end
|
423
431
|
|
424
|
-
|
432
|
+
should "parse multipart upload with filename with semicolons" do
|
425
433
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
|
426
434
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
427
435
|
params["files"][:type].should.equal "text/plain"
|
@@ -433,7 +441,7 @@ context "Rack::Utils::Multipart" do
|
|
433
441
|
params["files"][:tempfile].read.should.equal "contents"
|
434
442
|
end
|
435
443
|
|
436
|
-
|
444
|
+
should "not include file params if no file was selected" do
|
437
445
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
438
446
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
439
447
|
params["submit-name"].should.equal "Larry"
|
@@ -441,7 +449,7 @@ context "Rack::Utils::Multipart" do
|
|
441
449
|
params.keys.should.not.include "files"
|
442
450
|
end
|
443
451
|
|
444
|
-
|
452
|
+
should "parse IE multipart upload and clean up filename" do
|
445
453
|
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
|
446
454
|
params = Rack::Utils::Multipart.parse_multipart(env)
|
447
455
|
params["files"][:type].should.equal "text/plain"
|
@@ -454,7 +462,76 @@ context "Rack::Utils::Multipart" do
|
|
454
462
|
params["files"][:tempfile].read.should.equal "contents"
|
455
463
|
end
|
456
464
|
|
457
|
-
|
465
|
+
should "parse filename and modification param" do
|
466
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
|
467
|
+
params = Rack::Utils::Multipart.parse_multipart(env)
|
468
|
+
params["files"][:type].should.equal "image/jpeg"
|
469
|
+
params["files"][:filename].should.equal "genome.jpeg"
|
470
|
+
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
|
471
|
+
"Content-Disposition: attachment; " +
|
472
|
+
"name=\"files\"; " +
|
473
|
+
"filename=genome.jpeg; " +
|
474
|
+
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
475
|
+
"Content-Description: a complete map of the human genome\r\n"
|
476
|
+
params["files"][:name].should.equal "files"
|
477
|
+
params["files"][:tempfile].read.should.equal "contents"
|
478
|
+
end
|
479
|
+
|
480
|
+
should "parse filename with escaped quotes" do
|
481
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
|
482
|
+
params = Rack::Utils::Multipart.parse_multipart(env)
|
483
|
+
params["files"][:type].should.equal "application/octet-stream"
|
484
|
+
params["files"][:filename].should.equal "escape \"quotes"
|
485
|
+
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
486
|
+
"name=\"files\"; " +
|
487
|
+
"filename=\"escape \\\"quotes\"\r\n" +
|
488
|
+
"Content-Type: application/octet-stream\r\n"
|
489
|
+
params["files"][:name].should.equal "files"
|
490
|
+
params["files"][:tempfile].read.should.equal "contents"
|
491
|
+
end
|
492
|
+
|
493
|
+
should "parse filename with percent escaped quotes" do
|
494
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
|
495
|
+
params = Rack::Utils::Multipart.parse_multipart(env)
|
496
|
+
params["files"][:type].should.equal "application/octet-stream"
|
497
|
+
params["files"][:filename].should.equal "escape \"quotes"
|
498
|
+
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
499
|
+
"name=\"files\"; " +
|
500
|
+
"filename=\"escape %22quotes\"\r\n" +
|
501
|
+
"Content-Type: application/octet-stream\r\n"
|
502
|
+
params["files"][:name].should.equal "files"
|
503
|
+
params["files"][:tempfile].read.should.equal "contents"
|
504
|
+
end
|
505
|
+
|
506
|
+
should "parse filename with unescaped quotes" do
|
507
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
508
|
+
params = Rack::Utils::Multipart.parse_multipart(env)
|
509
|
+
params["files"][:type].should.equal "application/octet-stream"
|
510
|
+
params["files"][:filename].should.equal "escape \"quotes"
|
511
|
+
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
512
|
+
"name=\"files\"; " +
|
513
|
+
"filename=\"escape \"quotes\"\r\n" +
|
514
|
+
"Content-Type: application/octet-stream\r\n"
|
515
|
+
params["files"][:name].should.equal "files"
|
516
|
+
params["files"][:tempfile].read.should.equal "contents"
|
517
|
+
end
|
518
|
+
|
519
|
+
should "parse filename with escaped quotes and modification param" do
|
520
|
+
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
521
|
+
params = Rack::Utils::Multipart.parse_multipart(env)
|
522
|
+
params["files"][:type].should.equal "image/jpeg"
|
523
|
+
params["files"][:filename].should.equal "\"human\" genome.jpeg"
|
524
|
+
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
|
525
|
+
"Content-Disposition: attachment; " +
|
526
|
+
"name=\"files\"; " +
|
527
|
+
"filename=\"\"human\" genome.jpeg\"; " +
|
528
|
+
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
529
|
+
"Content-Description: a complete map of the human genome\r\n"
|
530
|
+
params["files"][:name].should.equal "files"
|
531
|
+
params["files"][:tempfile].read.should.equal "contents"
|
532
|
+
end
|
533
|
+
|
534
|
+
it "rewinds input after parsing upload" do
|
458
535
|
options = multipart_fixture(:text)
|
459
536
|
input = options[:input]
|
460
537
|
env = Rack::MockRequest.env_for("/", options)
|
@@ -464,7 +541,7 @@ context "Rack::Utils::Multipart" do
|
|
464
541
|
input.read.length.should.equal 197
|
465
542
|
end
|
466
543
|
|
467
|
-
|
544
|
+
it "builds multipart body" do
|
468
545
|
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
469
546
|
data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
|
470
547
|
|
@@ -480,7 +557,7 @@ context "Rack::Utils::Multipart" do
|
|
480
557
|
params["files"][:tempfile].read.should.equal "contents"
|
481
558
|
end
|
482
559
|
|
483
|
-
|
560
|
+
it "builds nested multipart body" do
|
484
561
|
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
485
562
|
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
|
486
563
|
|
@@ -496,7 +573,7 @@ context "Rack::Utils::Multipart" do
|
|
496
573
|
params["people"][0]["files"][:tempfile].read.should.equal "contents"
|
497
574
|
end
|
498
575
|
|
499
|
-
|
576
|
+
it "can parse fields that end at the end of the buffer" do
|
500
577
|
input = File.read(multipart_file("bad_robots"))
|
501
578
|
|
502
579
|
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
@@ -508,7 +585,7 @@ context "Rack::Utils::Multipart" do
|
|
508
585
|
req.POST['addresses'].should.not.equal nil
|
509
586
|
end
|
510
587
|
|
511
|
-
|
588
|
+
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
512
589
|
data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n")
|
513
590
|
options = {
|
514
591
|
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
|
@@ -527,31 +604,14 @@ context "Rack::Utils::Multipart" do
|
|
527
604
|
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
|
528
605
|
end
|
529
606
|
|
530
|
-
|
607
|
+
should "return nil if no UploadedFiles were used" do
|
531
608
|
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
532
609
|
data.should.equal nil
|
533
610
|
end
|
534
611
|
|
535
|
-
|
612
|
+
should "raise ArgumentError if params is not a Hash" do
|
536
613
|
lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
|
537
614
|
should.raise(ArgumentError).
|
538
615
|
message.should.equal "value must be a Hash"
|
539
616
|
end
|
540
|
-
|
541
|
-
private
|
542
|
-
def multipart_fixture(name)
|
543
|
-
file = multipart_file(name)
|
544
|
-
data = File.open(file, 'rb') { |io| io.read }
|
545
|
-
|
546
|
-
type = "multipart/form-data; boundary=AaB03x"
|
547
|
-
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
548
|
-
|
549
|
-
{ "CONTENT_TYPE" => type,
|
550
|
-
"CONTENT_LENGTH" => length.to_s,
|
551
|
-
:input => StringIO.new(data) }
|
552
|
-
end
|
553
|
-
|
554
|
-
def multipart_file(name)
|
555
|
-
File.join(File.dirname(__FILE__), "multipart", name.to_s)
|
556
|
-
end
|
557
617
|
end
|