rack 1.1.6 → 1.6.9

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 (212) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +1 -1
  3. data/HISTORY.md +375 -0
  4. data/KNOWN-ISSUES +23 -0
  5. data/README.rdoc +312 -0
  6. data/Rakefile +124 -0
  7. data/SPEC +125 -32
  8. data/contrib/rack.png +0 -0
  9. data/contrib/rack.svg +150 -0
  10. data/contrib/rack_logo.svg +1 -1
  11. data/contrib/rdoc.css +412 -0
  12. data/example/protectedlobster.rb +1 -1
  13. data/lib/rack/auth/abstract/handler.rb +4 -4
  14. data/lib/rack/auth/abstract/request.rb +7 -5
  15. data/lib/rack/auth/basic.rb +1 -1
  16. data/lib/rack/auth/digest/md5.rb +7 -3
  17. data/lib/rack/auth/digest/nonce.rb +1 -1
  18. data/lib/rack/auth/digest/params.rb +7 -9
  19. data/lib/rack/auth/digest/request.rb +10 -9
  20. data/lib/rack/backports/uri/common_18.rb +56 -0
  21. data/lib/rack/backports/uri/common_192.rb +52 -0
  22. data/lib/rack/backports/uri/common_193.rb +29 -0
  23. data/lib/rack/body_proxy.rb +39 -0
  24. data/lib/rack/builder.rb +106 -22
  25. data/lib/rack/cascade.rb +17 -6
  26. data/lib/rack/chunked.rb +44 -24
  27. data/lib/rack/commonlogger.rb +36 -13
  28. data/lib/rack/conditionalget.rb +49 -17
  29. data/lib/rack/config.rb +5 -0
  30. data/lib/rack/content_length.rb +14 -6
  31. data/lib/rack/content_type.rb +7 -1
  32. data/lib/rack/deflater.rb +73 -15
  33. data/lib/rack/directory.rb +18 -8
  34. data/lib/rack/etag.rb +59 -9
  35. data/lib/rack/file.rb +106 -44
  36. data/lib/rack/handler/cgi.rb +11 -11
  37. data/lib/rack/handler/fastcgi.rb +18 -6
  38. data/lib/rack/handler/lsws.rb +2 -4
  39. data/lib/rack/handler/mongrel.rb +22 -6
  40. data/lib/rack/handler/scgi.rb +16 -8
  41. data/lib/rack/handler/thin.rb +19 -4
  42. data/lib/rack/handler/webrick.rb +72 -19
  43. data/lib/rack/handler.rb +47 -14
  44. data/lib/rack/head.rb +10 -2
  45. data/lib/rack/lint.rb +260 -75
  46. data/lib/rack/lobster.rb +13 -8
  47. data/lib/rack/lock.rb +13 -3
  48. data/lib/rack/logger.rb +0 -2
  49. data/lib/rack/methodoverride.rb +27 -8
  50. data/lib/rack/mime.rb +625 -167
  51. data/lib/rack/mock.rb +78 -53
  52. data/lib/rack/multipart/generator.rb +93 -0
  53. data/lib/rack/multipart/parser.rb +253 -0
  54. data/lib/rack/multipart/uploaded_file.rb +34 -0
  55. data/lib/rack/multipart.rb +34 -0
  56. data/lib/rack/nulllogger.rb +21 -2
  57. data/lib/rack/recursive.rb +10 -5
  58. data/lib/rack/reloader.rb +3 -2
  59. data/lib/rack/request.rb +201 -74
  60. data/lib/rack/response.rb +41 -28
  61. data/lib/rack/rewindable_input.rb +15 -11
  62. data/lib/rack/runtime.rb +16 -3
  63. data/lib/rack/sendfile.rb +47 -29
  64. data/lib/rack/server.rb +223 -47
  65. data/lib/rack/session/abstract/id.rb +289 -30
  66. data/lib/rack/session/cookie.rb +133 -44
  67. data/lib/rack/session/memcache.rb +30 -56
  68. data/lib/rack/session/pool.rb +19 -43
  69. data/lib/rack/showexceptions.rb +53 -15
  70. data/lib/rack/showstatus.rb +14 -7
  71. data/lib/rack/static.rb +124 -12
  72. data/lib/rack/tempfile_reaper.rb +22 -0
  73. data/lib/rack/urlmap.rb +49 -15
  74. data/lib/rack/utils/okjson.rb +600 -0
  75. data/lib/rack/utils.rb +363 -361
  76. data/lib/rack.rb +17 -23
  77. data/rack.gemspec +11 -20
  78. data/test/builder/anything.rb +5 -0
  79. data/test/builder/comment.ru +4 -0
  80. data/test/builder/end.ru +5 -0
  81. data/test/builder/line.ru +1 -0
  82. data/test/builder/options.ru +2 -0
  83. data/test/cgi/assets/folder/test.js +1 -0
  84. data/test/cgi/assets/fonts/font.eot +1 -0
  85. data/test/cgi/assets/images/image.png +1 -0
  86. data/test/cgi/assets/index.html +1 -0
  87. data/test/cgi/assets/javascripts/app.js +1 -0
  88. data/test/cgi/assets/stylesheets/app.css +1 -0
  89. data/test/cgi/lighttpd.conf +26 -0
  90. data/test/cgi/rackup_stub.rb +6 -0
  91. data/test/cgi/sample_rackup.ru +5 -0
  92. data/test/cgi/test +9 -0
  93. data/test/cgi/test+directory/test+file +1 -0
  94. data/test/cgi/test.fcgi +8 -0
  95. data/test/cgi/test.ru +5 -0
  96. data/test/gemloader.rb +10 -0
  97. data/test/multipart/bad_robots +259 -0
  98. data/test/multipart/binary +0 -0
  99. data/test/multipart/content_type_and_no_filename +6 -0
  100. data/test/multipart/empty +10 -0
  101. data/test/multipart/fail_16384_nofile +814 -0
  102. data/test/multipart/file1.txt +1 -0
  103. data/test/multipart/filename_and_modification_param +7 -0
  104. data/test/multipart/filename_and_no_name +6 -0
  105. data/test/multipart/filename_with_escaped_quotes +6 -0
  106. data/test/multipart/filename_with_escaped_quotes_and_modification_param +7 -0
  107. data/test/multipart/filename_with_null_byte +7 -0
  108. data/test/multipart/filename_with_percent_escaped_quotes +6 -0
  109. data/test/multipart/filename_with_unescaped_percentages +6 -0
  110. data/test/multipart/filename_with_unescaped_percentages2 +6 -0
  111. data/test/multipart/filename_with_unescaped_percentages3 +6 -0
  112. data/test/multipart/filename_with_unescaped_quotes +6 -0
  113. data/test/multipart/ie +6 -0
  114. data/test/multipart/invalid_character +6 -0
  115. data/test/multipart/mixed_files +21 -0
  116. data/test/multipart/nested +10 -0
  117. data/test/multipart/none +9 -0
  118. data/test/multipart/semicolon +6 -0
  119. data/test/multipart/text +15 -0
  120. data/test/multipart/three_files_three_fields +31 -0
  121. data/test/multipart/webkit +32 -0
  122. data/test/rackup/config.ru +31 -0
  123. data/test/registering_handler/rack/handler/registering_myself.rb +8 -0
  124. data/test/{spec_rack_auth_basic.rb → spec_auth_basic.rb} +23 -15
  125. data/test/{spec_rack_auth_digest.rb → spec_auth_digest.rb} +56 -29
  126. data/test/spec_body_proxy.rb +85 -0
  127. data/test/spec_builder.rb +223 -0
  128. data/test/{spec_rack_cascade.rb → spec_cascade.rb} +28 -15
  129. data/test/{spec_rack_cgi.rb → spec_cgi.rb} +44 -31
  130. data/test/spec_chunked.rb +101 -0
  131. data/test/spec_commonlogger.rb +93 -0
  132. data/test/spec_conditionalget.rb +102 -0
  133. data/test/{spec_rack_config.rb → spec_config.rb} +6 -8
  134. data/test/spec_content_length.rb +85 -0
  135. data/test/spec_content_type.rb +45 -0
  136. data/test/spec_deflater.rb +339 -0
  137. data/test/{spec_rack_directory.rb → spec_directory.rb} +37 -10
  138. data/test/spec_etag.rb +107 -0
  139. data/test/{spec_rack_fastcgi.rb → spec_fastcgi.rb} +47 -29
  140. data/test/spec_file.rb +221 -0
  141. data/test/spec_handler.rb +72 -0
  142. data/test/spec_head.rb +45 -0
  143. data/test/{spec_rack_lint.rb → spec_lint.rb} +82 -60
  144. data/test/spec_lobster.rb +58 -0
  145. data/test/spec_lock.rb +164 -0
  146. data/test/spec_logger.rb +23 -0
  147. data/test/spec_methodoverride.rb +95 -0
  148. data/test/spec_mime.rb +51 -0
  149. data/test/{spec_rack_mock.rb → spec_mock.rb} +92 -38
  150. data/test/{spec_rack_mongrel.rb → spec_mongrel.rb} +46 -53
  151. data/test/spec_multipart.rb +600 -0
  152. data/test/spec_nulllogger.rb +20 -0
  153. data/test/spec_recursive.rb +72 -0
  154. data/test/spec_request.rb +1227 -0
  155. data/test/spec_response.rb +407 -0
  156. data/test/spec_rewindable_input.rb +118 -0
  157. data/test/spec_runtime.rb +49 -0
  158. data/test/spec_sendfile.rb +130 -0
  159. data/test/spec_server.rb +167 -0
  160. data/test/spec_session_abstract_id.rb +53 -0
  161. data/test/spec_session_cookie.rb +410 -0
  162. data/test/{spec_rack_session_memcache.rb → spec_session_memcache.rb} +119 -71
  163. data/test/{spec_rack_session_pool.rb → spec_session_pool.rb} +106 -69
  164. data/test/spec_showexceptions.rb +85 -0
  165. data/test/spec_showstatus.rb +103 -0
  166. data/test/spec_static.rb +145 -0
  167. data/test/spec_tempfile_reaper.rb +63 -0
  168. data/test/{spec_rack_thin.rb → spec_thin.rb} +35 -35
  169. data/test/{spec_rack_urlmap.rb → spec_urlmap.rb} +40 -19
  170. data/test/spec_utils.rb +647 -0
  171. data/test/spec_version.rb +17 -0
  172. data/test/spec_webrick.rb +184 -0
  173. data/test/static/another/index.html +1 -0
  174. data/test/static/index.html +1 -0
  175. data/test/testrequest.rb +78 -0
  176. data/test/unregistered_handler/rack/handler/unregistered.rb +7 -0
  177. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +7 -0
  178. metadata +220 -239
  179. data/RDOX +0 -0
  180. data/README +0 -592
  181. data/lib/rack/adapter/camping.rb +0 -22
  182. data/test/spec_auth.rb +0 -57
  183. data/test/spec_rack_builder.rb +0 -84
  184. data/test/spec_rack_camping.rb +0 -55
  185. data/test/spec_rack_chunked.rb +0 -62
  186. data/test/spec_rack_commonlogger.rb +0 -61
  187. data/test/spec_rack_conditionalget.rb +0 -41
  188. data/test/spec_rack_content_length.rb +0 -43
  189. data/test/spec_rack_content_type.rb +0 -30
  190. data/test/spec_rack_deflater.rb +0 -127
  191. data/test/spec_rack_etag.rb +0 -17
  192. data/test/spec_rack_file.rb +0 -75
  193. data/test/spec_rack_handler.rb +0 -43
  194. data/test/spec_rack_head.rb +0 -30
  195. data/test/spec_rack_lobster.rb +0 -45
  196. data/test/spec_rack_lock.rb +0 -38
  197. data/test/spec_rack_logger.rb +0 -21
  198. data/test/spec_rack_methodoverride.rb +0 -60
  199. data/test/spec_rack_nulllogger.rb +0 -13
  200. data/test/spec_rack_recursive.rb +0 -77
  201. data/test/spec_rack_request.rb +0 -594
  202. data/test/spec_rack_response.rb +0 -221
  203. data/test/spec_rack_rewindable_input.rb +0 -118
  204. data/test/spec_rack_runtime.rb +0 -35
  205. data/test/spec_rack_sendfile.rb +0 -86
  206. data/test/spec_rack_session_cookie.rb +0 -92
  207. data/test/spec_rack_showexceptions.rb +0 -21
  208. data/test/spec_rack_showstatus.rb +0 -72
  209. data/test/spec_rack_static.rb +0 -37
  210. data/test/spec_rack_utils.rb +0 -557
  211. data/test/spec_rack_webrick.rb +0 -130
  212. data/test/spec_rackup.rb +0 -164
