rack 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. data/README +23 -26
  2. data/SPEC +3 -3
  3. data/lib/rack/auth/digest/params.rb +1 -1
  4. data/lib/rack/content_length.rb +1 -1
  5. data/lib/rack/etag.rb +15 -6
  6. data/lib/rack/file.rb +3 -1
  7. data/lib/rack/handler/cgi.rb +8 -7
  8. data/lib/rack/handler/fastcgi.rb +1 -1
  9. data/lib/rack/handler/lsws.rb +1 -1
  10. data/lib/rack/handler/mongrel.rb +1 -1
  11. data/lib/rack/handler/scgi.rb +1 -4
  12. data/lib/rack/handler/webrick.rb +10 -6
  13. data/lib/rack/lint.rb +29 -37
  14. data/lib/rack/mime.rb +2 -0
  15. data/lib/rack/mock.rb +2 -1
  16. data/lib/rack/recursive.rb +4 -0
  17. data/lib/rack/request.rb +8 -6
  18. data/lib/rack/response.rb +1 -0
  19. data/lib/rack/rewindable_input.rb +13 -10
  20. data/lib/rack/sendfile.rb +8 -6
  21. data/lib/rack/server.rb +66 -7
  22. data/lib/rack/session/memcache.rb +1 -1
  23. data/lib/rack/urlmap.rb +6 -7
  24. data/lib/rack/utils.rb +64 -22
  25. data/lib/rack.rb +1 -12
  26. data/rack.gemspec +6 -6
  27. data/spec/cgi/lighttpd.conf +25 -0
  28. data/spec/cgi/rackup_stub.rb +6 -0
  29. data/spec/cgi/sample_rackup.ru +5 -0
  30. data/spec/cgi/test +9 -0
  31. data/spec/cgi/test.fcgi +8 -0
  32. data/spec/cgi/test.ru +5 -0
  33. data/spec/multipart/bad_robots +259 -0
  34. data/spec/multipart/binary +0 -0
  35. data/spec/multipart/empty +10 -0
  36. data/spec/multipart/fail_16384_nofile +814 -0
  37. data/spec/multipart/file1.txt +1 -0
  38. data/spec/multipart/filename_and_modification_param +7 -0
  39. data/spec/multipart/filename_with_escaped_quotes +6 -0
  40. data/spec/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  41. data/spec/multipart/filename_with_percent_escaped_quotes +6 -0
  42. data/spec/multipart/filename_with_unescaped_quotes +6 -0
  43. data/spec/multipart/ie +6 -0
  44. data/spec/multipart/nested +10 -0
  45. data/spec/multipart/none +9 -0
  46. data/spec/multipart/semicolon +6 -0
  47. data/spec/multipart/text +10 -0
  48. data/spec/rackup/config.ru +31 -0
  49. data/{test/spec_rack_auth_basic.rb → spec/spec_auth_basic.rb} +11 -14
  50. data/{test/spec_rack_auth_digest.rb → spec/spec_auth_digest.rb} +18 -21
  51. data/{test/spec_rack_builder.rb → spec/spec_builder.rb} +49 -10
  52. data/{test/spec_rack_cascade.rb → spec/spec_cascade.rb} +7 -10
  53. data/{test/spec_rack_cgi.rb → spec/spec_cgi.rb} +34 -32
  54. data/{test/spec_rack_chunked.rb → spec/spec_chunked.rb} +8 -10
  55. data/{test/spec_rack_commonlogger.rb → spec/spec_commonlogger.rb} +10 -15
  56. data/{test/spec_rack_conditionalget.rb → spec/spec_conditionalget.rb} +5 -7
  57. data/{test/spec_rack_config.rb → spec/spec_config.rb} +6 -7
  58. data/{test/spec_rack_content_length.rb → spec/spec_content_length.rb} +7 -8
  59. data/{test/spec_rack_content_type.rb → spec/spec_content_type.rb} +5 -6
  60. data/{test/spec_rack_deflater.rb → spec/spec_deflater.rb} +11 -13
  61. data/{test/spec_rack_directory.rb → spec/spec_directory.rb} +6 -10
  62. data/{test/spec_rack_etag.rb → spec/spec_etag.rb} +3 -5
  63. data/{test/spec_rack_fastcgi.rb → spec/spec_fastcgi.rb} +36 -29
  64. data/{test/spec_rack_file.rb → spec/spec_file.rb} +9 -13
  65. data/{test/spec_rack_handler.rb → spec/spec_handler.rb} +10 -12
  66. data/{test/spec_rack_head.rb → spec/spec_head.rb} +3 -3
  67. data/{test/spec_rack_lint.rb → spec/spec_lint.rb} +19 -32
  68. data/{test/spec_rack_lobster.rb → spec/spec_lobster.rb} +9 -11
  69. data/{test/spec_rack_lock.rb → spec/spec_lock.rb} +15 -17
  70. data/{test/spec_rack_logger.rb → spec/spec_logger.rb} +6 -7
  71. data/{test/spec_rack_methodoverride.rb → spec/spec_methodoverride.rb} +15 -17
  72. data/{test/spec_rack_mock.rb → spec/spec_mock.rb} +30 -32
  73. data/{test/spec_rack_mongrel.rb → spec/spec_mongrel.rb} +40 -46
  74. data/{test/spec_rack_nulllogger.rb → spec/spec_nulllogger.rb} +4 -5
  75. data/{test/spec_rack_recursive.rb → spec/spec_recursive.rb} +28 -36
  76. data/{test/spec_rack_request.rb → spec/spec_request.rb} +84 -49
  77. data/{test/spec_rack_response.rb → spec/spec_response.rb} +48 -29
  78. data/spec/spec_rewindable_input.rb +118 -0
  79. data/{test/spec_rack_runtime.rb → spec/spec_runtime.rb} +15 -11
  80. data/{test/spec_rack_sendfile.rb → spec/spec_sendfile.rb} +11 -14
  81. data/{test/spec_rack_session_cookie.rb → spec/spec_session_cookie.rb} +15 -18
  82. data/{test/spec_rack_session_memcache.rb → spec/spec_session_memcache.rb} +32 -26
  83. data/{test/spec_rack_session_pool.rb → spec/spec_session_pool.rb} +36 -31
  84. data/spec/spec_showexceptions.rb +23 -0
  85. data/spec/spec_showstatus.rb +79 -0
  86. data/{test/spec_rack_static.rb → spec/spec_static.rb} +5 -9
  87. data/{test/spec_rack_thin.rb → spec/spec_thin.rb} +30 -35
  88. data/{test/spec_rack_urlmap.rb → spec/spec_urlmap.rb} +6 -8
  89. data/{test/spec_rack_utils.rb → spec/spec_utils.rb} +136 -71
  90. data/{test/spec_rack_webrick.rb → spec/spec_webrick.rb} +28 -36
  91. data/spec/testrequest.rb +77 -0
  92. data/spec/unregistered_handler/rack/handler/unregistered.rb +7 -0
  93. data/spec/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  94. metadata +206 -124
  95. data/RDOX +0 -0
  96. data/lib/rack/adapter/camping.rb +0 -22
  97. data/test/spec_rack_camping.rb +0 -51
  98. data/test/spec_rack_rewindable_input.rb +0 -118
  99. data/test/spec_rack_showexceptions.rb +0 -21
  100. data/test/spec_rack_showstatus.rb +0 -72
  101. data/test/spec_rackup.rb +0 -154
