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.

Files changed (89) hide show
  1. data/README +9 -177
  2. data/Rakefile +2 -1
  3. data/SPEC +2 -2
  4. data/lib/rack.rb +2 -13
  5. data/lib/rack/auth/abstract/request.rb +7 -5
  6. data/lib/rack/auth/digest/md5.rb +6 -2
  7. data/lib/rack/auth/digest/params.rb +5 -7
  8. data/lib/rack/auth/digest/request.rb +1 -1
  9. data/lib/rack/backports/uri/common.rb +64 -0
  10. data/lib/rack/builder.rb +60 -3
  11. data/lib/rack/chunked.rb +29 -22
  12. data/lib/rack/conditionalget.rb +35 -16
  13. data/lib/rack/content_length.rb +3 -3
  14. data/lib/rack/deflater.rb +5 -2
  15. data/lib/rack/etag.rb +38 -10
  16. data/lib/rack/file.rb +76 -43
  17. data/lib/rack/handler.rb +13 -7
  18. data/lib/rack/handler/cgi.rb +0 -2
  19. data/lib/rack/handler/fastcgi.rb +13 -4
  20. data/lib/rack/handler/lsws.rb +0 -2
  21. data/lib/rack/handler/mongrel.rb +12 -2
  22. data/lib/rack/handler/scgi.rb +9 -1
  23. data/lib/rack/handler/thin.rb +7 -1
  24. data/lib/rack/handler/webrick.rb +12 -5
  25. data/lib/rack/lint.rb +2 -2
  26. data/lib/rack/lock.rb +29 -3
  27. data/lib/rack/methodoverride.rb +1 -1
  28. data/lib/rack/mime.rb +2 -2
  29. data/lib/rack/mock.rb +28 -33
  30. data/lib/rack/multipart.rb +34 -0
  31. data/lib/rack/multipart/generator.rb +93 -0
  32. data/lib/rack/multipart/parser.rb +164 -0
  33. data/lib/rack/multipart/uploaded_file.rb +30 -0
  34. data/lib/rack/request.rb +55 -19
  35. data/lib/rack/response.rb +10 -8
  36. data/lib/rack/sendfile.rb +14 -18
  37. data/lib/rack/server.rb +55 -8
  38. data/lib/rack/session/abstract/id.rb +233 -22
  39. data/lib/rack/session/cookie.rb +99 -46
  40. data/lib/rack/session/memcache.rb +30 -56
  41. data/lib/rack/session/pool.rb +22 -43
  42. data/lib/rack/showexceptions.rb +40 -11
  43. data/lib/rack/showstatus.rb +9 -2
  44. data/lib/rack/static.rb +29 -9
  45. data/lib/rack/urlmap.rb +6 -1
  46. data/lib/rack/utils.rb +67 -326
  47. data/rack.gemspec +2 -3
  48. data/test/builder/anything.rb +5 -0
  49. data/test/builder/comment.ru +4 -0
  50. data/test/builder/end.ru +3 -0
  51. data/test/builder/options.ru +2 -0
  52. data/test/cgi/lighttpd.conf +1 -1
  53. data/test/cgi/lighttpd.errors +412 -0
  54. data/test/multipart/content_type_and_no_filename +6 -0
  55. data/test/multipart/text +5 -0
  56. data/test/multipart/webkit +32 -0
  57. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  58. data/test/spec_auth_digest.rb +20 -5
  59. data/test/spec_builder.rb +29 -0
  60. data/test/spec_cgi.rb +11 -0
  61. data/test/spec_chunked.rb +1 -1
  62. data/test/spec_commonlogger.rb +1 -1
  63. data/test/spec_conditionalget.rb +47 -0
  64. data/test/spec_content_length.rb +0 -6
  65. data/test/spec_content_type.rb +5 -5
  66. data/test/spec_deflater.rb +46 -2
  67. data/test/spec_etag.rb +68 -1
  68. data/test/spec_fastcgi.rb +11 -0
  69. data/test/spec_file.rb +54 -3
  70. data/test/spec_handler.rb +23 -5
  71. data/test/spec_lint.rb +2 -2
  72. data/test/spec_lock.rb +111 -5
  73. data/test/spec_methodoverride.rb +2 -2
  74. data/test/spec_mock.rb +3 -3
  75. data/test/spec_mongrel.rb +1 -2
  76. data/test/spec_multipart.rb +279 -0
  77. data/test/spec_request.rb +222 -38
  78. data/test/spec_response.rb +9 -3
  79. data/test/spec_server.rb +74 -0
  80. data/test/spec_session_abstract_id.rb +43 -0
  81. data/test/spec_session_cookie.rb +97 -15
  82. data/test/spec_session_memcache.rb +60 -50
  83. data/test/spec_session_pool.rb +63 -40
  84. data/test/spec_showexceptions.rb +64 -0
  85. data/test/spec_static.rb +23 -0
  86. data/test/spec_utils.rb +65 -351
  87. data/test/spec_webrick.rb +23 -4
  88. metadata +35 -15
  89. data/test/spec_auth.rb +0 -57
@@ -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
@@ -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
@@ -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&amp;o"
221
196
  Rack::Utils.escape_html("f<o").should.equal "f&lt;o"
222
197
  Rack::Utils.escape_html("f>o").should.equal "f&gt;o"
223
- Rack::Utils.escape_html("f'o").should.equal "f&#39;o"
198
+ Rack::Utils.escape_html("f'o").should.equal "f&#x27;o"
224
199
  Rack::Utils.escape_html('f"o').should.equal "f&quot;o"
225
- Rack::Utils.escape_html("f/o").should.equal "f&#47;o"
226
- Rack::Utils.escape_html("<foo></foo>").should.equal "&lt;foo&gt;&lt;&#47;foo&gt;"
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&lt;"
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&#x2F;o"
201
+ Rack::Utils.escape_html("<foo></foo>").should.equal "&lt;foo&gt;&lt;&#x2F;foo&gt;"
248
202
  end
249
203
 
250
204
  should "figure out which encodings are acceptable" do
251
205
  helper = lambda do |a, b|
252
- request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
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 "should perform constant time string comparison" do
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