qoobaa-rack 1.0.0.1

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 (116) hide show
  1. data/COPYING +18 -0
  2. data/KNOWN-ISSUES +18 -0
  3. data/RDOX +0 -0
  4. data/README +353 -0
  5. data/Rakefile +164 -0
  6. data/SPEC +164 -0
  7. data/bin/rackup +176 -0
  8. data/contrib/rack_logo.svg +111 -0
  9. data/example/lobster.ru +4 -0
  10. data/example/protectedlobster.rb +14 -0
  11. data/example/protectedlobster.ru +8 -0
  12. data/lib/rack/adapter/camping.rb +22 -0
  13. data/lib/rack/auth/abstract/handler.rb +37 -0
  14. data/lib/rack/auth/abstract/request.rb +37 -0
  15. data/lib/rack/auth/basic.rb +58 -0
  16. data/lib/rack/auth/digest/md5.rb +124 -0
  17. data/lib/rack/auth/digest/nonce.rb +51 -0
  18. data/lib/rack/auth/digest/params.rb +55 -0
  19. data/lib/rack/auth/digest/request.rb +40 -0
  20. data/lib/rack/auth/openid.rb +487 -0
  21. data/lib/rack/builder.rb +63 -0
  22. data/lib/rack/cascade.rb +41 -0
  23. data/lib/rack/chunked.rb +49 -0
  24. data/lib/rack/commonlogger.rb +52 -0
  25. data/lib/rack/conditionalget.rb +47 -0
  26. data/lib/rack/content_length.rb +29 -0
  27. data/lib/rack/content_type.rb +23 -0
  28. data/lib/rack/deflater.rb +96 -0
  29. data/lib/rack/directory.rb +153 -0
  30. data/lib/rack/file.rb +88 -0
  31. data/lib/rack/handler/cgi.rb +61 -0
  32. data/lib/rack/handler/evented_mongrel.rb +8 -0
  33. data/lib/rack/handler/fastcgi.rb +88 -0
  34. data/lib/rack/handler/lsws.rb +60 -0
  35. data/lib/rack/handler/mongrel.rb +87 -0
  36. data/lib/rack/handler/scgi.rb +62 -0
  37. data/lib/rack/handler/swiftiplied_mongrel.rb +8 -0
  38. data/lib/rack/handler/thin.rb +18 -0
  39. data/lib/rack/handler/webrick.rb +71 -0
  40. data/lib/rack/handler.rb +69 -0
  41. data/lib/rack/head.rb +19 -0
  42. data/lib/rack/lint.rb +546 -0
  43. data/lib/rack/lobster.rb +65 -0
  44. data/lib/rack/lock.rb +16 -0
  45. data/lib/rack/methodoverride.rb +27 -0
  46. data/lib/rack/mime.rb +204 -0
  47. data/lib/rack/mock.rb +187 -0
  48. data/lib/rack/recursive.rb +57 -0
  49. data/lib/rack/reloader.rb +107 -0
  50. data/lib/rack/request.rb +248 -0
  51. data/lib/rack/response.rb +183 -0
  52. data/lib/rack/rewindable_input.rb +100 -0
  53. data/lib/rack/session/abstract/id.rb +142 -0
  54. data/lib/rack/session/cookie.rb +91 -0
  55. data/lib/rack/session/memcache.rb +109 -0
  56. data/lib/rack/session/pool.rb +100 -0
  57. data/lib/rack/showexceptions.rb +349 -0
  58. data/lib/rack/showstatus.rb +106 -0
  59. data/lib/rack/static.rb +38 -0
  60. data/lib/rack/urlmap.rb +55 -0
  61. data/lib/rack/utils.rb +528 -0
  62. data/lib/rack.rb +90 -0
  63. data/rack.gemspec +60 -0
  64. data/test/cgi/lighttpd.conf +20 -0
  65. data/test/cgi/test +9 -0
  66. data/test/cgi/test.fcgi +8 -0
  67. data/test/cgi/test.ru +7 -0
  68. data/test/multipart/binary +0 -0
  69. data/test/multipart/empty +10 -0
  70. data/test/multipart/file1.txt +1 -0
  71. data/test/multipart/ie +6 -0
  72. data/test/multipart/nested +10 -0
  73. data/test/multipart/none +9 -0
  74. data/test/multipart/text +10 -0
  75. data/test/spec_rack_auth_basic.rb +73 -0
  76. data/test/spec_rack_auth_digest.rb +226 -0
  77. data/test/spec_rack_auth_openid.rb +84 -0
  78. data/test/spec_rack_builder.rb +84 -0
  79. data/test/spec_rack_camping.rb +51 -0
  80. data/test/spec_rack_cascade.rb +48 -0
  81. data/test/spec_rack_cgi.rb +89 -0
  82. data/test/spec_rack_chunked.rb +62 -0
  83. data/test/spec_rack_commonlogger.rb +61 -0
  84. data/test/spec_rack_conditionalget.rb +41 -0
  85. data/test/spec_rack_content_length.rb +43 -0
  86. data/test/spec_rack_content_type.rb +30 -0
  87. data/test/spec_rack_deflater.rb +127 -0
  88. data/test/spec_rack_directory.rb +61 -0
  89. data/test/spec_rack_fastcgi.rb +89 -0
  90. data/test/spec_rack_file.rb +75 -0
  91. data/test/spec_rack_handler.rb +43 -0
  92. data/test/spec_rack_head.rb +30 -0
  93. data/test/spec_rack_lint.rb +521 -0
  94. data/test/spec_rack_lobster.rb +45 -0
  95. data/test/spec_rack_lock.rb +38 -0
  96. data/test/spec_rack_methodoverride.rb +60 -0
  97. data/test/spec_rack_mock.rb +243 -0
  98. data/test/spec_rack_mongrel.rb +189 -0
  99. data/test/spec_rack_recursive.rb +77 -0
  100. data/test/spec_rack_request.rb +504 -0
  101. data/test/spec_rack_response.rb +218 -0
  102. data/test/spec_rack_rewindable_input.rb +118 -0
  103. data/test/spec_rack_session_cookie.rb +82 -0
  104. data/test/spec_rack_session_memcache.rb +250 -0
  105. data/test/spec_rack_session_pool.rb +172 -0
  106. data/test/spec_rack_showexceptions.rb +21 -0
  107. data/test/spec_rack_showstatus.rb +72 -0
  108. data/test/spec_rack_static.rb +37 -0
  109. data/test/spec_rack_thin.rb +91 -0
  110. data/test/spec_rack_urlmap.rb +185 -0
  111. data/test/spec_rack_utils.rb +467 -0
  112. data/test/spec_rack_webrick.rb +130 -0
  113. data/test/testrequest.rb +57 -0
  114. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  115. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  116. metadata +276 -0
