rack 1.2.8 → 1.3.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.
- data/README +9 -177
- data/Rakefile +2 -1
- data/SPEC +2 -2
- data/lib/rack.rb +2 -13
- data/lib/rack/auth/abstract/request.rb +7 -5
- data/lib/rack/auth/digest/md5.rb +6 -2
- data/lib/rack/auth/digest/params.rb +5 -7
- data/lib/rack/auth/digest/request.rb +1 -1
- data/lib/rack/backports/uri/common.rb +64 -0
- data/lib/rack/builder.rb +60 -3
- data/lib/rack/chunked.rb +29 -22
- data/lib/rack/conditionalget.rb +35 -16
- data/lib/rack/content_length.rb +3 -3
- data/lib/rack/deflater.rb +5 -2
- data/lib/rack/etag.rb +38 -10
- data/lib/rack/file.rb +76 -43
- data/lib/rack/handler.rb +13 -7
- data/lib/rack/handler/cgi.rb +0 -2
- data/lib/rack/handler/fastcgi.rb +13 -4
- data/lib/rack/handler/lsws.rb +0 -2
- data/lib/rack/handler/mongrel.rb +12 -2
- data/lib/rack/handler/scgi.rb +9 -1
- data/lib/rack/handler/thin.rb +7 -1
- data/lib/rack/handler/webrick.rb +12 -5
- data/lib/rack/lint.rb +2 -2
- data/lib/rack/lock.rb +29 -3
- data/lib/rack/methodoverride.rb +1 -1
- data/lib/rack/mime.rb +2 -2
- data/lib/rack/mock.rb +28 -33
- data/lib/rack/multipart.rb +34 -0
- data/lib/rack/multipart/generator.rb +93 -0
- data/lib/rack/multipart/parser.rb +164 -0
- data/lib/rack/multipart/uploaded_file.rb +30 -0
- data/lib/rack/request.rb +55 -19
- data/lib/rack/response.rb +10 -8
- data/lib/rack/sendfile.rb +14 -18
- data/lib/rack/server.rb +55 -8
- data/lib/rack/session/abstract/id.rb +233 -22
- data/lib/rack/session/cookie.rb +99 -46
- data/lib/rack/session/memcache.rb +30 -56
- data/lib/rack/session/pool.rb +22 -43
- data/lib/rack/showexceptions.rb +40 -11
- data/lib/rack/showstatus.rb +9 -2
- data/lib/rack/static.rb +29 -9
- data/lib/rack/urlmap.rb +6 -1
- data/lib/rack/utils.rb +67 -326
- data/rack.gemspec +2 -3
- data/test/builder/anything.rb +5 -0
- data/test/builder/comment.ru +4 -0
- data/test/builder/end.ru +3 -0
- data/test/builder/options.ru +2 -0
- data/test/cgi/lighttpd.conf +1 -1
- data/test/cgi/lighttpd.errors +412 -0
- data/test/multipart/content_type_and_no_filename +6 -0
- data/test/multipart/text +5 -0
- data/test/multipart/webkit +32 -0
- data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
- data/test/spec_auth_digest.rb +20 -5
- data/test/spec_builder.rb +29 -0
- data/test/spec_cgi.rb +11 -0
- data/test/spec_chunked.rb +1 -1
- data/test/spec_commonlogger.rb +1 -1
- data/test/spec_conditionalget.rb +47 -0
- data/test/spec_content_length.rb +0 -6
- data/test/spec_content_type.rb +5 -5
- data/test/spec_deflater.rb +46 -2
- data/test/spec_etag.rb +68 -1
- data/test/spec_fastcgi.rb +11 -0
- data/test/spec_file.rb +54 -3
- data/test/spec_handler.rb +23 -5
- data/test/spec_lint.rb +2 -2
- data/test/spec_lock.rb +111 -5
- data/test/spec_methodoverride.rb +2 -2
- data/test/spec_mock.rb +3 -3
- data/test/spec_mongrel.rb +1 -2
- data/test/spec_multipart.rb +279 -0
- data/test/spec_request.rb +222 -38
- data/test/spec_response.rb +9 -3
- data/test/spec_server.rb +74 -0
- data/test/spec_session_abstract_id.rb +43 -0
- data/test/spec_session_cookie.rb +97 -15
- data/test/spec_session_memcache.rb +60 -50
- data/test/spec_session_pool.rb +63 -40
- data/test/spec_showexceptions.rb +64 -0
- data/test/spec_static.rb +23 -0
- data/test/spec_utils.rb +65 -351
- data/test/spec_webrick.rb +23 -4
- metadata +35 -15
- data/test/spec_auth.rb +0 -57
data/test/spec_showexceptions.rb
CHANGED
@@ -20,4 +20,68 @@ describe Rack::ShowExceptions do
|
|
20
20
|
res.should =~ /RuntimeError/
|
21
21
|
res.should =~ /ShowExceptions/
|
22
22
|
end
|
23
|
+
|
24
|
+
it "responds with plain text on AJAX requests accepting anything but HTML" do
|
25
|
+
res = nil
|
26
|
+
|
27
|
+
req = Rack::MockRequest.new(
|
28
|
+
Rack::ShowExceptions.new(
|
29
|
+
lambda{|env| raise RuntimeError, "It was never supposed to work" }
|
30
|
+
))
|
31
|
+
|
32
|
+
lambda{
|
33
|
+
res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest")
|
34
|
+
}.should.not.raise
|
35
|
+
|
36
|
+
res.should.be.a.server_error
|
37
|
+
res.status.should.equal 500
|
38
|
+
|
39
|
+
res.content_type.should.equal "text/plain"
|
40
|
+
|
41
|
+
res.body.should.include "RuntimeError: It was never supposed to work\n"
|
42
|
+
res.body.should.include __FILE__
|
43
|
+
end
|
44
|
+
|
45
|
+
it "responds with HTML on AJAX requests accepting HTML" do
|
46
|
+
res = nil
|
47
|
+
|
48
|
+
req = Rack::MockRequest.new(
|
49
|
+
Rack::ShowExceptions.new(
|
50
|
+
lambda{|env| raise RuntimeError, "It was never supposed to work" }
|
51
|
+
))
|
52
|
+
|
53
|
+
lambda{
|
54
|
+
res = req.get("/", "HTTP_X_REQUESTED_WITH" => "XMLHttpRequest", "HTTP_ACCEPT" => "text/html")
|
55
|
+
}.should.not.raise
|
56
|
+
|
57
|
+
res.should.be.a.server_error
|
58
|
+
res.status.should.equal 500
|
59
|
+
|
60
|
+
res.content_type.should.equal "text/html"
|
61
|
+
|
62
|
+
res.body.should.include "RuntimeError"
|
63
|
+
res.body.should.include "It was never supposed to work"
|
64
|
+
res.body.should.include Rack::Utils.escape_html(__FILE__)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "handles exceptions without a backtrace" do
|
68
|
+
res = nil
|
69
|
+
|
70
|
+
req = Rack::MockRequest.new(
|
71
|
+
Rack::ShowExceptions.new(
|
72
|
+
lambda{|env| raise RuntimeError, "", [] }
|
73
|
+
)
|
74
|
+
)
|
75
|
+
|
76
|
+
lambda{
|
77
|
+
res = req.get("/")
|
78
|
+
}.should.not.raise
|
79
|
+
|
80
|
+
res.should.be.a.server_error
|
81
|
+
res.status.should.equal 500
|
82
|
+
|
83
|
+
res.should =~ /RuntimeError/
|
84
|
+
res.should =~ /ShowExceptions/
|
85
|
+
res.should =~ /unknown location/
|
86
|
+
end
|
23
87
|
end
|
data/test/spec_static.rb
CHANGED
@@ -9,9 +9,12 @@ end
|
|
9
9
|
|
10
10
|
describe Rack::Static do
|
11
11
|
root = File.expand_path(File.dirname(__FILE__))
|
12
|
+
|
12
13
|
OPTIONS = {:urls => ["/cgi"], :root => root}
|
14
|
+
HASH_OPTIONS = {:urls => {"/cgi/sekret" => 'cgi/test'}, :root => root}
|
13
15
|
|
14
16
|
@request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, OPTIONS))
|
17
|
+
@hash_request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, HASH_OPTIONS))
|
15
18
|
|
16
19
|
it "serves files" do
|
17
20
|
res = @request.get("/cgi/test")
|
@@ -30,4 +33,24 @@ describe Rack::Static do
|
|
30
33
|
res.body.should == "Hello World"
|
31
34
|
end
|
32
35
|
|
36
|
+
it "serves hidden files" do
|
37
|
+
res = @hash_request.get("/cgi/sekret")
|
38
|
+
res.should.be.ok
|
39
|
+
res.body.should =~ /ruby/
|
40
|
+
end
|
41
|
+
|
42
|
+
it "calls down the chain if the URI is not specified" do
|
43
|
+
res = @hash_request.get("/something/else")
|
44
|
+
res.should.be.ok
|
45
|
+
res.body.should == "Hello World"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "supports serving fixed cache-control" do
|
49
|
+
opts = OPTIONS.merge(:cache_control => 'public')
|
50
|
+
request = Rack::MockRequest.new(Rack::Static.new(DummyApp.new, opts))
|
51
|
+
res = request.get("/cgi/test")
|
52
|
+
res.should.be.ok
|
53
|
+
res.headers['Cache-Control'].should == 'public'
|
54
|
+
end
|
55
|
+
|
33
56
|
end
|
data/test/spec_utils.rb
CHANGED
@@ -1,15 +1,9 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
1
3
|
require 'rack/utils'
|
2
4
|
require 'rack/mock'
|
3
5
|
|
4
6
|
describe Rack::Utils do
|
5
|
-
def kcodeu
|
6
|
-
one8 = RUBY_VERSION.to_f < 1.9
|
7
|
-
default_kcode, $KCODE = $KCODE, 'U' if one8
|
8
|
-
yield
|
9
|
-
ensure
|
10
|
-
$KCODE = default_kcode if one8
|
11
|
-
end
|
12
|
-
|
13
7
|
should "escape correctly" do
|
14
8
|
Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
|
15
9
|
Rack::Utils.escape("a space").should.equal "a+space"
|
@@ -26,38 +20,16 @@ describe Rack::Utils do
|
|
26
20
|
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
27
21
|
end
|
28
22
|
|
29
|
-
if RUBY_VERSION[/^\d+\.\d+/] == '1.8'
|
30
|
-
should "escape correctly for multibyte characters if $KCODE is set to 'U'" do
|
31
|
-
kcodeu do
|
32
|
-
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
|
33
|
-
matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
|
34
|
-
Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
|
35
|
-
matz_name_sep = "\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsu moto
|
36
|
-
matz_name_sep.force_encoding("UTF-8") if matz_name_sep.respond_to? :force_encoding
|
37
|
-
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
should "unescape multibyte characters correctly if $KCODE is set to 'U'" do
|
42
|
-
kcodeu do
|
43
|
-
Rack::Utils.unescape('%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8').should.equal(
|
44
|
-
"\xE3\x81\xBE\xE3\x81\xA4 \xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0])
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
should "escape objects that responds to to_s" do
|
50
|
-
kcodeu do
|
51
|
-
Rack::Utils.escape(:id).should.equal "id"
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
23
|
if "".respond_to?(:encode)
|
56
24
|
should "escape non-UTF8 strings" do
|
57
25
|
Rack::Utils.escape("ø".encode("ISO-8859-1")).should.equal "%F8"
|
58
26
|
end
|
59
27
|
end
|
60
28
|
|
29
|
+
should "escape path spaces with %20" do
|
30
|
+
Rack::Utils.escape_path("foo bar").should.equal "foo%20bar"
|
31
|
+
end
|
32
|
+
|
61
33
|
should "unescape correctly" do
|
62
34
|
Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
|
63
35
|
Rack::Utils.unescape("a+space").should.equal "a space"
|
@@ -104,6 +76,9 @@ describe Rack::Utils do
|
|
104
76
|
should.equal "foo" => "bar", "baz" => ""
|
105
77
|
Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
|
106
78
|
should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
|
79
|
+
|
80
|
+
Rack::Utils.parse_nested_query("a=b&pid%3D1234=1023").
|
81
|
+
should.equal "pid=1234" => "1023", "a" => "b"
|
107
82
|
|
108
83
|
Rack::Utils.parse_nested_query("foo[]").
|
109
84
|
should.equal "foo" => [nil]
|
@@ -215,41 +190,20 @@ describe Rack::Utils do
|
|
215
190
|
message.should.equal "value must be a Hash"
|
216
191
|
end
|
217
192
|
|
218
|
-
should "escape html entities [&><'\"/]" do
|
193
|
+
should "should escape html entities [&><'\"/]" do
|
219
194
|
Rack::Utils.escape_html("foo").should.equal "foo"
|
220
195
|
Rack::Utils.escape_html("f&o").should.equal "f&o"
|
221
196
|
Rack::Utils.escape_html("f<o").should.equal "f<o"
|
222
197
|
Rack::Utils.escape_html("f>o").should.equal "f>o"
|
223
|
-
Rack::Utils.escape_html("f'o").should.equal "f&#
|
198
|
+
Rack::Utils.escape_html("f'o").should.equal "f'o"
|
224
199
|
Rack::Utils.escape_html('f"o').should.equal "f"o"
|
225
|
-
Rack::Utils.escape_html("f/o").should.equal "f&#
|
226
|
-
Rack::Utils.escape_html("<foo></foo>").should.equal "<foo><&#
|
227
|
-
end
|
228
|
-
|
229
|
-
should "escape html entities even on MRI when it's bugged" do
|
230
|
-
test_escape = lambda do
|
231
|
-
kcodeu do
|
232
|
-
Rack::Utils.escape_html("\300<").should.equal "\300<"
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
if RUBY_VERSION.to_f < 1.9
|
237
|
-
test_escape.call
|
238
|
-
else
|
239
|
-
test_escape.should.raise(ArgumentError)
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
if "".respond_to?(:encode)
|
244
|
-
should "escape html entities in unicode strings" do
|
245
|
-
# the following will cause warnings if the regex is poorly encoded:
|
246
|
-
Rack::Utils.escape_html("☃").should.equal "☃"
|
247
|
-
end
|
200
|
+
Rack::Utils.escape_html("f/o").should.equal "f/o"
|
201
|
+
Rack::Utils.escape_html("<foo></foo>").should.equal "<foo></foo>"
|
248
202
|
end
|
249
203
|
|
250
204
|
should "figure out which encodings are acceptable" do
|
251
205
|
helper = lambda do |a, b|
|
252
|
-
|
206
|
+
Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
|
253
207
|
Rack::Utils.select_best_encoding(a, b)
|
254
208
|
end
|
255
209
|
|
@@ -274,12 +228,7 @@ describe Rack::Utils do
|
|
274
228
|
Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
|
275
229
|
end
|
276
230
|
|
277
|
-
should "
|
278
|
-
Rack::Utils.secure_compare('a', 'a').should.equal true
|
279
|
-
Rack::Utils.secure_compare('a', 'b').should.equal false
|
280
|
-
end
|
281
|
-
|
282
|
-
should "should return status code for integer" do
|
231
|
+
should "return status code for integer" do
|
283
232
|
Rack::Utils.status_code(200).should.equal 200
|
284
233
|
end
|
285
234
|
|
@@ -292,6 +241,49 @@ describe Rack::Utils do
|
|
292
241
|
end
|
293
242
|
end
|
294
243
|
|
244
|
+
describe Rack::Utils, "byte_range" do
|
245
|
+
should "ignore missing or syntactically invalid byte ranges" do
|
246
|
+
Rack::Utils.byte_ranges({},500).should.equal nil
|
247
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "foobar"},500).should.equal nil
|
248
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "furlongs=123-456"},500).should.equal nil
|
249
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes="},500).should.equal nil
|
250
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-"},500).should.equal nil
|
251
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123,456"},500).should.equal nil
|
252
|
+
# A range of non-positive length is syntactically invalid and ignored:
|
253
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-123"},500).should.equal nil
|
254
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=456-455"},500).should.equal nil
|
255
|
+
end
|
256
|
+
|
257
|
+
should "parse simple byte ranges" do
|
258
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},500).should.equal [(123..456)]
|
259
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-"},500).should.equal [(123..499)]
|
260
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},500).should.equal [(400..499)]
|
261
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},500).should.equal [(0..0)]
|
262
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=499-499"},500).should.equal [(499..499)]
|
263
|
+
end
|
264
|
+
|
265
|
+
should "truncate byte ranges" do
|
266
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-999"},500).should.equal [(123..499)]
|
267
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=600-999"},500).should.equal []
|
268
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-999"},500).should.equal [(0..499)]
|
269
|
+
end
|
270
|
+
|
271
|
+
should "ignore unsatisfiable byte ranges" do
|
272
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-501"},500).should.equal []
|
273
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=500-"},500).should.equal []
|
274
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=999-"},500).should.equal []
|
275
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},500).should.equal []
|
276
|
+
end
|
277
|
+
|
278
|
+
should "handle byte ranges of empty files" do
|
279
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=123-456"},0).should.equal []
|
280
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-"},0).should.equal []
|
281
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-100"},0).should.equal []
|
282
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=0-0"},0).should.equal []
|
283
|
+
Rack::Utils.byte_ranges({"HTTP_RANGE" => "bytes=-0"},0).should.equal []
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
295
287
|
describe Rack::Utils::HeaderHash do
|
296
288
|
should "retain header case" do
|
297
289
|
h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
|
@@ -375,6 +367,12 @@ describe Rack::Utils::HeaderHash do
|
|
375
367
|
end
|
376
368
|
end
|
377
369
|
|
370
|
+
should "not create headers out of thin air" do
|
371
|
+
h = Rack::Utils::HeaderHash.new
|
372
|
+
h['foo']
|
373
|
+
h['foo'].should.be.nil
|
374
|
+
h.should.not.include 'foo'
|
375
|
+
end
|
378
376
|
end
|
379
377
|
|
380
378
|
describe Rack::Utils::Context do
|
@@ -429,288 +427,4 @@ describe Rack::Utils::Context do
|
|
429
427
|
r5.status.should.equal 200
|
430
428
|
r4.body.should.equal r5.body
|
431
429
|
end
|
432
|
-
end
|
433
|
-
|
434
|
-
describe Rack::Utils::Multipart do
|
435
|
-
def multipart_fixture(name)
|
436
|
-
file = multipart_file(name)
|
437
|
-
data = File.open(file, 'rb') { |io| io.read }
|
438
|
-
|
439
|
-
type = "multipart/form-data; boundary=AaB03x"
|
440
|
-
length = data.respond_to?(:bytesize) ? data.bytesize : data.size
|
441
|
-
|
442
|
-
{ "CONTENT_TYPE" => type,
|
443
|
-
"CONTENT_LENGTH" => length.to_s,
|
444
|
-
:input => StringIO.new(data) }
|
445
|
-
end
|
446
|
-
|
447
|
-
def multipart_file(name)
|
448
|
-
File.join(File.dirname(__FILE__), "multipart", name.to_s)
|
449
|
-
end
|
450
|
-
|
451
|
-
should "return nil if content type is not multipart" do
|
452
|
-
env = Rack::MockRequest.env_for("/",
|
453
|
-
"CONTENT_TYPE" => 'application/x-www-form-urlencoded')
|
454
|
-
Rack::Utils::Multipart.parse_multipart(env).should.equal nil
|
455
|
-
end
|
456
|
-
|
457
|
-
should "parse multipart upload with text file" do
|
458
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
|
459
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
460
|
-
params["submit-name"].should.equal "Larry"
|
461
|
-
params["files"][:type].should.equal "text/plain"
|
462
|
-
params["files"][:filename].should.equal "file1.txt"
|
463
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
464
|
-
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
465
|
-
"Content-Type: text/plain\r\n"
|
466
|
-
params["files"][:name].should.equal "files"
|
467
|
-
params["files"][:tempfile].read.should.equal "contents"
|
468
|
-
end
|
469
|
-
|
470
|
-
should "parse multipart upload with nested parameters" do
|
471
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
|
472
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
473
|
-
params["foo"]["submit-name"].should.equal "Larry"
|
474
|
-
params["foo"]["files"][:type].should.equal "text/plain"
|
475
|
-
params["foo"]["files"][:filename].should.equal "file1.txt"
|
476
|
-
params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " +
|
477
|
-
"name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
|
478
|
-
"Content-Type: text/plain\r\n"
|
479
|
-
params["foo"]["files"][:name].should.equal "foo[files]"
|
480
|
-
params["foo"]["files"][:tempfile].read.should.equal "contents"
|
481
|
-
end
|
482
|
-
|
483
|
-
should "parse multipart upload with binary file" do
|
484
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
|
485
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
486
|
-
params["submit-name"].should.equal "Larry"
|
487
|
-
params["files"][:type].should.equal "image/png"
|
488
|
-
params["files"][:filename].should.equal "rack-logo.png"
|
489
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
490
|
-
"name=\"files\"; filename=\"rack-logo.png\"\r\n" +
|
491
|
-
"Content-Type: image/png\r\n"
|
492
|
-
params["files"][:name].should.equal "files"
|
493
|
-
params["files"][:tempfile].read.length.should.equal 26473
|
494
|
-
end
|
495
|
-
|
496
|
-
should "parse multipart upload with empty file" do
|
497
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
|
498
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
499
|
-
params["submit-name"].should.equal "Larry"
|
500
|
-
params["files"][:type].should.equal "text/plain"
|
501
|
-
params["files"][:filename].should.equal "file1.txt"
|
502
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
503
|
-
"name=\"files\"; filename=\"file1.txt\"\r\n" +
|
504
|
-
"Content-Type: text/plain\r\n"
|
505
|
-
params["files"][:name].should.equal "files"
|
506
|
-
params["files"][:tempfile].read.should.equal ""
|
507
|
-
end
|
508
|
-
|
509
|
-
should "parse multipart upload with filename with semicolons" do
|
510
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
|
511
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
512
|
-
params["files"][:type].should.equal "text/plain"
|
513
|
-
params["files"][:filename].should.equal "fi;le1.txt"
|
514
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
515
|
-
"name=\"files\"; filename=\"fi;le1.txt\"\r\n" +
|
516
|
-
"Content-Type: text/plain\r\n"
|
517
|
-
params["files"][:name].should.equal "files"
|
518
|
-
params["files"][:tempfile].read.should.equal "contents"
|
519
|
-
end
|
520
|
-
|
521
|
-
should "not include file params if no file was selected" do
|
522
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
|
523
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
524
|
-
params["submit-name"].should.equal "Larry"
|
525
|
-
params["files"].should.equal nil
|
526
|
-
params.keys.should.not.include "files"
|
527
|
-
end
|
528
|
-
|
529
|
-
should "parse IE multipart upload and clean up filename" do
|
530
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
|
531
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
532
|
-
params["files"][:type].should.equal "text/plain"
|
533
|
-
params["files"][:filename].should.equal "file1.txt"
|
534
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
535
|
-
"name=\"files\"; " +
|
536
|
-
'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
|
537
|
-
"\r\nContent-Type: text/plain\r\n"
|
538
|
-
params["files"][:name].should.equal "files"
|
539
|
-
params["files"][:tempfile].read.should.equal "contents"
|
540
|
-
end
|
541
|
-
|
542
|
-
should "parse filename and modification param" do
|
543
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_and_modification_param))
|
544
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
545
|
-
params["files"][:type].should.equal "image/jpeg"
|
546
|
-
params["files"][:filename].should.equal "genome.jpeg"
|
547
|
-
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
|
548
|
-
"Content-Disposition: attachment; " +
|
549
|
-
"name=\"files\"; " +
|
550
|
-
"filename=genome.jpeg; " +
|
551
|
-
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
552
|
-
"Content-Description: a complete map of the human genome\r\n"
|
553
|
-
params["files"][:name].should.equal "files"
|
554
|
-
params["files"][:tempfile].read.should.equal "contents"
|
555
|
-
end
|
556
|
-
|
557
|
-
should "parse filename with escaped quotes" do
|
558
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes))
|
559
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
560
|
-
params["files"][:type].should.equal "application/octet-stream"
|
561
|
-
params["files"][:filename].should.equal "escape \"quotes"
|
562
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
563
|
-
"name=\"files\"; " +
|
564
|
-
"filename=\"escape \\\"quotes\"\r\n" +
|
565
|
-
"Content-Type: application/octet-stream\r\n"
|
566
|
-
params["files"][:name].should.equal "files"
|
567
|
-
params["files"][:tempfile].read.should.equal "contents"
|
568
|
-
end
|
569
|
-
|
570
|
-
should "parse filename with percent escaped quotes" do
|
571
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_percent_escaped_quotes))
|
572
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
573
|
-
params["files"][:type].should.equal "application/octet-stream"
|
574
|
-
params["files"][:filename].should.equal "escape \"quotes"
|
575
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
576
|
-
"name=\"files\"; " +
|
577
|
-
"filename=\"escape %22quotes\"\r\n" +
|
578
|
-
"Content-Type: application/octet-stream\r\n"
|
579
|
-
params["files"][:name].should.equal "files"
|
580
|
-
params["files"][:tempfile].read.should.equal "contents"
|
581
|
-
end
|
582
|
-
|
583
|
-
should "parse filename with unescaped quotes" do
|
584
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_unescaped_quotes))
|
585
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
586
|
-
params["files"][:type].should.equal "application/octet-stream"
|
587
|
-
params["files"][:filename].should.equal "escape \"quotes"
|
588
|
-
params["files"][:head].should.equal "Content-Disposition: form-data; " +
|
589
|
-
"name=\"files\"; " +
|
590
|
-
"filename=\"escape \"quotes\"\r\n" +
|
591
|
-
"Content-Type: application/octet-stream\r\n"
|
592
|
-
params["files"][:name].should.equal "files"
|
593
|
-
params["files"][:tempfile].read.should.equal "contents"
|
594
|
-
end
|
595
|
-
|
596
|
-
should "parse filename with escaped quotes and modification param" do
|
597
|
-
env = Rack::MockRequest.env_for("/", multipart_fixture(:filename_with_escaped_quotes_and_modification_param))
|
598
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
599
|
-
params["files"][:type].should.equal "image/jpeg"
|
600
|
-
params["files"][:filename].should.equal "\"human\" genome.jpeg"
|
601
|
-
params["files"][:head].should.equal "Content-Type: image/jpeg\r\n" +
|
602
|
-
"Content-Disposition: attachment; " +
|
603
|
-
"name=\"files\"; " +
|
604
|
-
"filename=\"\"human\" genome.jpeg\"; " +
|
605
|
-
"modification-date=\"Wed, 12 Feb 1997 16:29:51 -0500\";\r\n" +
|
606
|
-
"Content-Description: a complete map of the human genome\r\n"
|
607
|
-
params["files"][:name].should.equal "files"
|
608
|
-
params["files"][:tempfile].read.should.equal "contents"
|
609
|
-
end
|
610
|
-
|
611
|
-
|
612
|
-
it "should parse very long unquoted multipart file names" do
|
613
|
-
data = <<-EOF
|
614
|
-
--AaB03x\r
|
615
|
-
Content-Type: text/plain\r
|
616
|
-
Content-Disposition: attachment; name=file; filename=#{'long' * 100}\r
|
617
|
-
\r
|
618
|
-
contents\r
|
619
|
-
--AaB03x--\r
|
620
|
-
EOF
|
621
|
-
|
622
|
-
options = {
|
623
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
624
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
625
|
-
:input => StringIO.new(data)
|
626
|
-
}
|
627
|
-
env = Rack::MockRequest.env_for("/", options)
|
628
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
629
|
-
|
630
|
-
params["file"][:filename].should.equal('long' * 100)
|
631
|
-
end
|
632
|
-
|
633
|
-
it "rewinds input after parsing upload" do
|
634
|
-
options = multipart_fixture(:text)
|
635
|
-
input = options[:input]
|
636
|
-
env = Rack::MockRequest.env_for("/", options)
|
637
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
638
|
-
params["submit-name"].should.equal "Larry"
|
639
|
-
params["files"][:filename].should.equal "file1.txt"
|
640
|
-
input.read.length.should.equal 197
|
641
|
-
end
|
642
|
-
|
643
|
-
it "builds multipart body" do
|
644
|
-
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
645
|
-
data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
|
646
|
-
|
647
|
-
options = {
|
648
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
649
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
650
|
-
:input => StringIO.new(data)
|
651
|
-
}
|
652
|
-
env = Rack::MockRequest.env_for("/", options)
|
653
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
654
|
-
params["submit-name"].should.equal "Larry"
|
655
|
-
params["files"][:filename].should.equal "file1.txt"
|
656
|
-
params["files"][:tempfile].read.should.equal "contents"
|
657
|
-
end
|
658
|
-
|
659
|
-
it "builds nested multipart body" do
|
660
|
-
files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
|
661
|
-
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
|
662
|
-
|
663
|
-
options = {
|
664
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
|
665
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
666
|
-
:input => StringIO.new(data)
|
667
|
-
}
|
668
|
-
env = Rack::MockRequest.env_for("/", options)
|
669
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
670
|
-
params["people"][0]["submit-name"].should.equal "Larry"
|
671
|
-
params["people"][0]["files"][:filename].should.equal "file1.txt"
|
672
|
-
params["people"][0]["files"][:tempfile].read.should.equal "contents"
|
673
|
-
end
|
674
|
-
|
675
|
-
it "can parse fields that end at the end of the buffer" do
|
676
|
-
input = File.read(multipart_file("bad_robots"))
|
677
|
-
|
678
|
-
req = Rack::Request.new Rack::MockRequest.env_for("/",
|
679
|
-
"CONTENT_TYPE" => "multipart/form-data, boundary=1yy3laWhgX31qpiHinh67wJXqKalukEUTvqTzmon",
|
680
|
-
"CONTENT_LENGTH" => input.size,
|
681
|
-
:input => input)
|
682
|
-
|
683
|
-
req.POST['file.path'].should.equal "/var/tmp/uploads/4/0001728414"
|
684
|
-
req.POST['addresses'].should.not.equal nil
|
685
|
-
end
|
686
|
-
|
687
|
-
it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
|
688
|
-
data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n")
|
689
|
-
options = {
|
690
|
-
"CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
|
691
|
-
"CONTENT_LENGTH" => data.length.to_s,
|
692
|
-
:input => StringIO.new(data)
|
693
|
-
}
|
694
|
-
env = Rack::MockRequest.env_for("/", options)
|
695
|
-
params = Rack::Utils::Multipart.parse_multipart(env)
|
696
|
-
|
697
|
-
params.should.not.equal nil
|
698
|
-
params.keys.should.include "AAAAAAAAAAAAAAAAAAA"
|
699
|
-
params["AAAAAAAAAAAAAAAAAAA"].keys.should.include "PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"
|
700
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"].keys.should.include "new"
|
701
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"].keys.should.include "-2"
|
702
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"].keys.should.include "ba_unit_id"
|
703
|
-
params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
|
704
|
-
end
|
705
|
-
|
706
|
-
should "return nil if no UploadedFiles were used" do
|
707
|
-
data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
|
708
|
-
data.should.equal nil
|
709
|
-
end
|
710
|
-
|
711
|
-
should "raise ArgumentError if params is not a Hash" do
|
712
|
-
lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
|
713
|
-
should.raise(ArgumentError).
|
714
|
-
message.should.equal "value must be a Hash"
|
715
|
-
end
|
716
|
-
end
|
430
|
+
end
|