rack 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rack might be problematic. Click here for more details.

Files changed (187) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +220 -155
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +77 -119
  5. data/Rakefile +25 -18
  6. data/SPEC +3 -4
  7. data/bin/rackup +1 -0
  8. data/example/lobster.ru +2 -0
  9. data/example/protectedlobster.rb +3 -1
  10. data/example/protectedlobster.ru +2 -0
  11. data/lib/rack.rb +63 -60
  12. data/lib/rack/auth/abstract/handler.rb +3 -1
  13. data/lib/rack/auth/abstract/request.rb +2 -0
  14. data/lib/rack/auth/basic.rb +4 -1
  15. data/lib/rack/auth/digest/md5.rb +9 -7
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +4 -2
  18. data/lib/rack/auth/digest/request.rb +2 -0
  19. data/lib/rack/body_proxy.rb +3 -6
  20. data/lib/rack/builder.rb +38 -15
  21. data/lib/rack/cascade.rb +6 -5
  22. data/lib/rack/chunked.rb +29 -6
  23. data/lib/rack/common_logger.rb +9 -8
  24. data/lib/rack/conditional_get.rb +3 -1
  25. data/lib/rack/config.rb +2 -0
  26. data/lib/rack/content_length.rb +3 -1
  27. data/lib/rack/content_type.rb +3 -1
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +28 -17
  30. data/lib/rack/directory.rb +17 -14
  31. data/lib/rack/etag.rb +3 -1
  32. data/lib/rack/events.rb +5 -3
  33. data/lib/rack/file.rb +5 -173
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler.rb +7 -2
  36. data/lib/rack/handler/cgi.rb +3 -1
  37. data/lib/rack/handler/fastcgi.rb +4 -2
  38. data/lib/rack/handler/lsws.rb +3 -1
  39. data/lib/rack/handler/scgi.rb +9 -6
  40. data/lib/rack/handler/thin.rb +3 -1
  41. data/lib/rack/handler/webrick.rb +4 -2
  42. data/lib/rack/head.rb +2 -0
  43. data/lib/rack/lint.rb +14 -11
  44. data/lib/rack/lobster.rb +7 -5
  45. data/lib/rack/lock.rb +2 -0
  46. data/lib/rack/logger.rb +2 -0
  47. data/lib/rack/media_type.rb +10 -5
  48. data/lib/rack/method_override.rb +9 -3
  49. data/lib/rack/mime.rb +9 -1
  50. data/lib/rack/mock.rb +74 -15
  51. data/lib/rack/multipart.rb +5 -3
  52. data/lib/rack/multipart/generator.rb +6 -7
  53. data/lib/rack/multipart/parser.rb +54 -51
  54. data/lib/rack/multipart/uploaded_file.rb +2 -0
  55. data/lib/rack/null_logger.rb +2 -0
  56. data/lib/rack/query_parser.rb +51 -25
  57. data/lib/rack/recursive.rb +7 -5
  58. data/lib/rack/reloader.rb +10 -4
  59. data/lib/rack/request.rb +89 -23
  60. data/lib/rack/response.rb +71 -31
  61. data/lib/rack/rewindable_input.rb +4 -2
  62. data/lib/rack/runtime.rb +4 -2
  63. data/lib/rack/sendfile.rb +15 -8
  64. data/lib/rack/server.rb +88 -16
  65. data/lib/rack/session/abstract/id.rb +104 -21
  66. data/lib/rack/session/cookie.rb +21 -11
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +17 -8
  69. data/lib/rack/show_exceptions.rb +16 -10
  70. data/lib/rack/show_status.rb +4 -2
  71. data/lib/rack/static.rb +15 -10
  72. data/lib/rack/tempfile_reaper.rb +2 -0
  73. data/lib/rack/urlmap.rb +11 -2
  74. data/lib/rack/utils.rb +55 -70
  75. data/rack.gemspec +19 -9
  76. metadata +32 -173
  77. data/test/builder/an_underscore_app.rb +0 -5
  78. data/test/builder/anything.rb +0 -5
  79. data/test/builder/comment.ru +0 -4
  80. data/test/builder/end.ru +0 -5
  81. data/test/builder/line.ru +0 -1
  82. data/test/builder/options.ru +0 -2
  83. data/test/cgi/assets/folder/test.js +0 -1
  84. data/test/cgi/assets/fonts/font.eot +0 -1
  85. data/test/cgi/assets/images/image.png +0 -1
  86. data/test/cgi/assets/index.html +0 -1
  87. data/test/cgi/assets/javascripts/app.js +0 -1
  88. data/test/cgi/assets/stylesheets/app.css +0 -1
  89. data/test/cgi/lighttpd.conf +0 -26
  90. data/test/cgi/rackup_stub.rb +0 -6
  91. data/test/cgi/sample_rackup.ru +0 -5
  92. data/test/cgi/test +0 -9
  93. data/test/cgi/test+directory/test+file +0 -1
  94. data/test/cgi/test.fcgi +0 -9
  95. data/test/cgi/test.gz +0 -0
  96. data/test/cgi/test.ru +0 -5
  97. data/test/gemloader.rb +0 -10
  98. data/test/helper.rb +0 -34
  99. data/test/multipart/bad_robots +0 -259
  100. data/test/multipart/binary +0 -0
  101. data/test/multipart/content_type_and_no_filename +0 -6
  102. data/test/multipart/empty +0 -10
  103. data/test/multipart/fail_16384_nofile +0 -814
  104. data/test/multipart/file1.txt +0 -1
  105. data/test/multipart/filename_and_modification_param +0 -7
  106. data/test/multipart/filename_and_no_name +0 -6
  107. data/test/multipart/filename_with_encoded_words +0 -7
  108. data/test/multipart/filename_with_escaped_quotes +0 -6
  109. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  110. data/test/multipart/filename_with_null_byte +0 -7
  111. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  112. data/test/multipart/filename_with_single_quote +0 -7
  113. data/test/multipart/filename_with_unescaped_percentages +0 -6
  114. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  115. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  116. data/test/multipart/filename_with_unescaped_quotes +0 -6
  117. data/test/multipart/ie +0 -6
  118. data/test/multipart/invalid_character +0 -6
  119. data/test/multipart/mixed_files +0 -21
  120. data/test/multipart/nested +0 -10
  121. data/test/multipart/none +0 -9
  122. data/test/multipart/quoted +0 -15
  123. data/test/multipart/rack-logo.png +0 -0
  124. data/test/multipart/semicolon +0 -6
  125. data/test/multipart/text +0 -15
  126. data/test/multipart/three_files_three_fields +0 -31
  127. data/test/multipart/unity3d_wwwform +0 -11
  128. data/test/multipart/webkit +0 -32
  129. data/test/rackup/config.ru +0 -31
  130. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  131. data/test/spec_auth_basic.rb +0 -89
  132. data/test/spec_auth_digest.rb +0 -260
  133. data/test/spec_body_proxy.rb +0 -85
  134. data/test/spec_builder.rb +0 -233
  135. data/test/spec_cascade.rb +0 -63
  136. data/test/spec_cgi.rb +0 -84
  137. data/test/spec_chunked.rb +0 -103
  138. data/test/spec_common_logger.rb +0 -95
  139. data/test/spec_conditional_get.rb +0 -103
  140. data/test/spec_config.rb +0 -23
  141. data/test/spec_content_length.rb +0 -86
  142. data/test/spec_content_type.rb +0 -46
  143. data/test/spec_deflater.rb +0 -375
  144. data/test/spec_directory.rb +0 -148
  145. data/test/spec_etag.rb +0 -108
  146. data/test/spec_events.rb +0 -133
  147. data/test/spec_fastcgi.rb +0 -85
  148. data/test/spec_file.rb +0 -264
  149. data/test/spec_handler.rb +0 -57
  150. data/test/spec_head.rb +0 -46
  151. data/test/spec_lint.rb +0 -515
  152. data/test/spec_lobster.rb +0 -59
  153. data/test/spec_lock.rb +0 -204
  154. data/test/spec_logger.rb +0 -24
  155. data/test/spec_media_type.rb +0 -42
  156. data/test/spec_method_override.rb +0 -96
  157. data/test/spec_mime.rb +0 -51
  158. data/test/spec_mock.rb +0 -359
  159. data/test/spec_multipart.rb +0 -722
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1393
  163. data/test/spec_response.rb +0 -510
  164. data/test/spec_rewindable_input.rb +0 -128
  165. data/test/spec_runtime.rb +0 -50
  166. data/test/spec_sendfile.rb +0 -125
  167. data/test/spec_server.rb +0 -193
  168. data/test/spec_session_abstract_id.rb +0 -31
  169. data/test/spec_session_abstract_session_hash.rb +0 -45
  170. data/test/spec_session_cookie.rb +0 -442
  171. data/test/spec_session_memcache.rb +0 -320
  172. data/test/spec_session_pool.rb +0 -210
  173. data/test/spec_show_exceptions.rb +0 -80
  174. data/test/spec_show_status.rb +0 -104
  175. data/test/spec_static.rb +0 -184
  176. data/test/spec_tempfile_reaper.rb +0 -64
  177. data/test/spec_thin.rb +0 -96
  178. data/test/spec_urlmap.rb +0 -237
  179. data/test/spec_utils.rb +0 -742
  180. data/test/spec_version.rb +0 -11
  181. data/test/spec_webrick.rb +0 -206
  182. data/test/static/another/index.html +0 -1
  183. data/test/static/foo.html +0 -1
  184. data/test/static/index.html +0 -1
  185. data/test/testrequest.rb +0 -78
  186. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  187. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1,375 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'stringio'
