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.

Files changed (105) hide show
  1. data/README +9 -205
  2. data/SPEC +3 -3
  3. data/lib/rack.rb +1 -24
  4. data/lib/rack/auth/abstract/request.rb +1 -5
  5. data/lib/rack/auth/digest/md5.rb +1 -2
  6. data/lib/rack/auth/digest/params.rb +1 -1
  7. data/lib/rack/content_length.rb +1 -1
  8. data/lib/rack/etag.rb +15 -6
  9. data/lib/rack/file.rb +3 -1
  10. data/lib/rack/handler/cgi.rb +8 -7
  11. data/lib/rack/handler/fastcgi.rb +1 -1
  12. data/lib/rack/handler/lsws.rb +1 -1
  13. data/lib/rack/handler/mongrel.rb +1 -1
  14. data/lib/rack/handler/scgi.rb +1 -4
  15. data/lib/rack/handler/webrick.rb +10 -6
  16. data/lib/rack/lint.rb +29 -37
  17. data/lib/rack/mime.rb +2 -0
  18. data/lib/rack/mock.rb +2 -1
  19. data/lib/rack/recursive.rb +4 -0
  20. data/lib/rack/request.rb +8 -6
  21. data/lib/rack/response.rb +1 -0
  22. data/lib/rack/rewindable_input.rb +13 -10
  23. data/lib/rack/sendfile.rb +8 -6
  24. data/lib/rack/server.rb +68 -9
  25. data/lib/rack/session/cookie.rb +1 -10
  26. data/lib/rack/session/memcache.rb +1 -1
  27. data/lib/rack/urlmap.rb +6 -7
  28. data/lib/rack/utils.rb +40 -71
  29. data/rack.gemspec +7 -11
  30. data/spec/cgi/lighttpd.conf +25 -0
  31. data/spec/cgi/rackup_stub.rb +6 -0
  32. data/spec/cgi/sample_rackup.ru +5 -0
  33. data/spec/cgi/test +9 -0
  34. data/spec/cgi/test.fcgi +8 -0
  35. data/spec/cgi/test.ru +5 -0
  36. data/spec/multipart/bad_robots +259 -0
  37. data/spec/multipart/binary +0 -0
  38. data/spec/multipart/empty +10 -0
  39. data/spec/multipart/fail_16384_nofile +814 -0
  40. data/spec/multipart/file1.txt +1 -0
  41. data/spec/multipart/filename_and_modification_param +7 -0
  42. data/spec/multipart/filename_with_escaped_quotes +6 -0
  43. data/spec/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  44. data/spec/multipart/filename_with_percent_escaped_quotes +6 -0
  45. data/spec/multipart/filename_with_unescaped_quotes +6 -0
  46. data/spec/multipart/ie +6 -0
  47. data/spec/multipart/nested +10 -0
  48. data/spec/multipart/none +9 -0
  49. data/spec/multipart/semicolon +6 -0
  50. data/spec/multipart/text +10 -0
  51. data/spec/rackup/config.ru +31 -0
  52. data/{test/spec_rack_auth_basic.rb → spec/spec_auth_basic.rb} +11 -14
  53. data/{test/spec_rack_auth_digest.rb → spec/spec_auth_digest.rb} +18 -27
  54. data/{test/spec_rack_builder.rb → spec/spec_builder.rb} +49 -10
  55. data/{test/spec_rack_cascade.rb → spec/spec_cascade.rb} +7 -10
  56. data/{test/spec_rack_cgi.rb → spec/spec_cgi.rb} +34 -32
  57. data/{test/spec_rack_chunked.rb → spec/spec_chunked.rb} +8 -10
  58. data/{test/spec_rack_commonlogger.rb → spec/spec_commonlogger.rb} +10 -15
  59. data/{test/spec_rack_conditionalget.rb → spec/spec_conditionalget.rb} +5 -7
  60. data/{test/spec_rack_config.rb → spec/spec_config.rb} +6 -7
  61. data/{test/spec_rack_content_length.rb → spec/spec_content_length.rb} +7 -8
  62. data/{test/spec_rack_content_type.rb → spec/spec_content_type.rb} +5 -6
  63. data/{test/spec_rack_deflater.rb → spec/spec_deflater.rb} +11 -13
  64. data/{test/spec_rack_directory.rb → spec/spec_directory.rb} +6 -10
  65. data/{test/spec_rack_etag.rb → spec/spec_etag.rb} +3 -5
  66. data/{test/spec_rack_fastcgi.rb → spec/spec_fastcgi.rb} +36 -29
  67. data/{test/spec_rack_file.rb → spec/spec_file.rb} +9 -13
  68. data/{test/spec_rack_handler.rb → spec/spec_handler.rb} +10 -12
  69. data/{test/spec_rack_head.rb → spec/spec_head.rb} +3 -3
  70. data/{test/spec_rack_lint.rb → spec/spec_lint.rb} +19 -32
  71. data/{test/spec_rack_lobster.rb → spec/spec_lobster.rb} +9 -11
  72. data/{test/spec_rack_lock.rb → spec/spec_lock.rb} +15 -17
  73. data/{test/spec_rack_logger.rb → spec/spec_logger.rb} +6 -7
  74. data/{test/spec_rack_methodoverride.rb → spec/spec_methodoverride.rb} +15 -17
  75. data/{test/spec_rack_mock.rb → spec/spec_mock.rb} +30 -32
  76. data/{test/spec_rack_mongrel.rb → spec/spec_mongrel.rb} +40 -46
  77. data/{test/spec_rack_nulllogger.rb → spec/spec_nulllogger.rb} +4 -5
  78. data/{test/spec_rack_recursive.rb → spec/spec_recursive.rb} +28 -36
  79. data/{test/spec_rack_request.rb → spec/spec_request.rb} +84 -98
  80. data/{test/spec_rack_response.rb → spec/spec_response.rb} +46 -27
  81. data/spec/spec_rewindable_input.rb +118 -0
  82. data/{test/spec_rack_runtime.rb → spec/spec_runtime.rb} +15 -11
  83. data/{test/spec_rack_sendfile.rb → spec/spec_sendfile.rb} +11 -14
  84. data/{test/spec_rack_session_cookie.rb → spec/spec_session_cookie.rb} +14 -36
  85. data/{test/spec_rack_session_memcache.rb → spec/spec_session_memcache.rb} +32 -26
  86. data/{test/spec_rack_session_pool.rb → spec/spec_session_pool.rb} +36 -31
  87. data/spec/spec_showexceptions.rb +23 -0
  88. data/spec/spec_showstatus.rb +79 -0
  89. data/{test/spec_rack_static.rb → spec/spec_static.rb} +5 -9
  90. data/{test/spec_rack_thin.rb → spec/spec_thin.rb} +30 -35
  91. data/{test/spec_rack_urlmap.rb → spec/spec_urlmap.rb} +6 -8
  92. data/{test/spec_rack_utils.rb → spec/spec_utils.rb} +134 -74
  93. data/{test/spec_rack_webrick.rb → spec/spec_webrick.rb} +28 -36
  94. data/spec/testrequest.rb +77 -0
  95. data/spec/unregistered_handler/rack/handler/unregistered.rb +7 -0
  96. data/spec/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  97. metadata +176 -191
  98. data/RDOX +0 -0
  99. data/lib/rack/adapter/camping.rb +0 -22
  100. data/test/spec_auth.rb +0 -57
  101. data/test/spec_rack_camping.rb +0 -55
  102. data/test/spec_rack_rewindable_input.rb +0 -118
  103. data/test/spec_rack_showexceptions.rb +0 -21
  104. data/test/spec_rack_showstatus.rb +0 -72
  105. 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
