rack 2.0.9.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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/{HISTORY.md → CHANGELOG.md} +214 -164
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +79 -133
  5. data/Rakefile +25 -18
  6. data/SPEC +9 -9
  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/auth/abstract/handler.rb +3 -1
  12. data/lib/rack/auth/abstract/request.rb +2 -0
  13. data/lib/rack/auth/basic.rb +4 -1
  14. data/lib/rack/auth/digest/md5.rb +9 -7
  15. data/lib/rack/auth/digest/nonce.rb +6 -3
  16. data/lib/rack/auth/digest/params.rb +4 -2
  17. data/lib/rack/auth/digest/request.rb +2 -0
  18. data/lib/rack/body_proxy.rb +3 -6
  19. data/lib/rack/builder.rb +38 -15
  20. data/lib/rack/cascade.rb +6 -5
  21. data/lib/rack/chunked.rb +29 -6
  22. data/lib/rack/common_logger.rb +9 -11
  23. data/lib/rack/conditional_get.rb +3 -1
  24. data/lib/rack/config.rb +2 -0
  25. data/lib/rack/content_length.rb +3 -1
  26. data/lib/rack/content_type.rb +3 -1
  27. data/lib/rack/core_ext/regexp.rb +14 -0
  28. data/lib/rack/deflater.rb +28 -17
  29. data/lib/rack/directory.rb +17 -14
  30. data/lib/rack/etag.rb +3 -1
  31. data/lib/rack/events.rb +5 -3
  32. data/lib/rack/file.rb +5 -173
  33. data/lib/rack/files.rb +178 -0
  34. data/lib/rack/handler/cgi.rb +3 -1
  35. data/lib/rack/handler/fastcgi.rb +4 -2
  36. data/lib/rack/handler/lsws.rb +3 -1
  37. data/lib/rack/handler/scgi.rb +9 -6
  38. data/lib/rack/handler/thin.rb +3 -1
  39. data/lib/rack/handler/webrick.rb +4 -2
  40. data/lib/rack/handler.rb +7 -2
  41. data/lib/rack/head.rb +2 -0
  42. data/lib/rack/lint.rb +15 -12
  43. data/lib/rack/lobster.rb +7 -5
  44. data/lib/rack/lock.rb +2 -0
  45. data/lib/rack/logger.rb +2 -0
  46. data/lib/rack/media_type.rb +10 -5
  47. data/lib/rack/method_override.rb +4 -2
  48. data/lib/rack/mime.rb +9 -1
  49. data/lib/rack/mock.rb +74 -15
  50. data/lib/rack/multipart/generator.rb +6 -7
  51. data/lib/rack/multipart/parser.rb +55 -62
  52. data/lib/rack/multipart/uploaded_file.rb +2 -0
  53. data/lib/rack/multipart.rb +6 -3
  54. data/lib/rack/null_logger.rb +2 -0
  55. data/lib/rack/query_parser.rb +51 -25
  56. data/lib/rack/recursive.rb +7 -5
  57. data/lib/rack/reloader.rb +10 -4
  58. data/lib/rack/request.rb +79 -26
  59. data/lib/rack/response.rb +71 -31
  60. data/lib/rack/rewindable_input.rb +4 -2
  61. data/lib/rack/runtime.rb +4 -2
  62. data/lib/rack/sendfile.rb +15 -8
  63. data/lib/rack/server.rb +88 -16
  64. data/lib/rack/session/abstract/id.rb +40 -22
  65. data/lib/rack/session/cookie.rb +10 -9
  66. data/lib/rack/session/memcache.rb +4 -93
  67. data/lib/rack/session/pool.rb +4 -2
  68. data/lib/rack/show_exceptions.rb +15 -9
  69. data/lib/rack/show_status.rb +4 -2
  70. data/lib/rack/static.rb +15 -10
  71. data/lib/rack/tempfile_reaper.rb +2 -0
  72. data/lib/rack/urlmap.rb +11 -2
  73. data/lib/rack/utils.rb +64 -93
  74. data/lib/rack.rb +63 -60
  75. data/rack.gemspec +17 -7
  76. metadata +33 -175
  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 -107
  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 -520
  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 -110
  157. data/test/spec_mime.rb +0 -51
  158. data/test/spec_mock.rb +0 -359
  159. data/test/spec_multipart.rb +0 -721
  160. data/test/spec_null_logger.rb +0 -21
  161. data/test/spec_recursive.rb +0 -75
  162. data/test/spec_request.rb +0 -1423
  163. data/test/spec_response.rb +0 -528
  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 -357
  172. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  173. data/test/spec_session_pool.rb +0 -247
  174. data/test/spec_show_exceptions.rb +0 -93
  175. data/test/spec_show_status.rb +0 -104
  176. data/test/spec_static.rb +0 -184
  177. data/test/spec_tempfile_reaper.rb +0 -64
  178. data/test/spec_thin.rb +0 -96
  179. data/test/spec_urlmap.rb +0 -237
  180. data/test/spec_utils.rb +0 -742
  181. data/test/spec_version.rb +0 -11
  182. data/test/spec_webrick.rb +0 -206
  183. data/test/static/another/index.html +0 -1
  184. data/test/static/foo.html +0 -1
  185. data/test/static/index.html +0 -1
  186. data/test/testrequest.rb +0 -78
  187. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  188. data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