3
- require 'time' # for Time#httpdate
4
- require 'rack/deflater'
5
- require 'rack/lint'
6
- require 'rack/mock'
7
- require 'zlib'
8
-
9
- describe Rack::Deflater do
10
-
11
- def build_response(status, body, accept_encoding, options = {})
12
- body = [body] if body.respond_to? :to_str
13
- app = lambda do |env|
14
- res = [status, options['response_headers'] || {}, body]
15
- res[1]['Content-Type'] = 'text/plain' unless res[0] == 304
16
- res
17
- end
18
-
19
- request = Rack::MockRequest.env_for('', (options['request_headers'] || {}).merge('HTTP_ACCEPT_ENCODING' => accept_encoding))
20
- deflater = Rack::Lint.new Rack::Deflater.new(app, options['deflater_options'] || {})
21
-
22
- deflater.call(request)
23
- end
24
-
25
- ##
26
- # Constructs response object and verifies if it yields right results
27
- #
28
- # [expected_status] expected response status, e.g. 200, 304
29
- # [expected_body] expected response body
30
- # [accept_encoing] what Accept-Encoding header to send and expect, e.g.
31
- # 'deflate' - accepts and expects deflate encoding in response
32
- # { 'gzip' => nil } - accepts gzip but expects no encoding in response
33
- # [options] hash of request options, i.e.
34
- # 'app_status' - what status dummy app should return (may be changed by deflater at some point)
35
- # 'app_body' - what body dummy app should return (may be changed by deflater at some point)
36
- # 'request_headers' - extra request headers to be sent
37
- # 'response_headers' - extra response headers to be returned
38
- # 'deflater_options' - options passed to deflater middleware
39
- # [block] useful for doing some extra verification
40
- def verify(expected_status, expected_body, accept_encoding, options = {}, &block)
41
- accept_encoding, expected_encoding = if accept_encoding.kind_of?(Hash)
42
- [accept_encoding.keys.first, accept_encoding.values.first]
43
- else
44
- [accept_encoding, accept_encoding.dup]
45
- end
46
-
47
- # build response
48
- status, headers, body = build_response(
49
- options['app_status'] || expected_status,
50
- options['app_body'] || expected_body,
51
- accept_encoding,
52
- options
53
- )
54
-
55
- # verify status
56
- status.must_equal expected_status
57
-
58
- # verify body
59
- unless options['skip_body_verify']
60
- body_text = ''
61
- body.each { |part| body_text << part }
62
-
63
- deflated_body = case expected_encoding
64
- when 'deflate'
65
- inflater = Zlib::Inflate.new(-Zlib::MAX_WBITS)
66
- inflater.inflate(body_text) << inflater.finish
67
- when 'gzip'
68
- io = StringIO.new(body_text)
69
- gz = Zlib::GzipReader.new(io)
70
- tmp = gz.read
71
- gz.close
72
- tmp
73
- else
74
- body_text
75
- end
76
-
77
- deflated_body.must_equal expected_body
78
- end
79
-
80
- # yield full response verification
81
- yield(status, headers, body) if block_given?
82
- end
83
-
84
- # automatic gzip detection (streamable)
85
- def auto_inflater
86
- Zlib::Inflate.new(32 + Zlib::MAX_WBITS)
87
- end
88
-
89
- def deflate_or_gzip
90
- {'deflate, gzip' => 'gzip'}
91
- end
92
-
93
- it 'be able to deflate bodies that respond to each' do
94
- app_body = Object.new
95
- class << app_body; def each; yield('foo'); yield('bar'); end; end
96
-
97
- verify(200, 'foobar', deflate_or_gzip, { 'app_body' => app_body }) do |status, headers, body|
98
- headers.must_equal({
99
- 'Content-Encoding' => 'gzip',
100
- 'Vary' => 'Accept-Encoding',
101
- 'Content-Type' => 'text/plain'
102
- })
103
- end
104
- end
105
-
106
- it 'flush deflated chunks to the client as they become ready' do
107
- app_body = Object.new
108
- class << app_body; def each; yield('foo'); yield('bar'); end; end
109
-
110
- verify(200, app_body, deflate_or_gzip, { 'skip_body_verify' => true }) do |status, headers, body|
111
- headers.must_equal({
112
- 'Content-Encoding' => 'gzip',
113
- 'Vary' => 'Accept-Encoding',
114
- 'Content-Type' => 'text/plain'
115
- })
116
-
117
- buf = []
118
- inflater = auto_inflater
119
- body.each { |part| buf << inflater.inflate(part) }
120
- buf << inflater.finish
121
-
122
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
123
- end
124
- end
125
-
126
- it 'does not raise when a client aborts reading' do
127
- app_body = Object.new
128
- class << app_body; def each; yield('foo'); yield('bar'); end; end
129
- opts = { 'skip_body_verify' => true }
130
- verify(200, app_body, 'gzip', opts) do |status, headers, body|
131
- headers.must_equal({
132
- 'Content-Encoding' => 'gzip',
133
- 'Vary' => 'Accept-Encoding',
134
- 'Content-Type' => 'text/plain'
135
- })
136
-
137
- buf = []
138
- inflater = auto_inflater
139
- FakeDisconnect = Class.new(RuntimeError)
140
- assert_raises(FakeDisconnect, "not Zlib::DataError not raised") do
141
- body.each do |part|
142
- tmp = inflater.inflate(part)
143
- buf << tmp if tmp.bytesize > 0
144
- raise FakeDisconnect
145
- end
146
- end
147
- inflater.finish
148
- buf.must_equal(%w(foo))
149
- end
150
- end
151
-
152
- # TODO: This is really just a special case of the above...
153
- it 'be able to deflate String bodies' do
154
- verify(200, 'Hello world!', deflate_or_gzip) do |status, headers, body|
155
- headers.must_equal({
156
- 'Content-Encoding' => 'gzip',
157
- 'Vary' => 'Accept-Encoding',
158
- 'Content-Type' => 'text/plain'
159
- })
160
- end
161
- end
162
-
163
- it 'be able to gzip bodies that respond to each' do
164
- app_body = Object.new
165
- class << app_body; def each; yield('foo'); yield('bar'); end; end
166
-
167
- verify(200, 'foobar', 'gzip', { 'app_body' => app_body }) do |status, headers, body|
168
- headers.must_equal({
169
- 'Content-Encoding' => 'gzip',
170
- 'Vary' => 'Accept-Encoding',
171
- 'Content-Type' => 'text/plain'
172
- })
173
- end
174
- end
175
-
176
- it 'flush gzipped chunks to the client as they become ready' do
177
- app_body = Object.new
178
- class << app_body; def each; yield('foo'); yield('bar'); end; end
179
-
180
- verify(200, app_body, 'gzip', { 'skip_body_verify' => true }) do |status, headers, body|
181
- headers.must_equal({
182
- 'Content-Encoding' => 'gzip',
183
- 'Vary' => 'Accept-Encoding',
184
- 'Content-Type' => 'text/plain'
185
- })
186
-
187
- buf = []
188
- inflater = Zlib::Inflate.new(Zlib::MAX_WBITS + 32)
189
- body.each { |part| buf << inflater.inflate(part) }
190
- buf << inflater.finish
191
-
192
- buf.delete_if { |part| part.empty? }.join.must_equal 'foobar'
193
- end
194
- end
195
-
196
- it 'be able to fallback to no deflation' do
197
- verify(200, 'Hello world!', 'superzip') do |status, headers, body|
198
- headers.must_equal({
199
- 'Vary' => 'Accept-Encoding',
200
- 'Content-Type' => 'text/plain'
201
- })
202
- end
203
- end
204
-
205
- it 'be able to skip when there is no response entity body' do
206
- verify(304, '', { 'gzip' => nil }, { 'app_body' => [] }) do |status, headers, body|
207
- headers.must_equal({})
208
- end
209
- end
210
-
211
- it 'handle the lack of an acceptable encoding' do
212
- app_body = 'Hello world!'
213
- not_found_body1 = 'An acceptable encoding for the requested resource / could not be found.'
214
- not_found_body2 = 'An acceptable encoding for the requested resource /foo/bar could not be found.'
215
- options1 = {
216
- 'app_status' => 200,
217
- 'app_body' => app_body,
218
- 'request_headers' => {
219
- 'PATH_INFO' => '/'
220
- }
221
- }
222
- options2 = {
223
- 'app_status' => 200,
224
- 'app_body' => app_body,
225
- 'request_headers' => {
226
- 'PATH_INFO' => '/foo/bar'
227
- }
228
- }
229
-
230
- verify(406, not_found_body1, 'identity;q=0', options1) do |status, headers, body|
231
- headers.must_equal({
232
- 'Content-Type' => 'text/plain',
233
- 'Content-Length' => not_found_body1.length.to_s
234
- })
235
- end
236
-
237
- verify(406, not_found_body2, 'identity;q=0', options2) do |status, headers, body|
238
- headers.must_equal({
239
- 'Content-Type' => 'text/plain',
240
- 'Content-Length' => not_found_body2.length.to_s
241
- })
242
- end
243
- end
244
-
245
- it 'handle gzip response with Last-Modified header' do
246
- last_modified = Time.now.httpdate
247
- options = {
248
- 'response_headers' => {
249
- 'Content-Type' => 'text/plain',
250
- 'Last-Modified' => last_modified
251
- }
252
- }
253
-
254
- verify(200, 'Hello World!', 'gzip', options) do |status, headers, body|
255
- headers.must_equal({
256
- 'Content-Encoding' => 'gzip',
257
- 'Vary' => 'Accept-Encoding',
258
- 'Last-Modified' => last_modified,
259
- 'Content-Type' => 'text/plain'
260
- })
261
- end
262
- end
263
-
264
- it 'do nothing when no-transform Cache-Control directive present' do
265
- options = {
266
- 'response_headers' => {
267
- 'Content-Type' => 'text/plain',
268
- 'Cache-Control' => 'no-transform'
269
- }
270
- }
271
- verify(200, 'Hello World!', { 'gzip' => nil }, options) do |status, headers, body|
272
- headers.wont_include 'Content-Encoding'
273
- end
274
- end
275
-
276
- it 'do nothing when Content-Encoding already present' do
277
- options = {
278
- 'response_headers' => {
279
- 'Content-Type' => 'text/plain',
280
- 'Content-Encoding' => 'gzip'
281
- }
282
- }
283
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
284
- end
285
-
286
- it 'deflate when Content-Encoding is identity' do
287
- options = {
288
- 'response_headers' => {
289
- 'Content-Type' => 'text/plain',
290
- 'Content-Encoding' => 'identity'
291
- }
292
- }
293
- verify(200, 'Hello World!', deflate_or_gzip, options)
294
- end
295
-
296
- it "deflate if content-type matches :include" do
297
- options = {
298
- 'response_headers' => {
299
- 'Content-Type' => 'text/plain'
300
- },
301
- 'deflater_options' => {
302
- :include => %w(text/plain)
303
- }
304
- }
305
- verify(200, 'Hello World!', 'gzip', options)
306
- end
307
-
308
- it "deflate if content-type is included it :include" do
309
- options = {
310
- 'response_headers' => {
311
- 'Content-Type' => 'text/plain; charset=us-ascii'
312
- },
313
- 'deflater_options' => {
314
- :include => %w(text/plain)
315
- }
316
- }
317
- verify(200, 'Hello World!', 'gzip', options)
318
- end
319
-
320
- it "not deflate if content-type is not set but given in :include" do
321
- options = {
322
- 'deflater_options' => {
323
- :include => %w(text/plain)
324
- }
325
- }
326
- verify(304, 'Hello World!', { 'gzip' => nil }, options)
327
- end
328
-
329
- it "not deflate if content-type do not match :include" do
330
- options = {
331
- 'response_headers' => {
332
- 'Content-Type' => 'text/plain'
333
- },
334
- 'deflater_options' => {
335
- :include => %w(text/json)
336
- }
337
- }
338
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
339
- end
340
-
341
- it "deflate response if :if lambda evaluates to true" do
342
- options = {
343
- 'deflater_options' => {
344
- :if => lambda { |env, status, headers, body| true }
345
- }
346
- }
347
- verify(200, 'Hello World!', deflate_or_gzip, options)
348
- end
349
-
350
- it "not deflate if :if lambda evaluates to false" do
351
- options = {
352
- 'deflater_options' => {
353
- :if => lambda { |env, status, headers, body| false }
354
- }
355
- }
356
- verify(200, 'Hello World!', { 'gzip' => nil }, options)
357
- end
358
-
359
- it "check for Content-Length via :if" do
360
- response = 'Hello World!'
361
- response_len = response.length
362
- options = {
363
- 'response_headers' => {
364
- 'Content-Length' => response_len.to_s
365
- },
366
- 'deflater_options' => {
367
- :if => lambda { |env, status, headers, body|
368
- headers['Content-Length'].to_i >= response_len
369
- }
370
- }
371
- }
372
-
373
- verify(200, response, 'gzip', options)
374
- end
375
- end
@@ -1,148 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/directory'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
- require 'tempfile'
6
- require 'fileutils'
7
-
8
- describe Rack::Directory do
9
- DOCROOT = File.expand_path(File.dirname(__FILE__)) unless defined? DOCROOT
10
- FILE_CATCH = proc{|env| [200, {'Content-Type'=>'text/plain', "Content-Length" => "7"}, ['passed!']] }
11
-
12
- attr_reader :app
13
-
14
- def setup
15
- @app = Rack::Lint.new(Rack::Directory.new(DOCROOT, FILE_CATCH))
16
- end
17
-
18
- it 'serves directories with + in the name' do
19
- Dir.mktmpdir do |dir|
20
- plus_dir = "foo+bar"
21
- full_dir = File.join(dir, plus_dir)
22
- FileUtils.mkdir full_dir
23
- FileUtils.touch File.join(full_dir, "omg.txt")
24
- app = Rack::Directory.new(dir, FILE_CATCH)
25
- env = Rack::MockRequest.env_for("/#{plus_dir}/")
26
- status,_,body = app.call env
27
-
28
- assert_equal 200, status
29
-
30
- str = ''
31
- body.each { |x| str << x }
32
- assert_match "foo+bar", str
33
- end
34
- end
35
-
36
- it "serve directory indices" do
37
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
38
- get("/cgi/")
39
-
40
- res.must_be :ok?
41
- assert_match(res, /<html><head>/)
42
- end
43
-
44
- it "pass to app if file found" do
45
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
46
- get("/cgi/test")
47
-
48
- res.must_be :ok?
49
- assert_match(res, /passed!/)
50
- end
51
-
52
- it "serve uri with URL encoded filenames" do
53
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
54
- get("/%63%67%69/") # "/cgi/test"
55
-
56
- res.must_be :ok?
57
- assert_match(res, /<html><head>/)
58
-
59
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
60
- get("/cgi/%74%65%73%74") # "/cgi/test"
61
-
62
- res.must_be :ok?
63
- assert_match(res, /passed!/)
64
- end
65
-
66
- it "serve uri with URL encoded null byte (%00) in filenames" do
67
- res = Rack::MockRequest.new(Rack::Lint.new(app))
68
- .get("/cgi/test%00")
69
-
70
- res.must_be :bad_request?
71
- end
72
-
73
- it "not allow directory traversal" do
74
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
75
- get("/cgi/../test")
76
-
77
- res.must_be :forbidden?
78
-
79
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
80
- get("/cgi/%2E%2E/test")
81
-
82
- res.must_be :forbidden?
83
- end
84
-
85
- it "404 if it can't find the file" do
86
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
87
- get("/cgi/blubb")
88
-
89
- res.must_be :not_found?
90
- end
91
-
92
- it "uri escape path parts" do # #265, properly escape file names
93
- mr = Rack::MockRequest.new(Rack::Lint.new(app))
94
-
95
- res = mr.get("/cgi/test%2bdirectory")
96
-
97
- res.must_be :ok?
98
- res.body.must_match(%r[/cgi/test\+directory/test\+file])
99
-
100
- res = mr.get("/cgi/test%2bdirectory/test%2bfile")
101
- res.must_be :ok?
102
- end
103
-
104
- it "correctly escape script name with spaces" do
105
- Dir.mktmpdir do |dir|
106
- space_dir = "foo bar"
107
- full_dir = File.join(dir, space_dir)
108
- FileUtils.mkdir full_dir
109
- FileUtils.touch File.join(full_dir, "omg omg.txt")
110
- app = Rack::Directory.new(dir, FILE_CATCH)
111
- env = Rack::MockRequest.env_for(Rack::Utils.escape_path("/#{space_dir}/"))
112
- status,_,body = app.call env
113
-
114
- assert_equal 200, status
115
-
116
- str = ''
117
- body.each { |x| str << x }
118
- assert_match "/foo%20bar/omg%20omg.txt", str
119
- end
120
- end
121
-
122
- it "correctly escape script name" do
123
- _app = app
124
- app2 = Rack::Builder.new do
125
- map '/script-path' do
126
- run _app
127
- end
128
- end
129
-
130
- mr = Rack::MockRequest.new(Rack::Lint.new(app2))
131
-
132
- res = mr.get("/script-path/cgi/test%2bdirectory")
133
-
134
- res.must_be :ok?
135
- res.body.must_match(%r[/script-path/cgi/test\+directory/test\+file])
136
-
137
- res = mr.get("/script-path/cgi/test+directory/test+file")
138
- res.must_be :ok?
139
- end
140
-
141
- it "return error when file not found for head request" do
142
- res = Rack::MockRequest.new(Rack::Lint.new(app)).
143
- head("/cgi/missing")
144
-
145
- res.must_be :not_found?
146
- res.body.must_be :empty?
147
- end
148
- end