rack 1.6.13 → 2.2.3

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 (191) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +694 -0
  3. data/CONTRIBUTING.md +136 -0
  4. data/{COPYING → MIT-LICENSE} +4 -2
  5. data/README.rdoc +157 -163
  6. data/Rakefile +38 -32
  7. data/{SPEC → SPEC.rdoc} +41 -13
  8. data/bin/rackup +1 -0
  9. data/contrib/rack_logo.svg +164 -111
  10. data/example/lobster.ru +2 -0
  11. data/example/protectedlobster.rb +4 -2
  12. data/example/protectedlobster.ru +3 -1
  13. data/lib/rack/auth/abstract/handler.rb +3 -1
  14. data/lib/rack/auth/abstract/request.rb +6 -2
  15. data/lib/rack/auth/basic.rb +7 -4
  16. data/lib/rack/auth/digest/md5.rb +13 -11
  17. data/lib/rack/auth/digest/nonce.rb +6 -3
  18. data/lib/rack/auth/digest/params.rb +5 -4
  19. data/lib/rack/auth/digest/request.rb +6 -4
  20. data/lib/rack/body_proxy.rb +21 -15
  21. data/lib/rack/builder.rb +119 -26
  22. data/lib/rack/cascade.rb +28 -12
  23. data/lib/rack/chunked.rb +70 -22
  24. data/lib/rack/common_logger.rb +80 -0
  25. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -16
  26. data/lib/rack/config.rb +2 -0
  27. data/lib/rack/content_length.rb +9 -8
  28. data/lib/rack/content_type.rb +5 -4
  29. data/lib/rack/core_ext/regexp.rb +14 -0
  30. data/lib/rack/deflater.rb +60 -70
  31. data/lib/rack/directory.rb +117 -85
  32. data/lib/rack/etag.rb +9 -7
  33. data/lib/rack/events.rb +153 -0
  34. data/lib/rack/file.rb +4 -149
  35. data/lib/rack/files.rb +218 -0
  36. data/lib/rack/handler/cgi.rb +17 -19
  37. data/lib/rack/handler/fastcgi.rb +17 -18
  38. data/lib/rack/handler/lsws.rb +14 -14
  39. data/lib/rack/handler/scgi.rb +22 -21
  40. data/lib/rack/handler/thin.rb +6 -3
  41. data/lib/rack/handler/webrick.rb +39 -32
  42. data/lib/rack/handler.rb +9 -26
  43. data/lib/rack/head.rb +16 -18
  44. data/lib/rack/lint.rb +110 -64
  45. data/lib/rack/lobster.rb +10 -10
  46. data/lib/rack/lock.rb +17 -11
  47. data/lib/rack/logger.rb +4 -2
  48. data/lib/rack/media_type.rb +43 -0
  49. data/lib/rack/{methodoverride.rb → method_override.rb} +10 -8
  50. data/lib/rack/mime.rb +27 -6
  51. data/lib/rack/mock.rb +124 -65
  52. data/lib/rack/multipart/generator.rb +20 -16
  53. data/lib/rack/multipart/parser.rb +273 -162
  54. data/lib/rack/multipart/uploaded_file.rb +15 -8
  55. data/lib/rack/multipart.rb +39 -8
  56. data/lib/rack/{nulllogger.rb → null_logger.rb} +3 -1
  57. data/lib/rack/query_parser.rb +217 -0
  58. data/lib/rack/recursive.rb +11 -9
  59. data/lib/rack/reloader.rb +8 -4
  60. data/lib/rack/request.rb +553 -305
  61. data/lib/rack/response.rb +244 -88
  62. data/lib/rack/rewindable_input.rb +5 -15
  63. data/lib/rack/runtime.rb +12 -18
  64. data/lib/rack/sendfile.rb +17 -15
  65. data/lib/rack/server.rb +125 -47
  66. data/lib/rack/session/abstract/id.rb +141 -93
  67. data/lib/rack/session/cookie.rb +35 -29
  68. data/lib/rack/session/memcache.rb +4 -93
  69. data/lib/rack/session/pool.rb +13 -11
  70. data/lib/rack/show_exceptions.rb +390 -0
  71. data/lib/rack/{showstatus.rb → show_status.rb} +12 -12
  72. data/lib/rack/static.rb +48 -11
  73. data/lib/rack/tempfile_reaper.rb +3 -3
  74. data/lib/rack/urlmap.rb +26 -19
  75. data/lib/rack/utils.rb +212 -294
  76. data/lib/rack/version.rb +29 -0
  77. data/lib/rack.rb +76 -33
  78. data/rack.gemspec +43 -30
  79. metadata +65 -187
  80. data/HISTORY.md +0 -375
  81. data/KNOWN-ISSUES +0 -44
  82. data/lib/rack/backports/uri/common_18.rb +0 -56
  83. data/lib/rack/backports/uri/common_192.rb +0 -52
  84. data/lib/rack/backports/uri/common_193.rb +0 -29
  85. data/lib/rack/commonlogger.rb +0 -72
  86. data/lib/rack/handler/evented_mongrel.rb +0 -8
  87. data/lib/rack/handler/mongrel.rb +0 -106
  88. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  89. data/lib/rack/showexceptions.rb +0 -387
  90. data/lib/rack/utils/okjson.rb +0 -600
  91. data/test/builder/anything.rb +0 -5
  92. data/test/builder/comment.ru +0 -4
  93. data/test/builder/end.ru +0 -5
  94. data/test/builder/line.ru +0 -1
  95. data/test/builder/options.ru +0 -2
  96. data/test/cgi/assets/folder/test.js +0 -1
  97. data/test/cgi/assets/fonts/font.eot +0 -1
  98. data/test/cgi/assets/images/image.png +0 -1
  99. data/test/cgi/assets/index.html +0 -1
  100. data/test/cgi/assets/javascripts/app.js +0 -1
  101. data/test/cgi/assets/stylesheets/app.css +0 -1
  102. data/test/cgi/lighttpd.conf +0 -26
  103. data/test/cgi/rackup_stub.rb +0 -6
  104. data/test/cgi/sample_rackup.ru +0 -5
  105. data/test/cgi/test +0 -9
  106. data/test/cgi/test+directory/test+file +0 -1
  107. data/test/cgi/test.fcgi +0 -8
  108. data/test/cgi/test.ru +0 -5
  109. data/test/gemloader.rb +0 -10
  110. data/test/multipart/bad_robots +0 -259
  111. data/test/multipart/binary +0 -0
  112. data/test/multipart/content_type_and_no_filename +0 -6
  113. data/test/multipart/empty +0 -10
  114. data/test/multipart/fail_16384_nofile +0 -814
  115. data/test/multipart/file1.txt +0 -1
  116. data/test/multipart/filename_and_modification_param +0 -7
  117. data/test/multipart/filename_and_no_name +0 -6
  118. data/test/multipart/filename_with_escaped_quotes +0 -6
  119. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  120. data/test/multipart/filename_with_null_byte +0 -7
  121. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  122. data/test/multipart/filename_with_unescaped_percentages +0 -6
  123. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  124. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  125. data/test/multipart/filename_with_unescaped_quotes +0 -6
  126. data/test/multipart/ie +0 -6
  127. data/test/multipart/invalid_character +0 -6
  128. data/test/multipart/mixed_files +0 -21
  129. data/test/multipart/nested +0 -10
  130. data/test/multipart/none +0 -9
  131. data/test/multipart/semicolon +0 -6
  132. data/test/multipart/text +0 -15
  133. data/test/multipart/three_files_three_fields +0 -31
  134. data/test/multipart/webkit +0 -32
  135. data/test/rackup/config.ru +0 -31
  136. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  137. data/test/spec_auth_basic.rb +0 -81
  138. data/test/spec_auth_digest.rb +0 -259
  139. data/test/spec_body_proxy.rb +0 -85
  140. data/test/spec_builder.rb +0 -223
  141. data/test/spec_cascade.rb +0 -61
  142. data/test/spec_cgi.rb +0 -102
  143. data/test/spec_chunked.rb +0 -101
  144. data/test/spec_commonlogger.rb +0 -93
  145. data/test/spec_conditionalget.rb +0 -102
  146. data/test/spec_config.rb +0 -22
  147. data/test/spec_content_length.rb +0 -85
  148. data/test/spec_content_type.rb +0 -45
  149. data/test/spec_deflater.rb +0 -339
  150. data/test/spec_directory.rb +0 -88
  151. data/test/spec_etag.rb +0 -107
  152. data/test/spec_fastcgi.rb +0 -107
  153. data/test/spec_file.rb +0 -221
  154. data/test/spec_handler.rb +0 -72
  155. data/test/spec_head.rb +0 -45
  156. data/test/spec_lint.rb +0 -550
  157. data/test/spec_lobster.rb +0 -58
  158. data/test/spec_lock.rb +0 -164
  159. data/test/spec_logger.rb +0 -23
  160. data/test/spec_methodoverride.rb +0 -111
  161. data/test/spec_mime.rb +0 -51
  162. data/test/spec_mock.rb +0 -297
  163. data/test/spec_mongrel.rb +0 -182
  164. data/test/spec_multipart.rb +0 -600
  165. data/test/spec_nulllogger.rb +0 -20
  166. data/test/spec_recursive.rb +0 -72
  167. data/test/spec_request.rb +0 -1232
  168. data/test/spec_response.rb +0 -407
  169. data/test/spec_rewindable_input.rb +0 -118
  170. data/test/spec_runtime.rb +0 -49
  171. data/test/spec_sendfile.rb +0 -130
  172. data/test/spec_server.rb +0 -167
  173. data/test/spec_session_abstract_id.rb +0 -53
  174. data/test/spec_session_cookie.rb +0 -410
  175. data/test/spec_session_memcache.rb +0 -358
  176. data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
  177. data/test/spec_session_pool.rb +0 -246
  178. data/test/spec_showexceptions.rb +0 -98
  179. data/test/spec_showstatus.rb +0 -103
  180. data/test/spec_static.rb +0 -145
  181. data/test/spec_tempfile_reaper.rb +0 -63
  182. data/test/spec_thin.rb +0 -91
  183. data/test/spec_urlmap.rb +0 -236
  184. data/test/spec_utils.rb +0 -647
  185. data/test/spec_version.rb +0 -17
  186. data/test/spec_webrick.rb +0 -184
  187. data/test/static/another/index.html +0 -1
  188. data/test/static/index.html +0 -1
  189. data/test/testrequest.rb +0 -78
  190. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  191. 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,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,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,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,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,81 +0,0 @@