@@ -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
- context "Rack::Utils" do
8
- specify "should escape correctly" do
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
- specify "should escape correctly for multibyte characters" do
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
- specify "should unescape correctly" do
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,11 +26,11 @@ context "Rack::Utils" do
29
26
  should.equal "q1!2\"'w$5&7/z8)?\\"
30
27
  end
31
28
 
32
- specify "should parse query strings correctly" do
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\"").
36
- should.equal "foo" => "bar"
33
+ should.equal "foo" => "\"bar\""
37
34
  Rack::Utils.parse_query("foo=bar&foo=quux").
38
35
  should.equal "foo" => ["bar", "quux"]
39
36
  Rack::Utils.parse_query("foo=1&bar=2").
@@ -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
- specify "should parse nested query strings correctly" do
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=").
@@ -51,7 +48,7 @@ context "Rack::Utils" do
51
48
  Rack::Utils.parse_nested_query("foo=bar").
52
49
  should.equal "foo" => "bar"
53
50
  Rack::Utils.parse_nested_query("foo=\"bar\"").
54
- should.equal "foo" => "bar"
51
+ should.equal "foo" => "\"bar\""
55
52
 
56
53
  Rack::Utils.parse_nested_query("foo=bar&foo=quux").
57
54
  should.equal "foo" => "quux"
