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.
- data/README +23 -26
- data/SPEC +3 -3
- data/lib/rack/auth/digest/params.rb +1 -1
- data/lib/rack/content_length.rb +1 -1
- data/lib/rack/etag.rb +15 -6
- data/lib/rack/file.rb +3 -1
- data/lib/rack/handler/cgi.rb +8 -7
- data/lib/rack/handler/fastcgi.rb +1 -1
- data/lib/rack/handler/lsws.rb +1 -1
- data/lib/rack/handler/mongrel.rb +1 -1
- data/lib/rack/handler/scgi.rb +1 -4
- data/lib/rack/handler/webrick.rb +10 -6
- data/lib/rack/lint.rb +29 -37
- data/lib/rack/mime.rb +2 -0
- data/lib/rack/mock.rb +2 -1
- data/lib/rack/recursive.rb +4 -0
- data/lib/rack/request.rb +8 -6
- data/lib/rack/response.rb +1 -0
- data/lib/rack/rewindable_input.rb +13 -10
- data/lib/rack/sendfile.rb +8 -6
- data/lib/rack/server.rb +66 -7
- data/lib/rack/session/memcache.rb +1 -1
- data/lib/rack/urlmap.rb +6 -7
- data/lib/rack/utils.rb +64 -22
- data/lib/rack.rb +1 -12
- data/rack.gemspec +6 -6
- data/spec/cgi/lighttpd.conf +25 -0
- data/spec/cgi/rackup_stub.rb +6 -0
- data/spec/cgi/sample_rackup.ru +5 -0
- data/spec/cgi/test +9 -0
- data/spec/cgi/test.fcgi +8 -0
- data/spec/cgi/test.ru +5 -0
- data/spec/multipart/bad_robots +259 -0
- data/spec/multipart/binary +0 -0
- data/spec/multipart/empty +10 -0
- data/spec/multipart/fail_16384_nofile +814 -0
- data/spec/multipart/file1.txt +1 -0
- data/spec/multipart/filename_and_modification_param +7 -0
- data/spec/multipart/filename_with_escaped_quotes +6 -0
- data/spec/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
- data/spec/multipart/filename_with_percent_escaped_quotes +6 -0
- data/spec/multipart/filename_with_unescaped_quotes +6 -0
- data/spec/multipart/ie +6 -0
- data/spec/multipart/nested +10 -0
- data/spec/multipart/none +9 -0
- data/spec/multipart/semicolon +6 -0
- data/spec/multipart/text +10 -0
- data/spec/rackup/config.ru +31 -0
- data/{test/spec_rack_auth_basic.rb → spec/spec_auth_basic.rb} +11 -14
- data/{test/spec_rack_auth_digest.rb → spec/spec_auth_digest.rb} +18 -21
- data/{test/spec_rack_builder.rb → spec/spec_builder.rb} +49 -10
- data/{test/spec_rack_cascade.rb → spec/spec_cascade.rb} +7 -10
- data/{test/spec_rack_cgi.rb → spec/spec_cgi.rb} +34 -32
- data/{test/spec_rack_chunked.rb → spec/spec_chunked.rb} +8 -10
- data/{test/spec_rack_commonlogger.rb → spec/spec_commonlogger.rb} +10 -15
- data/{test/spec_rack_conditionalget.rb → spec/spec_conditionalget.rb} +5 -7
- data/{test/spec_rack_config.rb → spec/spec_config.rb} +6 -7
- data/{test/spec_rack_content_length.rb → spec/spec_content_length.rb} +7 -8
- data/{test/spec_rack_content_type.rb → spec/spec_content_type.rb} +5 -6
- data/{test/spec_rack_deflater.rb → spec/spec_deflater.rb} +11 -13
- data/{test/spec_rack_directory.rb → spec/spec_directory.rb} +6 -10
- data/{test/spec_rack_etag.rb → spec/spec_etag.rb} +3 -5
- data/{test/spec_rack_fastcgi.rb → spec/spec_fastcgi.rb} +36 -29
- data/{test/spec_rack_file.rb → spec/spec_file.rb} +9 -13
- data/{test/spec_rack_handler.rb → spec/spec_handler.rb} +10 -12
- data/{test/spec_rack_head.rb → spec/spec_head.rb} +3 -3
- data/{test/spec_rack_lint.rb → spec/spec_lint.rb} +19 -32
- data/{test/spec_rack_lobster.rb → spec/spec_lobster.rb} +9 -11
- data/{test/spec_rack_lock.rb → spec/spec_lock.rb} +15 -17
- data/{test/spec_rack_logger.rb → spec/spec_logger.rb} +6 -7
- data/{test/spec_rack_methodoverride.rb → spec/spec_methodoverride.rb} +15 -17
- data/{test/spec_rack_mock.rb → spec/spec_mock.rb} +30 -32
- data/{test/spec_rack_mongrel.rb → spec/spec_mongrel.rb} +40 -46
- data/{test/spec_rack_nulllogger.rb → spec/spec_nulllogger.rb} +4 -5
- data/{test/spec_rack_recursive.rb → spec/spec_recursive.rb} +28 -36
- data/{test/spec_rack_request.rb → spec/spec_request.rb} +84 -49
- data/{test/spec_rack_response.rb → spec/spec_response.rb} +48 -29
- data/spec/spec_rewindable_input.rb +118 -0
- data/{test/spec_rack_runtime.rb → spec/spec_runtime.rb} +15 -11
- data/{test/spec_rack_sendfile.rb → spec/spec_sendfile.rb} +11 -14
- data/{test/spec_rack_session_cookie.rb → spec/spec_session_cookie.rb} +15 -18
- data/{test/spec_rack_session_memcache.rb → spec/spec_session_memcache.rb} +32 -26
- data/{test/spec_rack_session_pool.rb → spec/spec_session_pool.rb} +36 -31
- data/spec/spec_showexceptions.rb +23 -0
- data/spec/spec_showstatus.rb +79 -0
- data/{test/spec_rack_static.rb → spec/spec_static.rb} +5 -9
- data/{test/spec_rack_thin.rb → spec/spec_thin.rb} +30 -35
- data/{test/spec_rack_urlmap.rb → spec/spec_urlmap.rb} +6 -8
- data/{test/spec_rack_utils.rb → spec/spec_utils.rb} +136 -71
- data/{test/spec_rack_webrick.rb → spec/spec_webrick.rb} +28 -36
- data/spec/testrequest.rb +77 -0
- data/spec/unregistered_handler/rack/handler/unregistered.rb +7 -0
- data/spec/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
- metadata +206 -124
- data/RDOX +0 -0
- data/lib/rack/adapter/camping.rb +0 -22
- data/test/spec_rack_camping.rb +0 -51
- data/test/spec_rack_rewindable_input.rb +0 -118
- data/test/spec_rack_showexceptions.rb +0 -21
- data/test/spec_rack_showstatus.rb +0 -72
- data/test/spec_rackup.rb +0 -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
|
-
|
|
8
|
-
|
|
4
|
+
describe Rack::Utils do
|
|
5
|
+
should "escape correctly" do
|
|
9
6
|
Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
|
|
10
7
|
Rack::Utils.escape("a space").should.equal "a+space"
|
|
11
8
|
Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
|
|
12
9
|
should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
|
|
13
10
|
end
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
should "escape correctly for multibyte characters" do
|
|
16
13
|
matz_name = "\xE3\x81\xBE\xE3\x81\xA4\xE3\x82\x82\xE3\x81\xA8".unpack("a*")[0] # Matsumoto
|
|
17
14
|
matz_name.force_encoding("UTF-8") if matz_name.respond_to? :force_encoding
|
|
18
15
|
Rack::Utils.escape(matz_name).should.equal '%E3%81%BE%E3%81%A4%E3%82%82%E3%81%A8'
|
|
@@ -21,7 +18,7 @@ context "Rack::Utils" do
|
|
|
21
18
|
Rack::Utils.escape(matz_name_sep).should.equal '%E3%81%BE%E3%81%A4+%E3%82%82%E3%81%A8'
|
|
22
19
|
end
|
|
23
20
|
|
|
24
|
-
|
|
21
|
+
should "unescape correctly" do
|
|
25
22
|
Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
|
|
26
23
|
Rack::Utils.unescape("a+space").should.equal "a space"
|
|
27
24
|
Rack::Utils.unescape("a%20space").should.equal "a space"
|
|
@@ -29,11 +26,11 @@ context "Rack::Utils" do
|
|
|
29
26
|
should.equal "q1!2\"'w$5&7/z8)?\\"
|
|
30
27
|
end
|
|
31
28
|
|
|
32
|
-
|
|
29
|
+
should "parse query strings correctly" do
|
|
33
30
|
Rack::Utils.parse_query("foo=bar").
|
|
34
31
|
should.equal "foo" => "bar"
|
|
35
32
|
Rack::Utils.parse_query("foo=\"bar\"").
|
|
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
|
-
|
|
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
|
-
|
|
121
|
+
should "build query strings correctly" do
|
|
125
122
|
Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
|
|
126
123
|
Rack::Utils.build_query("foo" => ["bar", "quux"]).
|
|
127
124
|
should.equal "foo=bar&foo=quux"
|
|
@@ -131,7 +128,7 @@ context "Rack::Utils" do
|
|
|
131
128
|
should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
|
|
132
129
|
end
|
|
133
130
|
|
|
134
|
-
|
|
131
|
+
should "build nested query strings correctly" do
|
|
135
132
|
Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
|
|
136
133
|
Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
|
|
137
134
|
Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
|
|
@@ -178,7 +175,7 @@ context "Rack::Utils" do
|
|
|
178
175
|
message.should.equal "value must be a Hash"
|
|
179
176
|
end
|
|
180
177
|
|
|
181
|
-
|
|
178
|
+
should "figure out which encodings are acceptable" do
|
|
182
179
|
helper = lambda do |a, b|
|
|
183
180
|
request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
|
|
184
181
|
Rack::Utils.select_best_encoding(a, b)
|
|
@@ -201,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
|
-
|
|
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
|
-
|
|
205
|
+
should "return status code for integer" do
|
|
209
206
|
Rack::Utils.status_code(200).should.equal 200
|
|
210
207
|
end
|
|
211
208
|
|
|
212
|
-
|
|
209
|
+
should "return status code for string" do
|
|
213
210
|
Rack::Utils.status_code("200").should.equal 200
|
|
214
211
|
end
|
|
215
212
|
|
|
216
|
-
|
|
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
|
-
|
|
222
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
350
|
+
r4.status.should.equal 200
|
|
354
351
|
r5 = Rack::MockRequest.new(a5).get('/')
|
|
355
|
-
r5.status.should.
|
|
352
|
+
r5.status.should.equal 200
|
|
356
353
|
r4.body.should.equal r5.body
|
|
357
354
|
end
|
|
358
355
|
end
|
|
359
356
|
|
|
360
|
-
|
|
361
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 '
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
18
|
+
should "respond" do
|
|
25
19
|
lambda {
|
|
26
20
|
GET("/test")
|
|
27
21
|
}.should.not.raise
|
|
28
22
|
end
|
|
29
23
|
|
|
30
|
-
|
|
24
|
+
should "be a WEBrick" do
|
|
31
25
|
GET("/test")
|
|
32
|
-
status.should.
|
|
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
|
-
|
|
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
|
|
44
|
-
response["rack.multiprocess"].should.be
|
|
45
|
-
response["rack.run_once"].should.be
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
117
|
+
block_ran.should.be.true
|
|
124
118
|
@s.shutdown
|
|
125
119
|
end
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
@server.shutdown
|
|
129
|
-
end
|
|
121
|
+
@server.shutdown
|
|
130
122
|
end
|