@@ -0,0 +1,467 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/utils'
4
+ require 'rack/lint'
5
+ require 'rack/mock'
6
+
7
+ context "Rack::Utils" do
8
+ specify "should escape correctly" do
9
+ Rack::Utils.escape("fo<o>bar").should.equal "fo%3Co%3Ebar"
10
+ Rack::Utils.escape("a space").should.equal "a+space"
11
+ Rack::Utils.escape("q1!2\"'w$5&7/z8)?\\").
12
+ should.equal "q1%212%22%27w%245%267%2Fz8%29%3F%5C"
13
+ end
14
+
15
+ specify "should unescape correctly" do
16
+ Rack::Utils.unescape("fo%3Co%3Ebar").should.equal "fo<o>bar"
17
+ Rack::Utils.unescape("a+space").should.equal "a space"
18
+ Rack::Utils.unescape("a%20space").should.equal "a space"
19
+ Rack::Utils.unescape("q1%212%22%27w%245%267%2Fz8%29%3F%5C").
20
+ should.equal "q1!2\"'w$5&7/z8)?\\"
21
+ end
22
+
23
+ specify "should parse query strings correctly" do
24
+ Rack::Utils.parse_query("foo=bar").should.equal "foo" => "bar"
25
+ Rack::Utils.parse_query("foo=bar&foo=quux").
26
+ should.equal "foo" => ["bar", "quux"]
27
+ Rack::Utils.parse_query("foo=1&bar=2").
28
+ should.equal "foo" => "1", "bar" => "2"
29
+ Rack::Utils.parse_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
30
+ should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
31
+ Rack::Utils.parse_query("foo%3Dbaz=bar").should.equal "foo=baz" => "bar"
32
+ end
33
+
34
+ specify "should parse nested query strings correctly" do
35
+ Rack::Utils.parse_nested_query("foo").
36
+ should.equal "foo" => nil
37
+ Rack::Utils.parse_nested_query("foo=").
38
+ should.equal "foo" => ""
39
+ Rack::Utils.parse_nested_query("foo=bar").
40
+ should.equal "foo" => "bar"
41
+
42
+ Rack::Utils.parse_nested_query("foo=bar&foo=quux").
43
+ should.equal "foo" => "quux"
44
+ Rack::Utils.parse_nested_query("foo&foo=").
45
+ should.equal "foo" => ""
46
+ Rack::Utils.parse_nested_query("foo=1&bar=2").
47
+ should.equal "foo" => "1", "bar" => "2"
48
+ Rack::Utils.parse_nested_query("&foo=1&&bar=2").
49
+ should.equal "foo" => "1", "bar" => "2"
50
+ Rack::Utils.parse_nested_query("foo&bar=").
51
+ should.equal "foo" => nil, "bar" => ""
52
+ Rack::Utils.parse_nested_query("foo=bar&baz=").
53
+ should.equal "foo" => "bar", "baz" => ""
54
+ Rack::Utils.parse_nested_query("my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F").
55
+ should.equal "my weird field" => "q1!2\"'w$5&7/z8)?"
56
+
57
+ Rack::Utils.parse_nested_query("foo[]").
58
+ should.equal "foo" => [nil]
59
+ Rack::Utils.parse_nested_query("foo[]=").
60
+ should.equal "foo" => [""]
61
+ Rack::Utils.parse_nested_query("foo[]=bar").
62
+ should.equal "foo" => ["bar"]
63
+
64
+ Rack::Utils.parse_nested_query("foo[]=1&foo[]=2").
65
+ should.equal "foo" => ["1", "2"]
66
+ Rack::Utils.parse_nested_query("foo=bar&baz[]=1&baz[]=2&baz[]=3").
67
+ should.equal "foo" => "bar", "baz" => ["1", "2", "3"]
68
+ Rack::Utils.parse_nested_query("foo[]=bar&baz[]=1&baz[]=2&baz[]=3").
69
+ should.equal "foo" => ["bar"], "baz" => ["1", "2", "3"]
70
+
71
+ Rack::Utils.parse_nested_query("x[y][z]=1").
72
+ should.equal "x" => {"y" => {"z" => "1"}}
73
+ Rack::Utils.parse_nested_query("x[y][z][]=1").
74
+ should.equal "x" => {"y" => {"z" => ["1"]}}
75
+ Rack::Utils.parse_nested_query("x[y][z]=1&x[y][z]=2").
76
+ should.equal "x" => {"y" => {"z" => "2"}}
77
+ Rack::Utils.parse_nested_query("x[y][z][]=1&x[y][z][]=2").
78
+ should.equal "x" => {"y" => {"z" => ["1", "2"]}}
79
+
80
+ Rack::Utils.parse_nested_query("x[y][][z]=1").
81
+ should.equal "x" => {"y" => [{"z" => "1"}]}
82
+ Rack::Utils.parse_nested_query("x[y][][z][]=1").
83
+ should.equal "x" => {"y" => [{"z" => ["1"]}]}
84
+ Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=2").
85
+ should.equal "x" => {"y" => [{"z" => "1", "w" => "2"}]}
86
+
87
+ Rack::Utils.parse_nested_query("x[y][][v][w]=1").
88
+ should.equal "x" => {"y" => [{"v" => {"w" => "1"}}]}
89
+ Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][v][w]=2").
90
+ should.equal "x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}
91
+
92
+ Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][z]=2").
93
+ should.equal "x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}
94
+ Rack::Utils.parse_nested_query("x[y][][z]=1&x[y][][w]=a&x[y][][z]=2&x[y][][w]=3").
95
+ should.equal "x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}
96
+
97
+ lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y]z=2") }.
98
+ should.raise(TypeError).
99
+ message.should.equal "expected Hash (got String) for param `y'"
100
+
101
+ lambda { Rack::Utils.parse_nested_query("x[y]=1&x[]=1") }.
102
+ should.raise(TypeError).
103
+ message.should.equal "expected Array (got Hash) for param `x'"
104
+
105
+ lambda { Rack::Utils.parse_nested_query("x[y]=1&x[y][][w]=2") }.
106
+ should.raise(TypeError).
107
+ message.should.equal "expected Array (got String) for param `y'"
108
+ end
109
+
110
+ specify "should build query strings correctly" do
111
+ Rack::Utils.build_query("foo" => "bar").should.equal "foo=bar"
112
+ Rack::Utils.build_query("foo" => ["bar", "quux"]).
113
+ should.equal "foo=bar&foo=quux"
114
+ Rack::Utils.build_query("foo" => "1", "bar" => "2").
115
+ should.equal "foo=1&bar=2"
116
+ Rack::Utils.build_query("my weird field" => "q1!2\"'w$5&7/z8)?").
117
+ should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
118
+ end
119
+
120
+ specify "should build nested query strings correctly" do
121
+ Rack::Utils.build_nested_query("foo" => nil).should.equal "foo"
122
+ Rack::Utils.build_nested_query("foo" => "").should.equal "foo="
123
+ Rack::Utils.build_nested_query("foo" => "bar").should.equal "foo=bar"
124
+
125
+ Rack::Utils.build_nested_query("foo" => "1", "bar" => "2").
126
+ should.equal "foo=1&bar=2"
127
+ Rack::Utils.build_nested_query("my weird field" => "q1!2\"'w$5&7/z8)?").
128
+ should.equal "my+weird+field=q1%212%22%27w%245%267%2Fz8%29%3F"
129
+
130
+ Rack::Utils.build_nested_query("foo" => [nil]).
131
+ should.equal "foo[]"
132
+ Rack::Utils.build_nested_query("foo" => [""]).
133
+ should.equal "foo[]="
134
+ Rack::Utils.build_nested_query("foo" => ["bar"]).
135
+ should.equal "foo[]=bar"
136
+
137
+ # The ordering of the output query string is unpredictable with 1.8's
138
+ # unordered hash. Test that build_nested_query performs the inverse
139
+ # function of parse_nested_query.
140
+ [{"foo" => nil, "bar" => ""},
141
+ {"foo" => "bar", "baz" => ""},
142
+ {"foo" => ["1", "2"]},
143
+ {"foo" => "bar", "baz" => ["1", "2", "3"]},
144
+ {"foo" => ["bar"], "baz" => ["1", "2", "3"]},
145
+ {"foo" => ["1", "2"]},
146
+ {"foo" => "bar", "baz" => ["1", "2", "3"]},
147
+ {"x" => {"y" => {"z" => "1"}}},
148
+ {"x" => {"y" => {"z" => ["1"]}}},
149
+ {"x" => {"y" => {"z" => ["1", "2"]}}},
150
+ {"x" => {"y" => [{"z" => "1"}]}},
151
+ {"x" => {"y" => [{"z" => ["1"]}]}},
152
+ {"x" => {"y" => [{"z" => "1", "w" => "2"}]}},
153
+ {"x" => {"y" => [{"v" => {"w" => "1"}}]}},
154
+ {"x" => {"y" => [{"z" => "1", "v" => {"w" => "2"}}]}},
155
+ {"x" => {"y" => [{"z" => "1"}, {"z" => "2"}]}},
156
+ {"x" => {"y" => [{"z" => "1", "w" => "a"}, {"z" => "2", "w" => "3"}]}}
157
+ ].each { |params|
158
+ qs = Rack::Utils.build_nested_query(params)
159
+ Rack::Utils.parse_nested_query(qs).should.equal params
160
+ }
161
+
162
+ lambda { Rack::Utils.build_nested_query("foo=bar") }.
163
+ should.raise(ArgumentError).
164
+ message.should.equal "value must be a Hash"
165
+ end
166
+
167
+ specify "should figure out which encodings are acceptable" do
168
+ helper = lambda do |a, b|
169
+ request = Rack::Request.new(Rack::MockRequest.env_for("", "HTTP_ACCEPT_ENCODING" => a))
170
+ Rack::Utils.select_best_encoding(a, b)
171
+ end
172
+
173
+ helper.call(%w(), [["x", 1]]).should.equal(nil)
174
+ helper.call(%w(identity), [["identity", 0.0]]).should.equal(nil)
175
+ helper.call(%w(identity), [["*", 0.0]]).should.equal(nil)
176
+
177
+ helper.call(%w(identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("identity")
178
+
179
+ helper.call(%w(compress gzip identity), [["compress", 1.0], ["gzip", 1.0]]).should.equal("compress")
180
+ helper.call(%w(compress gzip identity), [["compress", 0.5], ["gzip", 1.0]]).should.equal("gzip")
181
+
182
+ helper.call(%w(foo bar identity), []).should.equal("identity")
183
+ helper.call(%w(foo bar identity), [["*", 1.0]]).should.equal("foo")
184
+ helper.call(%w(foo bar identity), [["*", 1.0], ["foo", 0.9]]).should.equal("bar")
185
+
186
+ helper.call(%w(foo bar identity), [["foo", 0], ["bar", 0]]).should.equal("identity")
187
+ helper.call(%w(foo bar baz identity), [["*", 0], ["identity", 0.1]]).should.equal("identity")
188
+ end
189
+
190
+ specify "should return the bytesize of String" do
191
+ Rack::Utils.bytesize("FOO\xE2\x82\xAC").should.equal 6
192
+ end
193
+ end
194
+
195
+ context "Rack::Utils::HeaderHash" do
196
+ specify "should retain header case" do
197
+ h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
198
+ h['ETag'] = 'Boo!'
199
+ h.to_hash.should.equal "Content-MD5" => "d5ff4e2a0 ...", "ETag" => 'Boo!'
200
+ end
201
+
202
+ specify "should check existence of keys case insensitively" do
203
+ h = Rack::Utils::HeaderHash.new("Content-MD5" => "d5ff4e2a0 ...")
204
+ h.should.include 'content-md5'
205
+ h.should.not.include 'ETag'
206
+ end
207
+
208
+ specify "should merge case-insensitively" do
209
+ h = Rack::Utils::HeaderHash.new("ETag" => 'HELLO', "content-length" => '123')
210
+ merged = h.merge("Etag" => 'WORLD', 'Content-Length' => '321', "Foo" => 'BAR')
211
+ merged.should.equal "Etag"=>'WORLD', "Content-Length"=>'321', "Foo"=>'BAR'
212
+ end
213
+
214
+ specify "should overwrite case insensitively and assume the new key's case" do
215
+ h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
216
+ h["foo-bar"] = "bizzle"
217
+ h["FOO-BAR"].should.equal "bizzle"
218
+ h.length.should.equal 1
219
+ h.to_hash.should.equal "foo-bar" => "bizzle"
220
+ end
221
+
222
+ specify "should be converted to real Hash" do
223
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
224
+ h.to_hash.should.be.instance_of Hash
225
+ end
226
+
227
+ specify "should convert Array values to Strings when converting to Hash" do
228
+ h = Rack::Utils::HeaderHash.new("foo" => ["bar", "baz"])
229
+ h.to_hash.should.equal({ "foo" => "bar\nbaz" })
230
+ end
231
+
232
+ specify "should replace hashes correctly" do
233
+ h = Rack::Utils::HeaderHash.new("Foo-Bar" => "baz")
234
+ j = {"foo" => "bar"}
235
+ h.replace(j)
236
+ h["foo"].should.equal "bar"
237
+ end
238
+
239
+ specify "should be able to delete the given key case-sensitively" do
240
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
241
+ h.delete("foo")
242
+ h["foo"].should.be.nil
243
+ h["FOO"].should.be.nil
244
+ end
245
+
246
+ specify "should be able to delete the given key case-insensitively" do
247
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
248
+ h.delete("FOO")
249
+ h["foo"].should.be.nil
250
+ h["FOO"].should.be.nil
251
+ end
252
+
253
+ specify "should return the deleted value when #delete is called on an existing key" do
254
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
255
+ h.delete("Foo").should.equal("bar")
256
+ end
257
+
258
+ specify "should return nil when #delete is called on a non-existant key" do
259
+ h = Rack::Utils::HeaderHash.new("foo" => "bar")
260
+ h.delete("Hello").should.be.nil
261
+ end
262
+ end
263
+
264
+ context "Rack::Utils::Context" do
265
+ class ContextTest
266
+ attr_reader :app
267
+ def initialize app; @app=app; end
268
+ def call env; context env; end
269
+ def context env, app=@app; app.call(env); end
270
+ end
271
+ test_target1 = proc{|e| e.to_s+' world' }
272
+ test_target2 = proc{|e| e.to_i+2 }
273
+ test_target3 = proc{|e| nil }
274
+ test_target4 = proc{|e| [200,{'Content-Type'=>'text/plain', 'Content-Length'=>'0'},['']] }
275
+ test_app = ContextTest.new test_target4
276
+
277
+ specify "should set context correctly" do
278
+ test_app.app.should.equal test_target4
279
+ c1 = Rack::Utils::Context.new(test_app, test_target1)
280
+ c1.for.should.equal test_app
281
+ c1.app.should.equal test_target1
282
+ c2 = Rack::Utils::Context.new(test_app, test_target2)
283
+ c2.for.should.equal test_app
284
+ c2.app.should.equal test_target2
285
+ end
286
+
287
+ specify "should alter app on recontexting" do
288
+ c1 = Rack::Utils::Context.new(test_app, test_target1)
289
+ c2 = c1.recontext(test_target2)
290
+ c2.for.should.equal test_app
291
+ c2.app.should.equal test_target2
292
+ c3 = c2.recontext(test_target3)
293
+ c3.for.should.equal test_app
294
+ c3.app.should.equal test_target3
295
+ end
296
+
297
+ specify "should run different apps" do
298
+ c1 = Rack::Utils::Context.new test_app, test_target1
299
+ c2 = c1.recontext test_target2
300
+ c3 = c2.recontext test_target3
301
+ c4 = c3.recontext test_target4
302
+ a4 = Rack::Lint.new c4
303
+ a5 = Rack::Lint.new test_app
304
+ r1 = c1.call('hello')
305
+ r1.should.equal 'hello world'
306
+ r2 = c2.call(2)
307
+ r2.should.equal 4
308
+ r3 = c3.call(:misc_symbol)
309
+ r3.should.be.nil
310
+ r4 = Rack::MockRequest.new(a4).get('/')
311
+ r4.status.should.be 200
312
+ r5 = Rack::MockRequest.new(a5).get('/')
313
+ r5.status.should.be 200
314
+ r4.body.should.equal r5.body
315
+ end
316
+ end
317
+
318
+ context "Rack::Utils::Multipart" do
319
+ specify "should return nil if content type is not multipart" do
320
+ env = Rack::MockRequest.env_for("/",
321
+ "CONTENT_TYPE" => 'application/x-www-form-urlencoded')
322
+ Rack::Utils::Multipart.parse_multipart(env).should.equal nil
323
+ end
324
+
325
+ specify "should parse multipart upload with text file" do
326
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:text))
327
+ params = Rack::Utils::Multipart.parse_multipart(env)
328
+ params["submit-name"].should.equal "Larry"
329
+ params["files"][:type].should.equal "text/plain"
330
+ params["files"][:filename].should.equal "file1.txt"
331
+ params["files"][:head].should.equal "Content-Disposition: form-data; " +
332
+ "name=\"files\"; filename=\"file1.txt\"\r\n" +
333
+ "Content-Type: text/plain\r\n"
334
+ params["files"][:name].should.equal "files"
335
+ params["files"][:tempfile].read.should.equal "contents"
336
+ end
337
+
338
+ specify "should parse multipart upload with nested parameters" do
339
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:nested))
340
+ params = Rack::Utils::Multipart.parse_multipart(env)
341
+ params["foo"]["submit-name"].should.equal "Larry"
342
+ params["foo"]["files"][:type].should.equal "text/plain"
343
+ params["foo"]["files"][:filename].should.equal "file1.txt"
344
+ params["foo"]["files"][:head].should.equal "Content-Disposition: form-data; " +
345
+ "name=\"foo[files]\"; filename=\"file1.txt\"\r\n" +
346
+ "Content-Type: text/plain\r\n"
347
+ params["foo"]["files"][:name].should.equal "foo[files]"
348
+ params["foo"]["files"][:tempfile].read.should.equal "contents"
349
+ end
350
+
351
+ specify "should parse multipart upload with binary file" do
352
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:binary))
353
+ params = Rack::Utils::Multipart.parse_multipart(env)
354
+ params["submit-name"].should.equal "Larry"
355
+ params["files"][:type].should.equal "image/png"
356
+ params["files"][:filename].should.equal "rack-logo.png"
357
+ params["files"][:head].should.equal "Content-Disposition: form-data; " +
358
+ "name=\"files\"; filename=\"rack-logo.png\"\r\n" +
359
+ "Content-Type: image/png\r\n"
360
+ params["files"][:name].should.equal "files"
361
+ params["files"][:tempfile].read.length.should.equal 26473
362
+ end
363
+
364
+ specify "should parse multipart upload with empty file" do
365
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:empty))
366
+ params = Rack::Utils::Multipart.parse_multipart(env)
367
+ params["submit-name"].should.equal "Larry"
368
+ params["files"][:type].should.equal "text/plain"
369
+ params["files"][:filename].should.equal "file1.txt"
370
+ params["files"][:head].should.equal "Content-Disposition: form-data; " +
371
+ "name=\"files\"; filename=\"file1.txt\"\r\n" +
372
+ "Content-Type: text/plain\r\n"
373
+ params["files"][:name].should.equal "files"
374
+ params["files"][:tempfile].read.should.equal ""
375
+ end
376
+
377
+ specify "should not include file params if no file was selected" do
378
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
379
+ params = Rack::Utils::Multipart.parse_multipart(env)
380
+ params["submit-name"].should.equal "Larry"
381
+ params["files"].should.equal nil
382
+ params.keys.should.not.include "files"
383
+ end
384
+
385
+ specify "should parse IE multipart upload and clean up filename" do
386
+ env = Rack::MockRequest.env_for("/", multipart_fixture(:ie))
387
+ params = Rack::Utils::Multipart.parse_multipart(env)
388
+ params["files"][:type].should.equal "text/plain"
389
+ params["files"][:filename].should.equal "file1.txt"
390
+ params["files"][:head].should.equal "Content-Disposition: form-data; " +
391
+ "name=\"files\"; " +
392
+ 'filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"' +
393
+ "\r\nContent-Type: text/plain\r\n"
394
+ params["files"][:name].should.equal "files"
395
+ params["files"][:tempfile].read.should.equal "contents"
396
+ end
397
+
398
+ specify "rewinds input after parsing upload" do
399
+ options = multipart_fixture(:text)
400
+ input = options[:input]
401
+ env = Rack::MockRequest.env_for("/", options)
402
+ params = Rack::Utils::Multipart.parse_multipart(env)
403
+ params["submit-name"].should.equal "Larry"
404
+ params["files"][:filename].should.equal "file1.txt"
405
+ input.read.length.should.equal 197
406
+ end
407
+
408
+ specify "builds multipart body" do
409
+ files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
410
+ data = Rack::Utils::Multipart.build_multipart("submit-name" => "Larry", "files" => files)
411
+
412
+ options = {
413
+ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
414
+ "CONTENT_LENGTH" => data.length.to_s,
415
+ :input => StringIO.new(data)
416
+ }
417
+ env = Rack::MockRequest.env_for("/", options)
418
+ params = Rack::Utils::Multipart.parse_multipart(env)
419
+ params["submit-name"].should.equal "Larry"
420
+ params["files"][:filename].should.equal "file1.txt"
421
+ params["files"][:tempfile].read.should.equal "contents"
422
+ end
423
+
424
+ specify "builds nested multipart body" do
425
+ files = Rack::Utils::Multipart::UploadedFile.new(multipart_file("file1.txt"))
426
+ data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => files}])
427
+
428
+ options = {
429
+ "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x",
430
+ "CONTENT_LENGTH" => data.length.to_s,
431
+ :input => StringIO.new(data)
432
+ }
433
+ env = Rack::MockRequest.env_for("/", options)
434
+ params = Rack::Utils::Multipart.parse_multipart(env)
435
+ params["people"][0]["submit-name"].should.equal "Larry"
436
+ params["people"][0]["files"][:filename].should.equal "file1.txt"
437
+ params["people"][0]["files"][:tempfile].read.should.equal "contents"
438
+ end
439
+
440
+ specify "should return nil if no UploadedFiles were used" do
441
+ data = Rack::Utils::Multipart.build_multipart("people" => [{"submit-name" => "Larry", "files" => "contents"}])
442
+ data.should.equal nil
443
+ end
444
+
445
+ specify "should raise ArgumentError if params is not a Hash" do
446
+ lambda { Rack::Utils::Multipart.build_multipart("foo=bar") }.
447
+ should.raise(ArgumentError).
448
+ message.should.equal "value must be a Hash"
449
+ end
450
+
451
+ private
452
+ def multipart_fixture(name)
453
+ file = multipart_file(name)
454
+ data = File.open(file, 'rb') { |io| io.read }
455
+
456
+ type = "multipart/form-data; boundary=AaB03x"
457
+ length = data.respond_to?(:bytesize) ? data.bytesize : data.size
458
+
459
+ { "CONTENT_TYPE" => type,
460
+ "CONTENT_LENGTH" => length.to_s,
461
+ :input => StringIO.new(data) }
462
+ end
463
+
464
+ def multipart_file(name)
465
+ File.join(File.dirname(__FILE__), "multipart", name.to_s)
466
+ end
467
+ end
@@ -0,0 +1,130 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/handler/webrick'
4
+ require 'rack/lint'
5
+ require 'rack/response'
6
+ require 'testrequest'
7
+
8
+ Thread.abort_on_exception = true
9
+
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
23
+
24
+ specify "should respond" do
25
+ lambda {
26
+ GET("/test")
27
+ }.should.not.raise
28
+ end
29
+
30
+ specify "should be a WEBrick" do
31
+ GET("/test")
32
+ status.should.be 200
33
+ response["SERVER_SOFTWARE"].should =~ /WEBrick/
34
+ response["HTTP_VERSION"].should.equal "HTTP/1.1"
35
+ response["SERVER_PROTOCOL"].should.equal "HTTP/1.1"
36
+ response["SERVER_PORT"].should.equal "9202"
37
+ response["SERVER_NAME"].should.equal "0.0.0.0"
38
+ end
39
+
40
+ specify "should have rack headers" do
41
+ GET("/test")
42
+ response["rack.version"].should.equal [1,0]
43
+ response["rack.multithread"].should.be true
44
+ response["rack.multiprocess"].should.be false
45
+ response["rack.run_once"].should.be false
46
+ end
47
+
48
+ specify "should have CGI headers on GET" do
49
+ GET("/test")
50
+ response["REQUEST_METHOD"].should.equal "GET"
51
+ response["SCRIPT_NAME"].should.equal "/test"
52
+ response["REQUEST_PATH"].should.equal "/"
53
+ response["PATH_INFO"].should.be.nil
54
+ response["QUERY_STRING"].should.equal ""
55
+ response["test.postdata"].should.equal ""
56
+
57
+ GET("/test/foo?quux=1")
58
+ response["REQUEST_METHOD"].should.equal "GET"
59
+ response["SCRIPT_NAME"].should.equal "/test"
60
+ response["REQUEST_PATH"].should.equal "/"
61
+ response["PATH_INFO"].should.equal "/foo"
62
+ response["QUERY_STRING"].should.equal "quux=1"
63
+
64
+ GET("/test/foo%25encoding?quux=1")
65
+ response["REQUEST_METHOD"].should.equal "GET"
66
+ response["SCRIPT_NAME"].should.equal "/test"
67
+ response["REQUEST_PATH"].should.equal "/"
68
+ response["PATH_INFO"].should.equal "/foo%25encoding"
69
+ response["QUERY_STRING"].should.equal "quux=1"
70
+ end
71
+
72
+ specify "should have CGI headers on POST" do
73
+ POST("/test", {"rack-form-data" => "23"}, {'X-test-header' => '42'})
74
+ status.should.equal 200
75
+ response["REQUEST_METHOD"].should.equal "POST"
76
+ response["SCRIPT_NAME"].should.equal "/test"
77
+ response["REQUEST_PATH"].should.equal "/"
78
+ response["QUERY_STRING"].should.equal ""
79
+ response["HTTP_X_TEST_HEADER"].should.equal "42"
80
+ response["test.postdata"].should.equal "rack-form-data=23"
81
+ end
82
+
83
+ specify "should support HTTP auth" do
84
+ GET("/test", {:user => "ruth", :passwd => "secret"})
85
+ response["HTTP_AUTHORIZATION"].should.equal "Basic cnV0aDpzZWNyZXQ="
86
+ end
87
+
88
+ specify "should set status" do
89
+ GET("/test?secret")
90
+ status.should.equal 403
91
+ response["rack.url_scheme"].should.equal "http"
92
+ end
93
+
94
+ specify "should correctly set cookies" do
95
+ @server.mount "/cookie-test", Rack::Handler::WEBrick,
96
+ Rack::Lint.new(lambda { |req|
97
+ res = Rack::Response.new
98
+ res.set_cookie "one", "1"
99
+ res.set_cookie "two", "2"
100
+ res.finish
101
+ })
102
+
103
+ Net::HTTP.start(@host, @port) { |http|
104
+ res = http.get("/cookie-test")
105
+ res.code.to_i.should.equal 200
106
+ res.get_fields("set-cookie").should.equal ["one=1", "two=2"]
107
+ }
108
+ end
109
+
110
+ specify "should provide a .run" do
111
+ block_ran = false
112
+ catch(:done) {
113
+ Rack::Handler::WEBrick.run(lambda {},
114
+ {:Port => 9210,
115
+ :Logger => WEBrick::Log.new(nil, WEBrick::BasicLog::WARN),
116
+ :AccessLog => []}) { |server|
117
+ block_ran = true
118
+ server.should.be.kind_of WEBrick::HTTPServer
119
+ @s = server
120
+ throw :done
121
+ }
122
+ }
123
+ block_ran.should.be true
124
+ @s.shutdown
125
+ end
126
+
127
+ teardown do
128
+ @server.shutdown
129
+ end
130
+ end
@@ -0,0 +1,57 @@
1
+ require 'yaml'
2
+ require 'net/http'
3
+
4
+ class TestRequest
5
+ def call(env)
6
+ status = env["QUERY_STRING"] =~ /secret/ ? 403 : 200
7
+ env["test.postdata"] = env["rack.input"].read
8
+ body = env.to_yaml
9
+ size = body.respond_to?(:bytesize) ? body.bytesize : body.size
10
+ [status, {"Content-Type" => "text/yaml", "Content-Length" => size.to_s}, [body]]
11
+ end
12
+
13
+ module Helpers
14
+ attr_reader :status, :response
15
+
16
+ def GET(path, header={})
17
+ Net::HTTP.start(@host, @port) { |http|
18
+ user = header.delete(:user)
19
+ passwd = header.delete(:passwd)
20
+
21
+ get = Net::HTTP::Get.new(path, header)
22
+ get.basic_auth user, passwd if user && passwd
23
+ http.request(get) { |response|
24
+ @status = response.code.to_i
25
+ @response = YAML.load(response.body)
26
+ }
27
+ }
28
+ end
29
+
30
+ def POST(path, formdata={}, header={})
31
+ Net::HTTP.start(@host, @port) { |http|
32
+ user = header.delete(:user)
33
+ passwd = header.delete(:passwd)
34
+
35
+ post = Net::HTTP::Post.new(path, header)
36
+ post.form_data = formdata
37
+ post.basic_auth user, passwd if user && passwd
38
+ http.request(post) { |response|
39
+ @status = response.code.to_i
40
+ @response = YAML.load(response.body)
41
+ }
42
+ }
43
+ end
44
+ end
45
+ end
46
+
47
+ class StreamingRequest
48
+ def self.call(env)
49
+ [200, {"Content-Type" => "text/plain"}, new]
50
+ end
51
+
52
+ def each
53
+ yield "hello there!\n"
54
+ sleep 5
55
+ yield "that is all.\n"
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ module Handler
3
+ # this class doesn't do anything, we're just seeing if we get it.
4
+ class Unregistered
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Rack
2
+ module Handler
3
+ # this class doesn't do anything, we're just seeing if we get it.
4
+ class UnregisteredLongOne
5
+ end
6
+ end
7
+ end