@@ -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
- specify "should build query strings correctly" do
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
- specify "should build nested query strings correctly" do
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
- specify "should figure out which encodings are acceptable" do
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,43 +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
- specify "should return the bytesize of String" do
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
- specify "should return status code for integer" do
205
+ should "return status code for integer" do
209
206
  Rack::Utils.status_code(200).should.equal 200
210
207
  end
211
208
 
212
- specify "should return status code for string" do
209
+ should "return status code for string" do
213
210
  Rack::Utils.status_code("200").should.equal 200
214
211
  end
215
212
 
216
- specify "should return status code for symbol" do
213
+ should "return status code for symbol" do
217
214
  Rack::Utils.status_code(:ok).should.equal 200
218
215
  end
219
216
  end
220
217
 
221
- context "Rack::Utils::HeaderHash" do
222
- specify "should retain header case" do
218
+ describe Rack::Utils::HeaderHash do
219
+ should "retain header case" do
223
220
  h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
224
221
  h['ETag'] = 'Boo!'
225
222
  h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
226
223
  end
227
224
 
228
- specify "should check existence of keys case insensitively" do
225
+ should "check existence of keys case insensitively" do
229
226
  h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
230
227
  h.should.include 'content-md5'
231
228
  h.should.not.include 'ETag'
232
229
  end
233
230
 
234
- specify "should merge case-insensitively" do
231
+ should "merge case-insensitively" do
235
232
  h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
236
233
  merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
237
234
  merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
238
235
  end
239
236
 
240
- specify "should overwrite case insensitively and assume the new key's case" do
237
+ should "overwrite case insensitively and assume the new key's case" do
241
238
  h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
242
239
  h["foo-bar"] = "bizzle"
243
240
  h["FOO-BAR"].should.equal "bizzle"
@@ -245,55 +242,55 @@ context "Rack::Utils::HeaderHash" do
245
242
  h.to_hash.should.equal "foo-bar" => "bizzle"
246
243
  end
247
244
 
248
- specify "should be converted to real Hash" do
245
+ should "be converted to real Hash" do
249
246
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
250
247
  h.to_hash.should.be.instance_of Hash
251
248
  end
252
249
 
253
- specify "should convert Array values to Strings when converting to Hash" do
250
+ should "convert Array values to Strings when converting to Hash" do
254
251
  h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
255
252
  h.to_hash.should.equal({ "foo" => "bar\nbaz" })
256
253
  end
257
254
 
258
- specify "should replace hashes correctly" do
255
+ should "replace hashes correctly" do
259
256
  h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
260
257
  j = {"foo" => "bar"}
261
258
  h.replace(j)
262
259
  h["foo"].should.equal "bar"
263
260
  end
264
261
 
265
- specify "should be able to delete the given key case-sensitively" do
262
+ should "be able to delete the given key case-sensitively" do
266
263
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
267
264
  h.delete("foo")