1
- require 'rack/auth/basic'
2
- require 'rack/lint'
3
- require 'rack/mock'
4
-
5
- describe Rack::Auth::Basic do
6
- def realm
7
- 'WallysWorld'
8
- end
9
-
10
- def unprotected_app
11
- Rack::Lint.new lambda { |env|
12
- [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ]
13
- }
14
- end
15
-
16
- def protected_app
17
- app = Rack::Auth::Basic.new(unprotected_app) { |username, password| 'Boss' == username }
18
- app.realm = realm
19
- app
20
- end
21
-
22
- before do
23
- @request = Rack::MockRequest.new(protected_app)
24
- end
25
-
26
- def request_with_basic_auth(username, password, &block)
27
- request 'HTTP_AUTHORIZATION' => 'Basic ' + ["#{username}:#{password}"].pack("m*"), &block
28
- end
29
-
30
- def request(headers = {})
31
- yield @request.get('/', headers)
32
- end
33
-
34
- def assert_basic_auth_challenge(response)
35
- response.should.be.a.client_error
36
- response.status.should.equal 401
37
- response.should.include 'WWW-Authenticate'
38
- response.headers['WWW-Authenticate'].should =~ /Basic realm="#{Regexp.escape(realm)}"/
39
- response.body.should.be.empty
40
- end
41
-
42
- should 'challenge correctly when no credentials are specified' do
43
- request do |response|
44
- assert_basic_auth_challenge response
45
- end
46
- end
47
-
48
- should 'rechallenge if incorrect credentials are specified' do
49
- request_with_basic_auth 'joe', 'password' do |response|
50
- assert_basic_auth_challenge response
51
- end
52
- end
53
-
54
- should 'return application output if correct credentials are specified' do
55
- request_with_basic_auth 'Boss', 'password' do |response|
56
- response.status.should.equal 200
57
- response.body.to_s.should.equal 'Hi Boss'
58
- end
59
- end
60
-
61
- should 'return 400 Bad Request if different auth scheme used' do
62
- request 'HTTP_AUTHORIZATION' => 'Digest params' do |response|
63
- response.should.be.a.client_error
64
- response.status.should.equal 400
65
- response.should.not.include 'WWW-Authenticate'
66
- end
67
- end
68
-
69
- should 'return 400 Bad Request for a malformed authorization header' do
70
- request 'HTTP_AUTHORIZATION' => '' do |response|
71
- response.should.be.a.client_error
72
- response.status.should.equal 400
73
- response.should.not.include 'WWW-Authenticate'
74
- end
75
- end
76
-
77
- it 'takes realm as optional constructor arg' do
78
- app = Rack::Auth::Basic.new(unprotected_app, realm) { true }
79
- realm.should == app.realm
80
- end
81
- end
@@ -1,259 +0,0 @@
1
- require 'rack/auth/digest/md5'
2
- require 'rack/lint'
3
- require 'rack/mock'
4
-
5
- describe Rack::Auth::Digest::MD5 do
6
- def realm
7
- 'WallysWorld'
8
- end
9
-
10
- def unprotected_app
11
- Rack::Lint.new lambda { |env|
12
- friend = Rack::Utils.parse_query(env["QUERY_STRING"])["friend"]
13
- [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}#{friend ? " and #{friend}" : ''}"] ]
14
- }
15
- end
16
-
17
- def protected_app
18
- Rack::Auth::Digest::MD5.new(unprotected_app, :realm => realm, :opaque => 'this-should-be-secret') do |username|
19
- { 'Alice' => 'correct-password' }[username]
20
- end
21
- end
22
-
23
- def protected_app_with_hashed_passwords
24
- app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
25
- username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil
26
- end
27
- app.realm = realm
28
- app.opaque = 'this-should-be-secret'
29
- app.passwords_hashed = true
30
- app
31
- end
32
-
33
- def partially_protected_app
34
- Rack::URLMap.new({
35
- '/' => unprotected_app,
36
- '/protected' => protected_app
37
- })
38
- end
39
-
40
- def protected_app_with_method_override
41
- Rack::MethodOverride.new(protected_app)
42
- end
43
-
44
- before do
45
- @request = Rack::MockRequest.new(protected_app)
46
- end
47
-
48
- def request(method, path, headers = {}, &block)
49
- response = @request.request(method, path, headers)
50
- block.call(response) if block
51
- return response
52
- end
53
-
54
- class MockDigestRequest
55
- def initialize(params)
56
- @params = params
57
- end
58
- def method_missing(sym)
59
- if @params.has_key? k = sym.to_s
60
- return @params[k]
61
- end
62
- super
63
- end
64
- def method
65
- @params['method']
66
- end
67
- def response(password)
68
- Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
69
- end
70
- end
71
-
72
- def request_with_digest_auth(method, path, username, password, options = {}, &block)
73
- request_options = {}
74
- request_options[:input] = options.delete(:input) if options.include? :input
75
-
76
- response = request(method, path, request_options)
77
-
78
- return response unless response.status == 401
79
-
80
- if wait = options.delete(:wait)
81
- sleep wait
82
- end
83
-
84
- challenge = response['WWW-Authenticate'].split(' ', 2).last
85
-
86
- params = Rack::Auth::Digest::Params.parse(challenge)
87
-
88
- params['username'] = username
89
- params['nc'] = '00000001'
90
- params['cnonce'] = 'nonsensenonce'
91
- params['uri'] = path
92
-
93
- params['method'] = method
94
-
95
- params.update options
96
-
97
- params['response'] = MockDigestRequest.new(params).response(password)
98
-
99
- request(method, path, request_options.merge('HTTP_AUTHORIZATION' => "Digest #{params}"), &block)
100
- end
101
-
102
- def assert_digest_auth_challenge(response)
103
- response.should.be.a.client_error
104
- response.status.should.equal 401
105
- response.should.include 'WWW-Authenticate'
106
- response.headers['WWW-Authenticate'].should =~ /^Digest /
107
- response.body.should.be.empty
108
- end
109
-
110
- def assert_bad_request(response)
111
- response.should.be.a.client_error
112
- response.status.should.equal 400
113
- response.should.not.include 'WWW-Authenticate'
114
- end
115
-
116
- should 'challenge when no credentials are specified' do
117
- request 'GET', '/' do |response|
118
- assert_digest_auth_challenge response
119
- end
120
- end
121
-
122
- should 'return application output if correct credentials given' do
123
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
124
- response.status.should.equal 200
125
- response.body.to_s.should.equal 'Hi Alice'
126
- end
127
- end
128
-
129
- should 'return application output if correct credentials given (hashed passwords)' do
130
- @request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
131
-
132
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password' do |response|
133
- response.status.should.equal 200
134
- response.body.to_s.should.equal 'Hi Alice'
135
- end
136
- end
137
-
138
- should 'rechallenge if incorrect username given' do
139
- request_with_digest_auth 'GET', '/', 'Bob', 'correct-password' do |response|
140
- assert_digest_auth_challenge response
141
- end
142
- end
143
-
144
- should 'rechallenge if incorrect password given' do
145
- request_with_digest_auth 'GET', '/', 'Alice', 'wrong-password' do |response|
146
- assert_digest_auth_challenge response
147
- end
148
- end
149
-
150
- should 'rechallenge if incorrect user and blank password given' do
151
- request_with_digest_auth 'GET', '/', 'Bob', '' do |response|
152
- assert_digest_auth_challenge response
153
- end
154
- end
155
-
156
- should 'not rechallenge if nonce is not stale' do
157
- begin
158
- Rack::Auth::Digest::Nonce.time_limit = 10
159
-
160
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 1 do |response|
161
- response.status.should.equal 200
162
- response.body.to_s.should.equal 'Hi Alice'
163
- response.headers['WWW-Authenticate'].should.not =~ /\bstale=true\b/
164
- end
165
- ensure
166
- Rack::Auth::Digest::Nonce.time_limit = nil
167
- end
168
- end
169
-
170
- should 'rechallenge with stale parameter if nonce is stale' do
171
- begin
172
- Rack::Auth::Digest::Nonce.time_limit = 1
173
-
174
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', :wait => 2 do |response|
175
- assert_digest_auth_challenge response
176
- response.headers['WWW-Authenticate'].should =~ /\bstale=true\b/
177
- end
178
- ensure
179
- Rack::Auth::Digest::Nonce.time_limit = nil
180
- end
181
- end
182
-
183
- should 'return 400 Bad Request if incorrect qop given' do
184
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
185
- assert_bad_request response
186
- end
187
- end
188
-
189
- should 'return 400 Bad Request if incorrect uri given' do
190
- request_with_digest_auth 'GET', '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
191
- assert_bad_request response
192
- end
193
- end
194
-
195
- should 'return 400 Bad Request if different auth scheme used' do
196
- request 'GET', '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
197
- assert_bad_request response
198
- end
199
- end
200
-
201
- should 'not require credentials for unprotected path' do
202
- @request = Rack::MockRequest.new(partially_protected_app)
203
- request 'GET', '/' do |response|
204
- response.should.be.ok
205
- end
206
- end
207
-
208
- should 'challenge when no credentials are specified for protected path' do
209
- @request = Rack::MockRequest.new(partially_protected_app)
210
- request 'GET', '/protected' do |response|
211
- assert_digest_auth_challenge response
212
- end
213
- end
214
-
215
- should 'return application output if correct credentials given for protected path' do
216
- @request = Rack::MockRequest.new(partially_protected_app)
217
- request_with_digest_auth 'GET', '/protected', 'Alice', 'correct-password' do |response|
218
- response.status.should.equal 200
219
- response.body.to_s.should.equal 'Hi Alice'
220
- end
221
- end
222
-
223
- should 'return application output when used with a query string and path as uri' do
224
- @request = Rack::MockRequest.new(partially_protected_app)
225
- request_with_digest_auth 'GET', '/protected?friend=Mike', 'Alice', 'correct-password' do |response|
226
- response.status.should.equal 200
227
- response.body.to_s.should.equal 'Hi Alice and Mike'
228
- end
229
- end
230
-
231
- should 'return application output when used with a query string and fullpath as uri' do
232
- @request = Rack::MockRequest.new(partially_protected_app)
233
- qs_uri = '/protected?friend=Mike'
234
- request_with_digest_auth 'GET', qs_uri, 'Alice', 'correct-password', 'uri' => qs_uri do |response|
235
- response.status.should.equal 200
236
- response.body.to_s.should.equal 'Hi Alice and Mike'
237
- end
238
- end
239
-
240
- should 'return application output if correct credentials given for POST' do
241
- request_with_digest_auth 'POST', '/', 'Alice', 'correct-password' do |response|
242
- response.status.should.equal 200
243
- response.body.to_s.should.equal 'Hi Alice'
244
- end
245
- end
246
-
247
- should 'return application output if correct credentials given for PUT (using method override of POST)' do
248
- @request = Rack::MockRequest.new(protected_app_with_method_override)
249
- request_with_digest_auth 'POST', '/', 'Alice', 'correct-password', :input => "_method=put" do |response|
250
- response.status.should.equal 200
251
- response.body.to_s.should.equal 'Hi Alice'
252
- end
253
- end
254
-
255
- it 'takes realm as optional constructor arg' do
256
- app = Rack::Auth::Digest::MD5.new(unprotected_app, realm) { true }
257
- realm.should == app.realm
258
- end
259
- end