- context "Rack::Handler::Thin" do
9
- include TestRequest::Helpers
10
-
11
- setup do
12
- @app = Rack::Lint.new(TestRequest.new)
13
- @server = nil
14
- Thin::Logging.silent = true
15
- @thread = Thread.new do
16
- Rack::Handler::Thin.run(@app, :Host => @host='0.0.0.0', :Port => @port=9204) do |server|
17
- @server = server
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
- specify "should respond" do
24
- lambda {
25
- GET("/")
26
- }.should.not.raise
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
- specify "should be a Thin" do
26
+ should "be a Thin" do
30
27
  GET("/")
31
- status.should.be 200
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
- specify "should have rack headers" do
36
+ should "have rack headers" do
40
37
  GET("/")
41
- response["rack.version"].should.equal [0,3]
42
- response["rack.multithread"].should.be false
43
- response["rack.multiprocess"].should.be false
44
- response["rack.run_once"].should.be false
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
- specify "should have CGI headers on GET" do
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
- specify "should have CGI headers on POST" do
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
- specify "should support HTTP auth" do
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
- specify "should set status" do
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
- teardown do
84
- @server.stop!
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
- context "Rack::URLMap" do
7
- specify "dispatches paths correctly" do
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
- specify "dispatches hosts correctly" do
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
- specify "should be nestable" do
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
- specify "should route root apps correctly" do
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
- specify "should not squeeze slashes" do
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
- 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,7 +26,7 @@ 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\"").
@@ -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=").
@@ -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,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
- 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 perform constant time string comparison" do
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
- specify "should return status code for string" do
209
+ should "return status code for string" do
218
210
  Rack::Utils.status_code("200").should.equal 200
219
211
  end
220
212
 
221
- specify "should return status code for symbol" do
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
- context "Rack::Utils::HeaderHash" do
227
- specify "should retain header case" do
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
- specify "should check existence of keys case insensitively" do
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
- specify "should merge case-insensitively" do
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
- 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
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
- specify "should be converted to real Hash" do
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
- specify "should convert Array values to Strings when converting to Hash" do
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
- specify "should replace hashes correctly" do
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
- specify "should be able to delete the given key case-sensitively" do
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
- specify "should be able to delete the given key case-insensitively" do
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
- 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
285
277
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
286
278
  h.delete("Foo").should.equal("bar")
287
279
  end
288
280
 
289
- 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
290
282
  h = Rack::Utils::HeaderHash.new("foo" => "bar")
291
283
  h.delete("Hello").should.be.nil
292
284
  end
293
285
 
294
- specify "should avoid unnecessary object creation if possible" do
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
- specify "should convert Array values to Strings when responding to #each" do
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
- context "Rack::Utils::Context" do
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
- specify "should set context correctly" do
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
- specify "should alter app on recontexting" do
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
- specify "should run different apps" do
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.be 200
350
+ r4.status.should.equal 200
359
351
  r5 = Rack::MockRequest.new(a5).get('/')
360
- r5.status.should.be 200
352
+ r5.status.should.equal 200
361
353
  r4.body.should.equal r5.body
362
354
  end
363
355
  end
364
356
 
365
- context "Rack::Utils::Multipart" do
366
- 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
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
- specify "should parse multipart upload with text file" do
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
- specify "should parse multipart upload with nested parameters" do
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
- specify "should parse multipart upload with binary file" do
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
- specify "should parse multipart upload with empty file" do
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
- specify "should parse multipart upload with filename with semicolons" do
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
- specify "should not include file params if no file was selected" do
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
- specify "should parse IE multipart upload and clean up filename" do
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
- 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
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
- specify "builds multipart body" do
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
- specify "builds nested multipart body" do
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
- 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
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
- 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
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
- specify "should return nil if no UploadedFiles were used" do
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
- specify "should raise ArgumentError if params is not a Hash" do
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