268
265
  h["foo"].should.be.nil
269
266
  h["FOO"].should.be.nil
270
267
  end
271
268
 
272
- specify "should be able to delete the given key case-insensitively" do
269
+ should "be able to delete the given key case-insensitively" do
273
270
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
274
271
  h.delete("FOO")
275
272
  h["foo"].should.be.nil
276
273
  h["FOO"].should.be.nil
277
274
  end
278
275
 
279
- specify "should return the deleted value when #delete is called on an existing key" do
276
+ should "return the deleted value when #delete is called on an existing key" do
280
277
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
281
278
  h.delete("Foo").should.equal("bar")
282
279
  end
283
280
 
284
- specify "should return nil when #delete is called on a non-existant key" do
281
+ should "return nil when #delete is called on a non-existant key" do
285
282
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
286
283
  h.delete("Hello").should.be.nil
287
284
  end
288
285
 
289
- specify "should avoid unnecessary object creation if possible" do
286
+ should "avoid unnecessary object creation if possible" do
290
287
  a = Rack::Utils::HeaderHash.new("foo" => "bar")
291
288
  b = Rack::Utils::HeaderHash.new(a)
292
289
  b.object_id.should.equal(a.object_id)
293
290
  b.should.equal(a)
294
291
  end
295
292
 
296
- specify "should convert Array values to Strings when responding to #each" do
293
+ should "convert Array values to Strings when responding to #each" do
297
294
  h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
298
295
  h.each do |k,v|
299
296
  k.should.equal("foo")
@@ -303,7 +300,7 @@ context "Rack::Utils::HeaderHash" do
303
300
 
304
301
  end
305
302
 
306
- context "Rack::Utils::Context" do
303
+ describe Rack::Utils::Context do
307
304
  class ContextTest
308
305
  attr_reader :app
309
306
  def initialize app; @app=app; end
@@ -316,7 +313,7 @@ context "Rack::Utils::Context" do
316
313
  test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
317
314
  test_app = ContextTest.new test_target4
318
315
 
319
- specify "should set context correctly" do
316
+ should "set context correctly" do
320
317
  test_app.app.should.equal test_target4
321
318
  c1 = Rack::Utils::Context.new(test_app, test_target1)
322
319
  c1.for.should.equal test_app
@@ -326,7 +323,7 @@ context "Rack::Utils::Context" do
326
323
  c2.app.should.equal test_target2
327
324
  end
328
325
 
329
- specify "should alter app on recontexting" do
326
+ should "alter app on recontexting" do
330
327
  c1 = Rack::Utils::Context.new(test_app, test_target1)
331
328
  c2 = c1.recontext(test_target2)
332
329
  c2.for.should.equal test_app
@@ -336,7 +333,7 @@ context "Rack::Utils::Context" do
336
333
  c3.app.should.equal test_target3
337
334
  end
338
335
 
339
- specify "should run different apps" do
336
+ should "run different apps" do
340
337
  c1 = Rack::Utils::Context.new test_app, test_target1
341
338
  c2 = c1.recontext test_target2
342
339
  c3 = c2.recontext test_target3
@@ -350,21 +347,37 @@ context "Rack::Utils::Context" do
350
347
  r3 = c3.call(:misc_symbol)
351
348
  r3.should.be.nil
352
349
  r4 = Rack::MockRequest.new(a4).get('/')
353
- r4.status.should.be 200
350
+ r4.status.should.equal 200
354
351
  r5 = Rack::MockRequest.new(a5).get('/')
355
- r5.status.should.be 200
352
+ r5.status.should.equal 200
356
353
  r4.body.should.equal r5.body
357
354
  end
358
355
  end
359
356
 
360
- context "Rack::Utils::Multipart" do
361
- specify "should return nil if content type is not multipart" do
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
362
375
  env = Rack::MockRequest.env_for("/",
363
376
  "CONTENT_TYPE" => 'application/x-www-form-urlencoded')