@@ -1 +0,0 @@
1
- contents
@@ -1,7 +0,0 @@
1
- --AaB03x
2
- Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename=genome.jpeg; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
4
- Content-Description: a complete map of the human genome
5
-
6
- contents
7
- --AaB03x--
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; filename="file1.txt"
3
- Content-Type: text/plain
4
-
5
- contents
6
- --AaB03x--
@@ -1,7 +0,0 @@
1
- --AaB03x
2
- Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename*=utf-8''%D1%84%D0%B0%D0%B9%D0%BB
4
- Content-Description: a complete map of the human genome
5
-
6
- contents
7
- --AaB03x--
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="escape \"quotes"
3
- Content-Type: application/octet-stream
4
-
5
- contents
6
- --AaB03x--
@@ -1,7 +0,0 @@
1
- --AaB03x
2
- Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename="\"human\" genome.jpeg"; modification-date="Wed, 12 Feb 1997 16:29:51 -0500";
4
- Content-Description: a complete map of the human genome
5
-
6
- contents
7
- --AaB03x--
@@ -1,7 +0,0 @@
1
- --AaB03x
2
- Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename="flowers.exe%00.jpg"
4
- Content-Description: a complete map of the human genome
5
-
6
- contents
7
- --AaB03x--
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="escape %22quotes"
3
- Content-Type: application/octet-stream
4
-
5
- contents
6
- --AaB03x--
@@ -1,7 +0,0 @@
1
- --AaB03x
2
- Content-Type: image/jpeg
3
- Content-Disposition: attachment; name="files"; filename="bob's flowers.jpg"
4
- Content-Description: a complete map of the human genome
5
-
6
- contents
7
- --AaB03x--
@@ -1,6 +0,0 @@
1
- ------WebKitFormBoundary2NHc7OhsgU68l3Al
2
- Content-Disposition: form-data; name="document[attachment]"; filename="100% of a photo.jpeg"
3
- Content-Type: image/jpeg
4
-
5
- contents
6
- ------WebKitFormBoundary2NHc7OhsgU68l3Al--
@@ -1,6 +0,0 @@
1
- ------WebKitFormBoundary2NHc7OhsgU68l3Al
2
- Content-Disposition: form-data; name="document[attachment]"; filename="100%a"
3
- Content-Type: image/jpeg
4
-
5
- contents
6
- ------WebKitFormBoundary2NHc7OhsgU68l3Al--
@@ -1,6 +0,0 @@
1
- ------WebKitFormBoundary2NHc7OhsgU68l3Al
2
- Content-Disposition: form-data; name="document[attachment]"; filename="100%"
3
- Content-Type: image/jpeg
4
-
5
- contents
6
- ------WebKitFormBoundary2NHc7OhsgU68l3Al--
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="escape "quotes"
3
- Content-Type: application/octet-stream
4
-
5
- contents
6
- --AaB03x--
data/test/multipart/ie DELETED
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="C:\Documents and Settings\Administrator\Desktop\file1.txt"
3
- Content-Type: text/plain
4
-
5
- contents
6
- --AaB03x--
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="invalid�.txt"
3
- Content-Type: text/plain
4
-
5
- contents
6
- --AaB03x--
@@ -1,21 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="foo"
3
-
4
- bar
5
- --AaB03x
6
- Content-Disposition: form-data; name="files"
7
- Content-Type: multipart/mixed, boundary=BbC04y
8
-
9
- --BbC04y
10
- Content-Disposition: attachment; filename="file.txt"
11
- Content-Type: text/plain
12
-
13
- contents
14
- --BbC04y
15
- Content-Disposition: attachment; filename="flowers.jpg"
16
- Content-Type: image/jpeg
17
- Content-Transfer-Encoding: binary
18
-
19
- contents
20
- --BbC04y--
21
- --AaB03x--
@@ -1,10 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="foo[submit-name]"
3
-
4
- Larry
5
- --AaB03x
6
- Content-Disposition: form-data; name="foo[files]"; filename="file1.txt"
7
- Content-Type: text/plain
8
-
9
- contents
10
- --AaB03x--
data/test/multipart/none DELETED
@@ -1,9 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="submit-name"
3
-
4
- Larry
5
- --AaB03x
6
- Content-Disposition: form-data; name="files"; filename=""
7
-
8
-
9
- --AaB03x--
@@ -1,15 +0,0 @@
1
- --AaB:03x
2
- Content-Disposition: form-data; name="submit-name"
3
-
4
- Larry
5
- --AaB:03x
6
- Content-Disposition: form-data; name="submit-name-with-content"
7
- Content-Type: text/plain
8
-
9
- Berry
10
- --AaB:03x
11
- Content-Disposition: form-data; name="files"; filename="file1.txt"
12
- Content-Type: text/plain
13
-
14
- contents
15
- --AaB:03x--
Binary file
@@ -1,6 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="files"; filename="fi;le1.txt"
3
- Content-Type: text/plain
4
-
5
- contents
6
- --AaB03x--
data/test/multipart/text DELETED
@@ -1,15 +0,0 @@
1
- --AaB03x
2
- Content-Disposition: form-data; name="submit-name"
3
-
4
- Larry
5
- --AaB03x
6
- Content-Disposition: form-data; name="submit-name-with-content"
7
- Content-Type: text/plain
8
-
9
- Berry
10
- --AaB03x
11
- Content-Disposition: form-data; name="files"; filename="file1.txt"
12
- Content-Type: text/plain
13
-
14
- contents
15
- --AaB03x--
@@ -1,31 +0,0 @@
1
- --AaB03x
2
- content-disposition: form-data; name="reply"
3
-
4
- yes
5
- --AaB03x
6
- content-disposition: form-data; name="to"
7
-
8
- people
9
- --AaB03x
10
- content-disposition: form-data; name="from"
11
-
12
- others
13
- --AaB03x
14
- content-disposition: form-data; name="fileupload1"; filename="file1.jpg"
15
- Content-Type: image/jpeg
16
- Content-Transfer-Encoding: base64
17
-
18
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
19
- --AaB03x
20
- content-disposition: form-data; name="fileupload2"; filename="file2.jpg"
21
- Content-Type: image/jpeg
22
- Content-Transfer-Encoding: base64
23
-
24
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
25
- --AaB03x
26
- content-disposition: form-data; name="fileupload3"; filename="file3.jpg"
27
- Content-Type: image/jpeg
28
- Content-Transfer-Encoding: base64
29
-
30
- /9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg
31
- --AaB03x--
@@ -1,11 +0,0 @@
1
- --AaB03x
2
- Content-Type: text/plain; charset="utf-8"
3
- Content-disposition: form-data; name="user_sid"
4
-
5
- bbf14f82-d2aa-4c07-9fb8-ca6714a7ea97
6
- --AaB03x
7
- Content-Type: image/png; charset=UTF-8
8
- Content-disposition: form-data; name="file";
9
- filename="b67879ed-bfed-4491-a8cc-f99cca769f94.png"
10
-
11
- --AaB03x
@@ -1,32 +0,0 @@
1
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
2
- Content-Disposition: form-data; name="_method"
3
-
4
- put
5
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
6
- Content-Disposition: form-data; name="profile[blog]"
7
-
8
-
9
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
10
- Content-Disposition: form-data; name="profile[public_email]"
11
-
12
-
13
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
14
- Content-Disposition: form-data; name="profile[interests]"
15
-
16
-
17
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
18
- Content-Disposition: form-data; name="profile[bio]"
19
-
20
- hello
21
-
22
- "quote"
23
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
24
- Content-Disposition: form-data; name="media"; filename=""
25
- Content-Type: application/octet-stream
26
-
27
-
28
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR
29
- Content-Disposition: form-data; name="commit"
30
-
31
- Save
32
- ------WebKitFormBoundaryWLHCs9qmcJJoyjKR--
@@ -1,31 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/../testrequest"
2
-
3
- $stderr = File.open("#{File.dirname(__FILE__)}/log_output", "w")
4
-
5
- class EnvMiddleware
6
- def initialize(app)
7
- @app = app
8
- end
9
-
10
- def call(env)
11
- # provides a way to test that lint is present
12
- if env["PATH_INFO"] == "/broken_lint"
13
- return [200, {}, ["Broken Lint"]]
14
- # provides a way to kill the process without knowing the pid
15
- elsif env["PATH_INFO"] == "/die"
16
- exit!
17
- end
18
-
19
- env["test.$DEBUG"] = $DEBUG
20
- env["test.$EVAL"] = BUKKIT if defined?(BUKKIT)
21
- env["test.$VERBOSE"] = $VERBOSE
22
- env["test.$LOAD_PATH"] = $LOAD_PATH
23
- env["test.stderr"] = File.expand_path($stderr.path)
24
- env["test.Ping"] = defined?(Ping)
25
- env["test.pid"] = Process.pid
26
- @app.call(env)
27
- end
28
- end
29
-
30
- use EnvMiddleware
31
- run TestRequest.new
@@ -1,8 +0,0 @@
1
- module Rack
2
- module Handler
3
- class RegisteringMyself
4
- end
5
-
6
- register :registering_myself, RegisteringMyself
7
- end
8
- end
@@ -1,89 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/auth/basic'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
-
6
- describe Rack::Auth::Basic do
7
- def realm
8
- 'WallysWorld'
9
- end
10
-
11
- def unprotected_app
12
- Rack::Lint.new lambda { |env|
13
- [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ]
14
- }
15
- end
16
-
17
- def protected_app
18
- app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username }
19
- app.realm = realm
20
- app
21
- end
22
-
23
- before do
24
- @request = Rack::MockRequest.new(protected_app)
25
- end
26
-
27
- def request_with_basic_auth(username, password, &block)
28
- request 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack("m*"), &block
29
- end
30
-
31
- def request(headers = {})
32
- yield @request.get('/', headers)
33
- end
34
-
35
- def assert_basic_auth_challenge(response)
36
- response.must_be :client_error?
37
- response.status.must_equal 401
38
- response.must_include 'WWW-Authenticate'
39
- response.headers['WWW-Authenticate'].must_match(/Basic realm="#{Regexp.escape(realm)}"/)
40
- response.body.must_be :empty?
41
- end
42
-
43
- it 'challenge correctly when no credentials are specified' do
44
- request do |response|
45
- assert_basic_auth_challenge response
46
- end
47
- end
48
-
49
- it 'rechallenge if incorrect credentials are specified' do
50
- request_with_basic_auth 'joe', 'password' do |response|
51
- assert_basic_auth_challenge response
52
- end
53
- end
54
-
55
- it 'return application output if correct credentials are specified' do
56
- request_with_basic_auth 'Boss', 'password' do |response|
57
- response.status.must_equal 200
58
- response.body.to_s.must_equal 'Hi Boss'
59
- end
60
- end
61
-
62
- it 'return 400 Bad Request if different auth scheme used' do
63
- request 'HTTP_AUTHORIZATION' => 'Digest params' do |response|
64
- response.must_be :client_error?
65
- response.status.must_equal 400
66
- response.wont_include 'WWW-Authenticate'
67
- end
68
- end
69
-
70
- it 'return 400 Bad Request for a malformed authorization header' do
71
- request 'HTTP_AUTHORIZATION' => '' do |response|
72
- response.must_be :client_error?
73
- response.status.must_equal 400
74
- response.wont_include 'WWW-Authenticate'
75
- end
76
- end
77
-
78
- it 'return 401 Bad Request for a nil authorization header' do
79
- request 'HTTP_AUTHORIZATION' => nil do |response|
80
- response.must_be :client_error?
81
- response.status.must_equal 401
82
- end
83
- end
84
-
85
- it 'takes realm as optional constructor arg' do
86
- app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
87
- realm.must_equal app.realm
88
- end
89
- end
@@ -1,260 +0,0 @@
1
- require 'minitest/autorun'
2
- require 'rack/auth/digest/md5'
3
- require 'rack/lint'
4
- require 'rack/mock'
5
-
6
- describe Rack::Auth::Digest::MD5 do
7
- def realm
8
- 'WallysWorld'
9
- end
10
-
11
- def unprotected_app
12
- Rack::Lint.new lambda { |env|
13
- friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"]
14
- [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ]
15
- }
16
- end
17
-
18
- def protected_app
19
- Rack::Auth::Digest::MD5.new(unprotected_app, :realm => realm, :opaque => 'this-should-be-secret') do |username|
20
- { 'Alice' => 'correct-password' }[username]
21
- end
22
- end
23
-
24
- def protected_app_with_hashed_passwords
25
- app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
26
- username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil
27
- end
28
- app.realm = realm
29
- app.opaque = 'this-should-be-secret'
30
- app.passwords_hashed = true
31
- app
32
- end
33
-
34
- def partially_protected_app
35
- Rack::URLMap.new({
36
- '/' => unprotected_app,
37
- '/protected' => protected_app
38
- })
39
- end
40
-
41
- def protected_app_with_method_override
42
- Rack::MethodOverride.new(protected_app)
43
- end
44
-
45
- before do
46
- @request = Rack::MockRequest.new(protected_app)
47
- end
48
-
49
- def request(method, path, headers = {}, &block)
50
- response = @request.request(method, path, headers)
51
- block.call(response) if block
52
- return response
53
- end
54
-
55
- class MockDigestRequest
56
- def initialize(params)
57
- @params = params
58
- end
59
- def method_missing(sym)
60
- if @params.has_key? k = sym.to_s
61
- return @params[k]
62
- end
63
- super
64
- end
65
- def method
66
- @params['method']
67
- end
68
- def response(password)
69
- Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
70
- end
71
- end
72
-
73
- def request_with_digest_auth(method, path, username, password, options = {}, &block)
74
- request_options = {}
75
- request_options[:input] = options.delete(:input) if options.include? :input
76
-
77
- response = request(method, path, request_options)
78
-
79
- return response unless response.status == 401
80
-
81
- if wait = options.delete(:wait)
82
- sleep wait
83
- end
84
-
85
- challenge = response['WWW-Authenticate'].split(' ', 2).last
86
-
87
- params = Rack::Auth::Digest::Params.parse(challenge)
88
-
89
- params['username'] = username
90
- params['nc'] = '00000001'
91
- params['cnonce'] = 'nonsensenonce'
92
- params['uri'] = path
93
-
94
- params['method'] = method
95
-
96
- params.update options
97
-
98
- params['response'] = MockDigestRequest.new(params).response(password)
99
-
100
- request(method, path, request_options.merge('HTTP_AUTHORIZATION' => "Digest #{params}"), &block)
101
- end
102
-
103
- def assert_digest_auth_challenge(response)
104
- response.must_be :client_error?
105
- response.status.must_equal 401
106
- response.must_include 'WWW-Authenticate'
107
- response.headers['WWW-Authenticate'].must_match(/^Digest /)
108
- response.body.must_be :empty?
109
- end
110
-
111
- def assert_bad_request(response)
112
- response.must_be :client_error?
113
- response.status.must_equal 400
114
- response.wont_include 'WWW-Authenticate'
115
- end
116
-
117
- it 'challenge when no credentials are specified' do
118
- request 'GET', '/' do |response|
119
- assert_digest_auth_challenge response
120
- end
121
- end
122
-
123
- it 'return application output if correct credentials given' do
124
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
125
- response.status.must_equal 200
126
- response.body.to_s.must_equal 'Hi Alice'
127
- end
128
- end
129
-
130
- it 'return application output if correct credentials given (hashed passwords)' do
131
- @request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
132
-
133
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
134
- response.status.must_equal 200
135
- response.body.to_s.must_equal 'Hi Alice'
136
- end
137
- end
138
-
139
- it 'rechallenge if incorrect username given' do
140
- request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response|
141
- assert_digest_auth_challenge response
142
- end
143
- end
144
-
145
- it 'rechallenge if incorrect password given' do
146
- request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response|
147
- assert_digest_auth_challenge response
148
- end
149
- end
150
-
151
- it 'rechallenge if incorrect user and blank password given' do
152
- request_with_digest_auth 'GET', '/', 'Bob', '' do |response|
153
- assert_digest_auth_challenge response
154
- end
155
- end
156
-
157
- it 'not rechallenge if nonce is not stale' do
158
- begin
159
- Rack::Auth::Digest::Nonce.time_limit = 10
160
-
161
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response|
162
- response.status.must_equal 200
163
- response.body.to_s.must_equal 'Hi Alice'
164
- response.headers['WWW-Authenticate'].wont_match(/\bstale=true\b/)
165
- end
166
- ensure
167
- Rack::Auth::Digest::Nonce.time_limit = nil
168
- end
169
- end
170
-
171
- it 'rechallenge with stale parameter if nonce is stale' do
172
- begin
173
- Rack::Auth::Digest::Nonce.time_limit = 1
174
-
175
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 2 do |response|
176
- assert_digest_auth_challenge response
177
- response.headers['WWW-Authenticate'].must_match(/\bstale=true\b/)
178
- end
179
- ensure
180
- Rack::Auth::Digest::Nonce.time_limit = nil
181
- end
182
- end
183
-
184
- it 'return 400 Bad Request if incorrect qop given' do
185
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
186
- assert_bad_request response
187
- end
188
- end
189
-
190
- it 'return 400 Bad Request if incorrect uri given' do
191
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
192
- assert_bad_request response
193
- end
194
- end
195
-
196
- it 'return 400 Bad Request if different auth scheme used' do
197
- request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
198
- assert_bad_request response
199
- end
200
- end
201
-
202
- it 'not require credentials for unprotected path' do
203
- @request = Rack::MockRequest.new(partially_protected_app)
204
- request 'GET', '/' do |response|
205
- response.must_be :ok?
206
- end
207
- end
208
-
209
- it 'challenge when no credentials are specified for protected path' do
210
- @request = Rack::MockRequest.new(partially_protected_app)
211
- request 'GET', '/protected' do |response|
212
- assert_digest_auth_challenge response
213
- end
214
- end
215
-
216
- it 'return application output if correct credentials given for protected path' do
217
- @request = Rack::MockRequest.new(partially_protected_app)
218
- request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response|
219
- response.status.must_equal 200
220
- response.body.to_s.must_equal 'Hi Alice'
221
- end
222
- end
223
-
224
- it 'return application output when used with a query string and path as uri' do
225
- @request = Rack::MockRequest.new(partially_protected_app)
226
- request_with_digest_auth 'GET', '/protected?friend=Mike', 'Alice', 'correct-password' do |response|
227
- response.status.must_equal 200
228
- response.body.to_s.must_equal 'Hi Alice and Mike'
229
- end
230
- end
231
-
232
- it 'return application output when used with a query string and fullpath as uri' do
233
- @request = Rack::MockRequest.new(partially_protected_app)
234
- qs_uri = '/protected?friend=Mike'
235
- request_with_digest_auth 'GET', qs_uri, 'Alice', 'correct-password', 'uri' => qs_uri do |response|
236
- response.status.must_equal 200
237
- response.body.to_s.must_equal 'Hi Alice and Mike'
238
- end
239
- end
240
-
241
- it 'return application output if correct credentials given for POST' do
242
- request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
243
- response.status.must_equal 200
244
- response.body.to_s.must_equal 'Hi Alice'
245
- end
246
- end
247
-
248
- it 'return application output if correct credentials given for PUT (using method override of POST)' do
249
- @request = Rack::MockRequest.new(protected_app_with_method_override)
250
- request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response|
251
- response.status.must_equal 200
252
- response.body.to_s.must_equal 'Hi Alice'
253
- end
254
- end
255
-
256
- it 'takes realm as optional constructor arg' do
257
- app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true }
258
- realm.must_equal app.realm
259
- end
260
- end