@@ -1,15 +1,14 @@
1
- require 'test/spec'
2
1
  require 'stringio'
3
-
2
+ require 'tempfile'
4
3
  require 'rack/lint'
5
4
  require 'rack/mock'
6
5
 
7
- context "Rack::Lint" do
6
+ describe Rack::Lint do
8
7
  def env(*args)
9
8
  Rack::MockRequest.env_for("/", *args)
10
9
  end
11
10
 
12
- specify "passes valid request" do
11
+ should "pass valid request" do
13
12
  lambda {
14
13
  Rack::Lint.new(lambda { |env|
15
14
  [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
@@ -17,12 +16,12 @@ context "Rack::Lint" do
17
16
  }.should.not.raise
18
17
  end
19
18
 
20
- specify "notices fatal errors" do
19
+ should "notice fatal errors" do
21
20
  lambda { Rack::Lint.new(nil).call }.should.raise(Rack::Lint::LintError).
22
21
  message.should.match(/No env given/)
23
22
  end
24
23
 
25
- specify "notices environment errors" do
24
+ should "notice environment errors" do
26
25
  lambda { Rack::Lint.new(nil).call 5 }.should.raise(Rack::Lint::LintError).
27
26
  message.should.match(/not a Hash/)
28
27
 
@@ -76,6 +75,23 @@ context "Rack::Lint" do
76
75
  }.should.raise(Rack::Lint::LintError).
77
76
  message.should.equal("logger [] must respond to info")
78
77
 
78
+ lambda {
79
+ Rack::Lint.new(nil).call(env("rack.multipart.buffer_size" => 0))
80
+ }.should.raise(Rack::Lint::LintError).
81
+ message.should.equal("rack.multipart.buffer_size must be an Integer > 0 if specified")
82
+
83
+ lambda {
84
+ Rack::Lint.new(nil).call(env("rack.multipart.tempfile_factory" => Tempfile))
85
+ }.should.raise(Rack::Lint::LintError).
86
+ message.should.equal("rack.multipart.tempfile_factory must respond to #call")
87
+
88
+ lambda {
89
+ Rack::Lint.new(lambda { |env|
90
+ env['rack.multipart.tempfile_factory'].call("testfile", "text/plain")
91
+ }).call(env("rack.multipart.tempfile_factory" => lambda { |filename, content_type| Object.new }))
92
+ }.should.raise(Rack::Lint::LintError).
93
+ message.should.equal("rack.multipart.tempfile_factory return value must respond to #<<")
94
+
79
95
  lambda {
80
96
  Rack::Lint.new(nil).call(env("REQUEST_METHOD" => "FUCKUP?"))
81
97
  }.should.raise(Rack::Lint::LintError).
@@ -110,7 +126,7 @@ context "Rack::Lint" do
110
126
  message.should.match(/cannot be .* make it ''/)
111
127
  end
112
128
 
113
- specify "notices input errors" do
129
+ should "notice input errors" do
114
130
  lambda {
115
131
  Rack::Lint.new(nil).call(env("rack.input" => ""))
116
132
  }.should.raise(Rack::Lint::LintError).
@@ -139,14 +155,14 @@ context "Rack::Lint" do
139
155
  message.should.match(/does not have ASCII-8BIT as its external encoding/)
140
156
  end
141
157
 
142
- specify "notices error errors" do
158
+ should "notice error errors" do
143
159
  lambda {
144
160
  Rack::Lint.new(nil).call(env("rack.errors" => ""))
145
161
  }.should.raise(Rack::Lint::LintError).
146
162
  message.should.match(/does not respond to #puts/)
147
163
  end
148
164
 
149
- specify "notices status errors" do
165
+ should "notice status errors" do
150
166
  lambda {
151
167
  Rack::Lint.new(lambda { |env|
152
168
  ["cc", {}, ""]
@@ -162,7 +178,7 @@ context "Rack::Lint" do
162
178
  message.should.match(/must be >=100 seen as integer/)
163
179
  end
164
180
 
165
- specify "notices header errors" do
181
+ should "notice header errors" do
166
182
  lambda {
167
183
  Rack::Lint.new(lambda { |env|
168
184
  [200, Object.new, []]
@@ -184,26 +200,36 @@ context "Rack::Lint" do
184
200
  }.should.raise(Rack::Lint::LintError).
185
201
  message.should.match(/must not contain Status/)
186
202
 
187
- lambda {
188
- Rack::Lint.new(lambda { |env|
189
- [200, {"Content-Type:" => "text/plain"}, []]
190
- }).call(env({}))
191
- }.should.raise(Rack::Lint::LintError).
192
- message.should.match(/must not contain :/)
193
-
194
- lambda {
195
- Rack::Lint.new(lambda { |env|
196
- [200, {"Content-" => "text/plain"}, []]
197
- }).call(env({}))
198
- }.should.raise(Rack::Lint::LintError).
199
- message.should.match(/must not end/)
200
-
201
- lambda {
202
- Rack::Lint.new(lambda { |env|
203
- [200, {"..%%quark%%.." => "text/plain"}, []]
204
- }).call(env({}))
205
- }.should.raise(Rack::Lint::LintError).
206
- message.should.equal("invalid header name: ..%%quark%%..")
203
+ # From RFC 7230:<F24><F25>
204
+ # Most HTTP header field values are defined using common syntax
205
+ # components (token, quoted-string, and comment) separated by
206
+ # whitespace or specific delimiting characters. Delimiters are chosen
207
+ # from the set of US-ASCII visual characters not allowed in a token
208
+ # (DQUOTE and "(),/:;<=>?@[\]{}").
209
+ #
210
+ # token = 1*tchar
211
+ #
212
+ # tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
213
+ # / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
214
+ # / DIGIT / ALPHA
215
+ # ; any VCHAR, except delimiters
216
+ invalid_headers = 0.upto(31).map(&:chr) + %W<( ) , / : ; < = > ? @ [ \\ ] { } \x7F>
217
+ invalid_headers.each do |invalid_header|
218
+ lambda {
219
+ Rack::Lint.new(lambda { |env|
220
+ [200, {invalid_header => "text/plain"}, []]
221
+ }).call(env({}))
222
+ }.should.raise(Rack::Lint::LintError, "on invalid header: #{invalid_header}").
223
+ message.should.equal("invalid header name: #{invalid_header}")
224
+ end
225
+ valid_headers = 0.upto(127).map(&:chr) - invalid_headers
226
+ valid_headers.each do |valid_header|
227
+ lambda {
228
+ Rack::Lint.new(lambda { |env|
229
+ [200, {valid_header => "text/plain"}, []]
230
+ }).call(env({}))
231
+ }.should.not.raise(Rack::Lint::LintError, "on valid header: #{valid_header}")
232
+ end
207
233
 
208
234
  lambda {
209
235
  Rack::Lint.new(lambda { |env|
@@ -233,17 +259,24 @@ context "Rack::Lint" do
233
259
  [200, {"Foo-Bar" => "one\ntwo\nthree", "Content-Length" => "0", "Content-Type" => "text/plain" }, []]
234
260
  }).call(env({}))
235
261
  }.should.not.raise(Rack::Lint::LintError)
236
- end
237
262
 
238
- specify "notices content-type errors" do
263
+ # non-Hash header responses should be allowed
239
264
  lambda {
240
265
  Rack::Lint.new(lambda { |env|
241
- [200, {"Content-length" => "0"}, []]
266
+ [200, [%w(Content-Type text/plain), %w(Content-Length 0)], []]
242
267
  }).call(env({}))
243
- }.should.raise(Rack::Lint::LintError).
244
- message.should.match(/No Content-Type/)
268
+ }.should.not.raise(TypeError)
269
+ end
270
+
271
+ should "notice content-type errors" do
272
+ # lambda {
273
+ # Rack::Lint.new(lambda { |env|
274
+ # [200, {"Content-length" => "0"}, []]
275
+ # }).call(env({}))
276
+ # }.should.raise(Rack::Lint::LintError).
277
+ # message.should.match(/No Content-Type/)
245
278
 
246
- [100, 101, 204, 304].each do |status|
279
+ [100, 101, 204, 205, 304].each do |status|
247
280
  lambda {
248
281
  Rack::Lint.new(lambda { |env|
249
282
  [status, {"Content-type" => "text/plain", "Content-length" => "0"}, []]
@@ -253,8 +286,8 @@ context "Rack::Lint" do
253
286
  end
254
287
  end
255
288
 
256
- specify "notices content-length errors" do
257
- [100, 101, 204, 304].each do |status|
289
+ should "notice content-length errors" do
290
+ [100, 101, 204, 205, 304].each do |status|
258
291
  lambda {
259
292
  Rack::Lint.new(lambda { |env|
260
293
  [status, {"Content-length" => "0"}, []]
@@ -266,22 +299,22 @@ context "Rack::Lint" do
266
299
  lambda {
267
300
  Rack::Lint.new(lambda { |env|
268
301
  [200, {"Content-type" => "text/plain", "Content-Length" => "1"}, []]
269
- }).call(env({}))
302
+ }).call(env({}))[2].each { }
270
303
  }.should.raise(Rack::Lint::LintError).
271
304
  message.should.match(/Content-Length header was 1, but should be 0/)
272
305
  end
273
306
 
274
- specify "notices body errors" do
307
+ should "notice body errors" do
275
308
  lambda {
276
- status, header, body = Rack::Lint.new(lambda { |env|
309
+ body = Rack::Lint.new(lambda { |env|
277
310
  [200, {"Content-type" => "text/plain","Content-length" => "3"}, [1,2,3]]
278
- }).call(env({}))
311
+ }).call(env({}))[2]
279
312
  body.each { |part| }
280
313
  }.should.raise(Rack::Lint::LintError).
281
314
  message.should.match(/yielded non-string/)
282
315
  end
283
316
 
284
- specify "notices input handling errors" do
317
+ should "notice input handling errors" do
285
318
  lambda {
286
319
  Rack::Lint.new(lambda { |env|
287
320
  env["rack.input"].gets("\r\n")
@@ -425,7 +458,7 @@ context "Rack::Lint" do
425
458
  message.should.match(/close must not be called/)
426
459
  end
427
460
 
428
- specify "notices error handling errors" do
461
+ should "notice error handling errors" do
429
462
  lambda {
430
463
  Rack::Lint.new(lambda { |env|
431
464
  env["rack.errors"].write(42)
@@ -443,7 +476,7 @@ context "Rack::Lint" do
443
476
  message.should.match(/close must not be called/)
444
477
  end
445
478
 
446
- specify "notices HEAD errors" do
479
+ should "notice HEAD errors" do
447
480
  lambda {
448
481
  Rack::Lint.new(lambda { |env|
449
482
  [200, {"Content-type" => "test/plain", "Content-length" => "3"}, []]
@@ -453,12 +486,12 @@ context "Rack::Lint" do
453
486
  lambda {
454
487
  Rack::Lint.new(lambda { |env|
455
488
  [200, {"Content-type" => "test/plain", "Content-length" => "3"}, ["foo"]]
456
- }).call(env({"REQUEST_METHOD" => "HEAD"}))
489
+ }).call(env({"REQUEST_METHOD" => "HEAD"}))[2].each { }
457
490
  }.should.raise(Rack::Lint::LintError).
458
491
  message.should.match(/body was given for HEAD/)
459
492
  end
460
493
 
461
- specify "passes valid read calls" do
494
+ should "pass valid read calls" do
462
495
  hello_str = "hello world"
463
496
  hello_str.force_encoding("ASCII-8BIT") if hello_str.respond_to? :force_encoding
464
497
  lambda {
@@ -505,19 +538,8 @@ context "Rack::Lint" do
505
538
  end
506
539
  end
507
540
 
508
- context "Rack::Lint::InputWrapper" do
509
- specify "delegates :size to underlying IO object" do
510
- class IOMock
511
- def size
512
- 101
513
- end
514
- end
515
-
516
- wrapper = Rack::Lint::InputWrapper.new(IOMock.new)
517
- wrapper.size.should == 101
518
- end
519
-
520
- specify "delegates :rewind to underlying IO object" do
541
+ describe "Rack::Lint::InputWrapper" do
542
+ should "delegate :rewind to underlying IO object" do
521
543
  io = StringIO.new("123")
522
544
  wrapper = Rack::Lint::InputWrapper.new(io)
523
545
  wrapper.read.should.equal "123"
@@ -0,0 +1,58 @@
1
+ require 'rack/lobster'
2
+ require 'rack/lint'
3
+ require 'rack/mock'
4
+
5
+ module LobsterHelpers
6
+ def lobster
7
+ Rack::MockRequest.new Rack::Lint.new(Rack::Lobster.new)
8
+ end
9
+
10
+ def lambda_lobster
11
+ Rack::MockRequest.new Rack::Lint.new(Rack::Lobster::LambdaLobster)
12
+ end
13
+ end
14
+
15
+ describe Rack::Lobster::LambdaLobster do
16
+ extend LobsterHelpers
17
+
18
+ should "be a single lambda" do
19
+ Rack::Lobster::LambdaLobster.should.be.kind_of Proc
20
+ end
21
+
22
+ should "look like a lobster" do
23
+ res = lambda_lobster.get("/")
24
+ res.should.be.ok
25
+ res.body.should.include "(,(,,(,,,("
26
+ res.body.should.include "?flip"
27
+ end
28
+
29
+ should "be flippable" do
30
+ res = lambda_lobster.get("/?flip")
31
+ res.should.be.ok
32
+ res.body.should.include "(,,,(,,(,("
33
+ end
34
+ end
35
+
36
+ describe Rack::Lobster do
37
+ extend LobsterHelpers
38
+
39
+ should "look like a lobster" do
40
+ res = lobster.get("/")
41
+ res.should.be.ok
42
+ res.body.should.include "(,(,,(,,,("
43
+ res.body.should.include "?flip"
44
+ res.body.should.include "crash"
45
+ end
46
+
47
+ should "be flippable" do
48
+ res = lobster.get("/?flip=left")
49
+ res.should.be.ok
50
+ res.body.should.include "),,,),,),)"
51
+ end
52
+
53
+ should "provide crashing for testing purposes" do
54
+ lambda {
55
+ lobster.get("/?flip=crash")
56
+ }.should.raise
57
+ end
58
+ end
data/test/spec_lock.rb ADDED
@@ -0,0 +1,164 @@
1
+ require 'rack/lint'
2
+ require 'rack/lock'
3
+ require 'rack/mock'
4
+
5
+ class Lock
6
+ attr_reader :synchronized
7
+
8
+ def initialize
9
+ @synchronized = false
10
+ end
11
+
12
+ def synchronize
13
+ @synchronized = true
14
+ yield
15
+ end
16
+
17
+ def lock
18
+ @synchronized = true
19
+ end
20
+
21
+ def unlock
22
+ @synchronized = false
23
+ end
24
+ end
25
+
26
+ module LockHelpers
27
+ def lock_app(app, lock = Lock.new)
28
+ app = if lock
29
+ Rack::Lock.new app, lock
30
+ else
31
+ Rack::Lock.new app
32
+ end
33
+ Rack::Lint.new app
34
+ end
35
+ end
36
+
37
+ describe Rack::Lock do
38
+ extend LockHelpers
39
+
40
+ describe 'Proxy' do
41
+ extend LockHelpers
42
+
43
+ should 'delegate each' do
44
+ env = Rack::MockRequest.env_for("/")
45
+ response = Class.new {
46
+ attr_accessor :close_called
47
+ def initialize; @close_called = false; end
48
+ def each; %w{ hi mom }.each { |x| yield x }; end
49
+ }.new
50
+
51
+ app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] })
52
+ response = app.call(env)[2]
53
+ list = []
54
+ response.each { |x| list << x }
55
+ list.should.equal %w{ hi mom }
56
+ end
57
+
58
+ should 'delegate to_path' do
59
+ lock = Lock.new
60
+ env = Rack::MockRequest.env_for("/")
61
+
62
+ res = ['Hello World']
63
+ def res.to_path ; "/tmp/hello.txt" ; end
64
+
65
+ app = Rack::Lock.new(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] }, lock)
66
+ body = app.call(env)[2]
67
+
68
+ body.should.respond_to :to_path
69
+ body.to_path.should.equal "/tmp/hello.txt"
70
+ end
71
+
72
+ should 'not delegate to_path if body does not implement it' do
73
+ env = Rack::MockRequest.env_for("/")
74
+
75
+ res = ['Hello World']
76
+
77
+ app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, res] })
78
+ body = app.call(env)[2]
79
+
80
+ body.should.not.respond_to :to_path
81
+ end
82
+ end
83
+
84
+ should 'call super on close' do
85
+ env = Rack::MockRequest.env_for("/")
86
+ response = Class.new {
87
+ attr_accessor :close_called
88
+ def initialize; @close_called = false; end
89
+ def close; @close_called = true; end
90
+ }.new
91
+
92
+ app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] })
93
+ app.call(env)
94
+ response.close_called.should.equal false
95
+ response.close
96
+ response.close_called.should.equal true
97
+ end
98
+
99
+ should "not unlock until body is closed" do
100
+ lock = Lock.new
101
+ env = Rack::MockRequest.env_for("/")
102
+ response = Object.new
103
+ app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, response] }, lock)
104
+ lock.synchronized.should.equal false
105
+ response = app.call(env)[2]
106
+ lock.synchronized.should.equal true
107
+ response.close
108
+ lock.synchronized.should.equal false
109
+ end
110
+
111
+ should "return value from app" do
112
+ env = Rack::MockRequest.env_for("/")
113
+ body = [200, {"Content-Type" => "text/plain"}, %w{ hi mom }]
114
+ app = lock_app(lambda { |inner_env| body })
115
+
116
+ res = app.call(env)
117
+ res[0].should.equal body[0]
118
+ res[1].should.equal body[1]
119
+ res[2].to_enum.to_a.should.equal ["hi", "mom"]
120
+ end
121
+
122
+ should "call synchronize on lock" do
123
+ lock = Lock.new
124
+ env = Rack::MockRequest.env_for("/")
125
+ app = lock_app(lambda { |inner_env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] }, lock)
126
+ lock.synchronized.should.equal false
127
+ app.call(env)
128
+ lock.synchronized.should.equal true
129
+ end
130
+
131
+ should "unlock if the app raises" do
132
+ lock = Lock.new
133
+ env = Rack::MockRequest.env_for("/")
134
+ app = lock_app(lambda { raise Exception }, lock)
135
+ lambda { app.call(env) }.should.raise(Exception)
136
+ lock.synchronized.should.equal false
137
+ end
138
+
139
+ should "unlock if the app throws" do
140
+ lock = Lock.new
141
+ env = Rack::MockRequest.env_for("/")
142
+ app = lock_app(lambda {|_| throw :bacon }, lock)
143
+ lambda { app.call(env) }.should.throw(:bacon)
144
+ lock.synchronized.should.equal false
145
+ end
146
+
147
+ should "set multithread flag to false" do
148
+ app = lock_app(lambda { |env|
149
+ env['rack.multithread'].should.equal false
150
+ [200, {"Content-Type" => "text/plain"}, %w{ a b c }]
151
+ }, false)
152
+ app.call(Rack::MockRequest.env_for("/"))
153
+ end
154
+
155
+ should "reset original multithread flag when exiting lock" do
156
+ app = Class.new(Rack::Lock) {
157
+ def call(env)
158
+ env['rack.multithread'].should.equal true
159
+ super
160
+ end
161
+ }.new(lambda { |env| [200, {"Content-Type" => "text/plain"}, %w{ a b c }] })
162
+ Rack::Lint.new(app).call(Rack::MockRequest.env_for("/"))
163
+ end
164
+ end
@@ -0,0 +1,23 @@
1
+ require 'stringio'
2
+ require 'rack/lint'
3
+ require 'rack/logger'
4
+ require 'rack/mock'
5
+
6
+ describe Rack::Logger do
7
+ app = lambda { |env|
8
+ log = env['rack.logger']
9
+ log.debug("Created logger")
10
+ log.info("Program started")
11
+ log.warn("Nothing to do!")
12
+
13
+ [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]]
14
+ }
15
+
16
+ should "conform to Rack::Lint" do
17
+ errors = StringIO.new
18
+ a = Rack::Lint.new(Rack::Logger.new(app))
19
+ Rack::MockRequest.new(a).get('/', 'rack.errors' => errors)
20
+ errors.string.should.match(/INFO -- : Program started/)
21
+ errors.string.should.match(/WARN -- : Nothing to do/)
22
+ end
23
+ end
@@ -0,0 +1,95 @@
1
+ require 'stringio'
2
+ require 'rack/methodoverride'
3
+ require 'rack/mock'
4
+
5
+ describe Rack::MethodOverride do
6
+ def app
7
+ Rack::Lint.new(Rack::MethodOverride.new(lambda {|e|
8
+ [200, {"Content-Type" => "text/plain"}, []]
9
+ }))
10
+ end
11
+
12
+ should "not affect GET requests" do
13
+ env = Rack::MockRequest.env_for("/?_method=delete", :method => "GET")
14
+ app.call env
15
+
16
+ env["REQUEST_METHOD"].should.equal "GET"
17
+ end
18
+
19
+ should "modify REQUEST_METHOD for POST requests when _method parameter is set" do
20
+ env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=put")
21
+ app.call env
22
+
23
+ env["REQUEST_METHOD"].should.equal "PUT"
24
+ end
25
+
26
+ should "modify REQUEST_METHOD for POST requests when X-HTTP-Method-Override is set" do
27
+ env = Rack::MockRequest.env_for("/",
28
+ :method => "POST",
29
+ "HTTP_X_HTTP_METHOD_OVERRIDE" => "PATCH"
30
+ )
31
+ app.call env
32
+
33
+ env["REQUEST_METHOD"].should.equal "PATCH"
34
+ end
35
+
36
+ should "not modify REQUEST_METHOD if the method is unknown" do
37
+ env = Rack::MockRequest.env_for("/", :method => "POST", :input => "_method=foo")
38
+ app.call env
39
+
40
+ env["REQUEST_METHOD"].should.equal "POST"
41
+ end
42
+
43
+ should "not modify REQUEST_METHOD when _method is nil" do
44
+ env = Rack::MockRequest.env_for("/", :method => "POST", :input => "foo=bar")
45
+ app.call env
46
+
47
+ env["REQUEST_METHOD"].should.equal "POST"
48
+ end
49
+
50
+ should "store the original REQUEST_METHOD prior to overriding" do
51
+ env = Rack::MockRequest.env_for("/",
52
+ :method => "POST",
53
+ :input => "_method=options")
54
+ app.call env
55
+
56
+ env["rack.methodoverride.original_method"].should.equal "POST"
57
+ end
58
+
59
+ should "not modify REQUEST_METHOD when given invalid multipart form data" do
60
+ input = <<EOF
61
+ --AaB03x\r
62
+ content-disposition: form-data; name="huge"; filename="huge"\r
63
+ EOF
64
+ env = Rack::MockRequest.env_for("/",
65
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
66
+ "CONTENT_LENGTH" => input.size.to_s,
67
+ :method => "POST", :input => input)
68
+ app.call env
69
+
70
+ env["REQUEST_METHOD"].should.equal "POST"
71
+ end
72
+
73
+ should "write error to RACK_ERRORS when given invalid multipart form data" do
74
+ input = <<EOF
75
+ --AaB03x\r
76
+ content-disposition: form-data; name="huge"; filename="huge"\r
77
+ EOF
78
+ env = Rack::MockRequest.env_for("/",
79
+ "CONTENT_TYPE" => "multipart/form-data, boundary=AaB03x",
80
+ "CONTENT_LENGTH" => input.size.to_s,
81
+ "rack.errors" => StringIO.new,
82
+ :method => "POST", :input => input)
83
+ Rack::MethodOverride.new(proc { [200, {"Content-Type" => "text/plain"}, []] }).call env
84
+
85
+ env["rack.errors"].rewind
86
+ env["rack.errors"].read.should =~ /Bad request content body/
87
+ end
88
+
89
+ should "not modify REQUEST_METHOD for POST requests when the params are unparseable" do
90
+ env = Rack::MockRequest.env_for("/", :method => "POST", :input => "(%bad-params%)")
91
+ app.call env
92
+
93
+ env["REQUEST_METHOD"].should.equal "POST"
94
+ end
95
+ end
data/test/spec_mime.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'rack/mime'
2
+
3
+ describe Rack::Mime do
4
+
5
+ it "should return the fallback mime-type for files with no extension" do
6
+ fallback = 'image/jpg'
7
+ Rack::Mime.mime_type(File.extname('no_ext'), fallback).should.equal fallback
8
+ end
9
+
10
+ it "should always return 'application/octet-stream' for unknown file extensions" do
11
+ unknown_ext = File.extname('unknown_ext.abcdefg')
12
+ Rack::Mime.mime_type(unknown_ext).should.equal 'application/octet-stream'
13
+ end
14
+
15
+ it "should return the mime-type for a given extension" do
16
+ # sanity check. it would be infeasible test every single mime-type.
17
+ Rack::Mime.mime_type(File.extname('image.jpg')).should.equal 'image/jpeg'
18
+ end
19
+
20
+ it "should support null fallbacks" do
21
+ Rack::Mime.mime_type('.nothing', nil).should.equal nil
22
+ end
23
+
24
+ it "should match exact mimes" do
25
+ Rack::Mime.match?('text/html', 'text/html').should.equal true
26
+ Rack::Mime.match?('text/html', 'text/meme').should.equal false
27
+ Rack::Mime.match?('text', 'text').should.equal true
28
+ Rack::Mime.match?('text', 'binary').should.equal false
29
+ end
30
+
31
+ it "should match class wildcard mimes" do
32
+ Rack::Mime.match?('text/html', 'text/*').should.equal true
33
+ Rack::Mime.match?('text/plain', 'text/*').should.equal true
34
+ Rack::Mime.match?('application/json', 'text/*').should.equal false
35
+ Rack::Mime.match?('text/html', 'text').should.equal true
36
+ end
37
+
38
+ it "should match full wildcards" do
39
+ Rack::Mime.match?('text/html', '*').should.equal true
40
+ Rack::Mime.match?('text/plain', '*').should.equal true
41
+ Rack::Mime.match?('text/html', '*/*').should.equal true
42
+ Rack::Mime.match?('text/plain', '*/*').should.equal true
43
+ end
44
+
45
+ it "should match type wildcard mimes" do
46
+ Rack::Mime.match?('text/html', '*/html').should.equal true
47
+ Rack::Mime.match?('text/plain', '*/plain').should.equal true
48
+ end
49
+
50
+ end
51
+