364
377
  Rack::Utils::Multipart.parse_multipart(env).should.equal nil
365
378
  end
366
379
 
367
- specify "should parse multipart upload with text file" do
380
+ should "parse multipart upload with text file" do
368
381
  env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
369
382
  params = Rack::Utils::Multipart.parse_multipart(env)
370
383
  params["submit-name"].should.equal "Larry"
@@ -377,7 +390,7 @@ context "Rack::Utils::Multipart" do
377
390
  params["files"][:tempfile].read.should.equal "contents"
378
391
  end
379
392
 
380
- specify "should parse multipart upload with nested parameters" do
393
+ should "parse multipart upload with nested parameters" do
381
394
  env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
382
395
  params = Rack::Utils::Multipart.parse_multipart(env)
383
396
  params["foo"]["submit-name"].should.equal "Larry"
@@ -390,7 +403,7 @@ context "Rack::Utils::Multipart" do
390
403
  params["foo"]["files"][:tempfile].read.should.equal "contents"
391
404
  end
392
405
 
393
- specify "should parse multipart upload with binary file" do
406
+ should "parse multipart upload with binary file" do
394
407
  env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
395
408
  params = Rack::Utils::Multipart.parse_multipart(env)
396
409
  params["submit-name"].should.equal "Larry"
@@ -403,7 +416,7 @@ context "Rack::Utils::Multipart" do
403
416
  params["files"][:tempfile].read.length.should.equal 26473
404
417
  end
405
418
 
406
- specify "should parse multipart upload with empty file" do
419
+ should "parse multipart upload with empty file" do
407
420
  env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
408
421
  params = Rack::Utils::Multipart.parse_multipart(env)
409
422
  params["submit-name"].should.equal "Larry"
@@ -416,7 +429,7 @@ context "Rack::Utils::Multipart" do
416
429
  params["files"][:tempfile].read.should.equal ""
417
430
  end
418
431
 
419
- specify "should parse multipart upload with filename with semicolons" do
432
+ should "parse multipart upload with filename with semicolons" do
420
433
  env = Rack::MockRequest.env_for("/", multipart_fixture(:semicolon))
421
434
  params = Rack::Utils::Multipart.parse_multipart(env)
422
435
  params["files"][:type].should.equal "text/plain"
@@ -428,7 +441,7 @@ context "Rack::Utils::Multipart" do
428
441
  params["files"][:tempfile].read.should.equal "contents"
429
442
  end
430
443
 
431
- specify "should not include file params if no file was selected" do
444
+ should "not include file params if no file was selected" do
432
445
  env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
433
446
  params = Rack::Utils::Multipart.parse_multipart(env)
434
447
  params["submit-name"].should.equal "Larry"
@@ -436,7 +449,7 @@ context "Rack::Utils::Multipart" do
436
449
  params.keys.should.not.include "files"
437
450
  end
438
451
 
439
- specify "should parse IE multipart upload and clean up filename" do
452
+ should "parse IE multipart upload and clean up filename" do
440
453
  env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
441
454
  params = Rack::Utils::Multipart.parse_multipart(env)
442
455
  params["files"][:type].should.equal "text/plain"
@@ -449,7 +462,76 @@ context "Rack::Utils::Multipart" do
449
462
  params["files"][:tempfile].read.should.equal "contents"
450
463
  end
451
464
 
452
- specify "rewinds input after parsing upload" do
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
453
535
  options = multipart_fixture(:text)
454
536
  input = options[:input]
455
537
  env = Rack::MockRequest.env_for("/", options)
@@ -459,7 +541,7 @@ context "Rack::Utils::Multipart" do
459
541
  input.read.length.should.equal 197
460
542
  end
461
543
 
462
- specify "builds multipart body" do
544
+ it "builds multipart body" do
463
545
  files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
464
546
  data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
465
547
 
@@ -475,7 +557,7 @@ context "Rack::Utils::Multipart" do
475
557
  params["files"][:tempfile].read.should.equal "contents"
476
558
  end
477
559
 
478
- specify "builds nested multipart body" do
560
+ it "builds nested multipart body" do
479
561
  files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
480
562
  data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
481
563
 
@@ -491,7 +573,7 @@ context "Rack::Utils::Multipart" do
491
573
  params["people"][0]["files"][:tempfile].read.should.equal "contents"
492
574
  end
493
575
 
494
- specify "can parse fields that end at the end of the buffer" do
576
+ it "can parse fields that end at the end of the buffer" do
495
577
  input = File.read(multipart_file("bad_robots"))
496
578
 
497
579
  req = Rack::Request.new Rack::MockRequest.env_for("/",
@@ -503,7 +585,7 @@ context "Rack::Utils::Multipart" do
503
585
  req.POST['addresses'].should.not.equal nil
504
586
  end
505
587
 
506
- specify "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
588
+ it "builds complete params with the chunk size of 16384 slicing exactly on boundary" do
507
589
  data = File.open(multipart_file("fail_16384_nofile")) { |f| f.read }.gsub(/\n/, "\r\n")
508
590
  options = {
509
591
  "CONTENT_TYPE" => "multipart/form-data; boundary=----WebKitFormBoundaryWsY0GnpbI5U7ztzo",
@@ -522,31 +604,14 @@ context "Rack::Utils::Multipart" do
522
604
  params["AAAAAAAAAAAAAAAAAAA"]["PLAPLAPLA_MEMMEMMEMM_ATTRATTRER"]["new"]["-2"]["ba_unit_id"].should.equal "1017"
523
605
  end
524
606
 
525
- specify "should return nil if no UploadedFiles were used" do
607
+ should "return nil if no UploadedFiles were used" do
526
608
  data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
527
609
  data.should.equal nil
528
610
  end
529
611
 
530
- specify "should raise ArgumentError if params is not a Hash" do
612
+ should "raise ArgumentError if params is not a Hash" do
531
613
  lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
532
614
  should.raise(ArgumentError).
533
615
  message.should.equal "value must be a Hash"
534
616
  end
535
-
536
- private
537
- def multipart_fixture(name)
538
- file = multipart_file(name)
539
- data = File.open(file, 'rb') { |io| io.read }
540
-
541
- type = "multipart/form-data; boundary=AaB03x"
542
- length = data.respond_to?(:bytesize) ? data.bytesize : data.size
543
-
544
- { "CONTENT_TYPE" => type,
545
- "CONTENT_LENGTH" => length.to_s,
546
- :input => StringIO.new(data) }
547
- end
548
-
549
- def multipart_file(name)
550
- File.join(File.dirname(__FILE__), "multipart", name.to_s)
551
- end
552
617
  end
@@ -1,35 +1,29 @@
1
- require 'test/spec'
2
-
3
- require 'rack/handler/webrick'
4
- require 'rack/lint'
5
- require 'rack/response'
6
- require 'testrequest'
1
+ require 'rack/mock'
2
+ require File.expand_path('../testrequest', __FILE__)
7
3
 
8
4
  Thread.abort_on_exception = true
9
5
 
10
- context "Rack::Handler::WEBrick" do
11
- include TestRequest::Helpers
12
-
13
- setup do
14
- @server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0',
15
- :Port => @port=9202,
16
- :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
17
- :AccessLog => [])
18
- @server.mount "/test", Rack::Handler::WEBrick,
19
- Rack::Lint.new(TestRequest.new)
20
- Thread.new { @server.start }
21
- trap(:INT) { @server.shutdown }
22
- end
6
+ describe Rack::Handler::WEBrick do
7
+ extend TestRequest::Helpers
8
+
9
+ @server = WEBrick::HTTPServer.new(:Host => @host='0.0.0.0',
10
+ :Port => @port=9202,
11
+ :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
12
+ :AccessLog => [])
13
+ @server.mount "/test", Rack::Handler::WEBrick,
14
+ Rack::Lint.new(TestRequest.new)
15
+ Thread.new { @server.start }
16
+ trap(:INT) { @server.shutdown }
23
17
 
24
- specify "should respond" do
18
+ should "respond" do
25
19
  lambda {
26
20
  GET("/test")
27
21
  }.should.not.raise
28
22
  end
29
23
 
30
- specify "should be a WEBrick" do
24
+ should "be a WEBrick" do
31
25
  GET("/test")
32
- status.should.be 200
26
+ status.should.equal 200
33
27
  response["SERVER_SOFTWARE"].should =~ /WEBrick/
34
28
  response["HTTP_VERSION"].should.equal "HTTP/1.1"
35
29
  response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
@@ -37,15 +31,15 @@ context "Rack::Handler::WEBrick" do
37
31
  response["SERVER_NAME"].should.equal "0.0.0.0"
38
32
  end
39
33
 
40
- specify "should have rack headers" do
34
+ should "have rack headers" do
41
35
  GET("/test")
42
36
  response["rack.version"].should.equal [1,1]
43
- response["rack.multithread"].should.be true
44
- response["rack.multiprocess"].should.be false
45
- response["rack.run_once"].should.be false
37
+ response["rack.multithread"].should.be.true
38
+ response["rack.multiprocess"].should.be.false
39
+ response["rack.run_once"].should.be.false
46
40
  end
47
41
 
48
- specify "should have CGI headers on GET" do
42
+ should "have CGI headers on GET" do
49
43
  GET("/test")
50
44
  response["REQUEST_METHOD"].should.equal "GET"
51
45
  response["SCRIPT_NAME"].should.equal "/test"
@@ -69,7 +63,7 @@ context "Rack::Handler::WEBrick" do
69
63
  response["QUERY_STRING"].should.equal "quux=1"
70
64
  end
71
65
 
72
- specify "should have CGI headers on POST" do
66
+ should "have CGI headers on POST" do
73
67
  POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
74
68
  status.should.equal 200
75
69
  response["REQUEST_METHOD"].should.equal "POST"
@@ -80,18 +74,18 @@ context "Rack::Handler::WEBrick" do
80
74
  response["test.postdata"].should.equal "rack-form-data=23"
81
75
  end
82
76
 
83
- specify "should support HTTP auth" do
77
+ should "support HTTP auth" do
84
78
  GET("/test", {:user => "ruth", :passwd => "secret"})
85
79
  response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
86
80
  end
87
81
 
88
- specify "should set status" do
82
+ should "set status" do
89
83
  GET("/test?secret")
90
84
  status.should.equal 403
91
85
  response["rack.url_scheme"].should.equal "http"
92
86
  end
93
87
 
94
- specify "should correctly set cookies" do
88
+ should "correctly set cookies" do
95
89
  @server.mount "/cookie-test", Rack::Handler::WEBrick,
96
90
  Rack::Lint.new(lambda { |req|
97
91
  res = Rack::Response.new
@@ -107,7 +101,7 @@ context "Rack::Handler::WEBrick" do
107
101
  }
108
102
  end
109
103
 
110
- specify "should provide a .run" do
104
+ should "provide a .run" do
111
105
  block_ran = false
112
106
  catch(:done) {
113
107
  Rack::Handler::WEBrick.run(lambda {},
@@ -120,11 +114,9 @@ context "Rack::Handler::WEBrick" do
120
114
  throw :done
121
115
  }
122
116
  }
123
- block_ran.should.be true
117
+ block_ran.should.be.true
124
118
  @s.shutdown
125
119
  end
126
120
 
127
- teardown do
128
- @server.shutdown
129
- end
121
+ @server.shutdown
130
122
  end