rack 1.4.7 → 2.1.4

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 (183) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +77 -0
  3. data/{COPYING → MIT-LICENSE} +4 -2
  4. data/README.rdoc +122 -456
  5. data/Rakefile +32 -31
  6. data/SPEC +119 -29
  7. data/bin/rackup +1 -0
  8. data/contrib/rack_logo.svg +164 -111
  9. data/example/lobster.ru +2 -0
  10. data/example/protectedlobster.rb +4 -2
  11. data/example/protectedlobster.ru +3 -1
  12. data/lib/rack/auth/abstract/handler.rb +7 -5
  13. data/lib/rack/auth/abstract/request.rb +8 -6
  14. data/lib/rack/auth/basic.rb +5 -2
  15. data/lib/rack/auth/digest/md5.rb +10 -8
  16. data/lib/rack/auth/digest/nonce.rb +6 -3
  17. data/lib/rack/auth/digest/params.rb +5 -4
  18. data/lib/rack/auth/digest/request.rb +4 -2
  19. data/lib/rack/body_proxy.rb +11 -9
  20. data/lib/rack/builder.rb +63 -20
  21. data/lib/rack/cascade.rb +10 -9
  22. data/lib/rack/chunked.rb +45 -11
  23. data/lib/rack/{commonlogger.rb → common_logger.rb} +24 -15
  24. data/lib/rack/{conditionalget.rb → conditional_get.rb} +20 -6
  25. data/lib/rack/config.rb +7 -0
  26. data/lib/rack/content_length.rb +12 -6
  27. data/lib/rack/content_type.rb +4 -2
  28. data/lib/rack/core_ext/regexp.rb +14 -0
  29. data/lib/rack/deflater.rb +73 -42
  30. data/lib/rack/directory.rb +77 -56
  31. data/lib/rack/etag.rb +25 -13
  32. data/lib/rack/events.rb +156 -0
  33. data/lib/rack/file.rb +4 -143
  34. data/lib/rack/files.rb +178 -0
  35. data/lib/rack/handler/cgi.rb +18 -17
  36. data/lib/rack/handler/fastcgi.rb +21 -17
  37. data/lib/rack/handler/lsws.rb +14 -12
  38. data/lib/rack/handler/scgi.rb +27 -21
  39. data/lib/rack/handler/thin.rb +19 -5
  40. data/lib/rack/handler/webrick.rb +66 -24
  41. data/lib/rack/handler.rb +29 -19
  42. data/lib/rack/head.rb +21 -14
  43. data/lib/rack/lint.rb +259 -65
  44. data/lib/rack/lobster.rb +17 -10
  45. data/lib/rack/lock.rb +19 -10
  46. data/lib/rack/logger.rb +4 -2
  47. data/lib/rack/media_type.rb +43 -0
  48. data/lib/rack/method_override.rb +52 -0
  49. data/lib/rack/mime.rb +43 -6
  50. data/lib/rack/mock.rb +109 -44
  51. data/lib/rack/multipart/generator.rb +11 -12
  52. data/lib/rack/multipart/parser.rb +302 -115
  53. data/lib/rack/multipart/uploaded_file.rb +4 -3
  54. data/lib/rack/multipart.rb +40 -9
  55. data/lib/rack/null_logger.rb +39 -0
  56. data/lib/rack/query_parser.rb +218 -0
  57. data/lib/rack/recursive.rb +14 -11
  58. data/lib/rack/reloader.rb +12 -5
  59. data/lib/rack/request.rb +484 -270
  60. data/lib/rack/response.rb +196 -77
  61. data/lib/rack/rewindable_input.rb +5 -14
  62. data/lib/rack/runtime.rb +13 -6
  63. data/lib/rack/sendfile.rb +44 -20
  64. data/lib/rack/server.rb +175 -61
  65. data/lib/rack/session/abstract/id.rb +276 -133
  66. data/lib/rack/session/cookie.rb +75 -40
  67. data/lib/rack/session/memcache.rb +4 -87
  68. data/lib/rack/session/pool.rb +24 -18
  69. data/lib/rack/show_exceptions.rb +392 -0
  70. data/lib/rack/{showstatus.rb → show_status.rb} +11 -9
  71. data/lib/rack/static.rb +65 -38
  72. data/lib/rack/tempfile_reaper.rb +24 -0
  73. data/lib/rack/urlmap.rb +40 -15
  74. data/lib/rack/utils.rb +316 -285
  75. data/lib/rack.rb +78 -23
  76. data/rack.gemspec +26 -19
  77. metadata +44 -209
  78. data/KNOWN-ISSUES +0 -30
  79. data/lib/rack/backports/uri/common_18.rb +0 -56
  80. data/lib/rack/backports/uri/common_192.rb +0 -52
  81. data/lib/rack/backports/uri/common_193.rb +0 -29
  82. data/lib/rack/handler/evented_mongrel.rb +0 -8
  83. data/lib/rack/handler/mongrel.rb +0 -100
  84. data/lib/rack/handler/swiftiplied_mongrel.rb +0 -8
  85. data/lib/rack/methodoverride.rb +0 -33
  86. data/lib/rack/nulllogger.rb +0 -18
  87. data/lib/rack/showexceptions.rb +0 -378
  88. data/test/builder/anything.rb +0 -5
  89. data/test/builder/comment.ru +0 -4
  90. data/test/builder/end.ru +0 -5
  91. data/test/builder/line.ru +0 -1
  92. data/test/builder/options.ru +0 -2
  93. data/test/cgi/assets/folder/test.js +0 -1
  94. data/test/cgi/assets/fonts/font.eot +0 -1
  95. data/test/cgi/assets/images/image.png +0 -1
  96. data/test/cgi/assets/index.html +0 -1
  97. data/test/cgi/assets/javascripts/app.js +0 -1
  98. data/test/cgi/assets/stylesheets/app.css +0 -1
  99. data/test/cgi/lighttpd.conf +0 -26
  100. data/test/cgi/lighttpd.errors +0 -1
  101. data/test/cgi/rackup_stub.rb +0 -6
  102. data/test/cgi/sample_rackup.ru +0 -5
  103. data/test/cgi/test +0 -9
  104. data/test/cgi/test+directory/test+file +0 -1
  105. data/test/cgi/test.fcgi +0 -8
  106. data/test/cgi/test.ru +0 -5
  107. data/test/gemloader.rb +0 -10
  108. data/test/multipart/bad_robots +0 -259
  109. data/test/multipart/binary +0 -0
  110. data/test/multipart/content_type_and_no_filename +0 -6
  111. data/test/multipart/empty +0 -10
  112. data/test/multipart/fail_16384_nofile +0 -814
  113. data/test/multipart/file1.txt +0 -1
  114. data/test/multipart/filename_and_modification_param +0 -7
  115. data/test/multipart/filename_with_escaped_quotes +0 -6
  116. data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
  117. data/test/multipart/filename_with_percent_escaped_quotes +0 -6
  118. data/test/multipart/filename_with_unescaped_percentages +0 -6
  119. data/test/multipart/filename_with_unescaped_percentages2 +0 -6
  120. data/test/multipart/filename_with_unescaped_percentages3 +0 -6
  121. data/test/multipart/filename_with_unescaped_quotes +0 -6
  122. data/test/multipart/ie +0 -6
  123. data/test/multipart/mixed_files +0 -21
  124. data/test/multipart/nested +0 -10
  125. data/test/multipart/none +0 -9
  126. data/test/multipart/semicolon +0 -6
  127. data/test/multipart/text +0 -15
  128. data/test/multipart/three_files_three_fields +0 -31
  129. data/test/multipart/webkit +0 -32
  130. data/test/rackup/config.ru +0 -31
  131. data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
  132. data/test/spec_auth.rb +0 -57
  133. data/test/spec_auth_basic.rb +0 -81
  134. data/test/spec_auth_digest.rb +0 -259
  135. data/test/spec_body_proxy.rb +0 -69
  136. data/test/spec_builder.rb +0 -207
  137. data/test/spec_cascade.rb +0 -61
  138. data/test/spec_cgi.rb +0 -102
  139. data/test/spec_chunked.rb +0 -87
  140. data/test/spec_commonlogger.rb +0 -57
  141. data/test/spec_conditionalget.rb +0 -102
  142. data/test/spec_config.rb +0 -22
  143. data/test/spec_content_length.rb +0 -86
  144. data/test/spec_content_type.rb +0 -45
  145. data/test/spec_deflater.rb +0 -187
  146. data/test/spec_directory.rb +0 -88
  147. data/test/spec_etag.rb +0 -98
  148. data/test/spec_fastcgi.rb +0 -107
  149. data/test/spec_file.rb +0 -200
  150. data/test/spec_handler.rb +0 -59
  151. data/test/spec_head.rb +0 -48
  152. data/test/spec_lint.rb +0 -515
  153. data/test/spec_lobster.rb +0 -58
  154. data/test/spec_lock.rb +0 -167
  155. data/test/spec_logger.rb +0 -23
  156. data/test/spec_methodoverride.rb +0 -72
  157. data/test/spec_mock.rb +0 -269
  158. data/test/spec_mongrel.rb +0 -182
  159. data/test/spec_multipart.rb +0 -479
  160. data/test/spec_nulllogger.rb +0 -23
  161. data/test/spec_recursive.rb +0 -72
  162. data/test/spec_request.rb +0 -955
  163. data/test/spec_response.rb +0 -313
  164. data/test/spec_rewindable_input.rb +0 -118
  165. data/test/spec_runtime.rb +0 -49
  166. data/test/spec_sendfile.rb +0 -90
  167. data/test/spec_server.rb +0 -121
  168. data/test/spec_session_abstract_id.rb +0 -43
  169. data/test/spec_session_cookie.rb +0 -361
  170. data/test/spec_session_memcache.rb +0 -321
  171. data/test/spec_session_pool.rb +0 -209
  172. data/test/spec_showexceptions.rb +0 -92
  173. data/test/spec_showstatus.rb +0 -84
  174. data/test/spec_static.rb +0 -145
  175. data/test/spec_thin.rb +0 -86
  176. data/test/spec_urlmap.rb +0 -213
  177. data/test/spec_utils.rb +0 -554
  178. data/test/spec_webrick.rb +0 -143
  179. data/test/static/another/index.html +0 -1
  180. data/test/static/index.html +0 -1
  181. data/test/testrequest.rb +0 -78
  182. data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
  183. 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; 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,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,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
data/test/spec_auth.rb DELETED
@@ -1,57 +0,0 @@
1
- require 'rack'
2
-
3
- describe Rack::Auth do
4
- it "should have all common authentication schemes" do
5
- Rack::Auth.schemes.should.include? 'basic'
6
- Rack::Auth.schemes.should.include? 'digest'
7
- Rack::Auth.schemes.should.include? 'bearer'
8
- Rack::Auth.schemes.should.include? 'token'
9
- end
10
-
11
- it "should allow registration of new auth schemes" do
12
- Rack::Auth.schemes.should.not.include "test"
13
- Rack::Auth.add_scheme "test"
14
- Rack::Auth.schemes.should.include "test"
15
- end
16
- end
17
-
18
- describe Rack::Auth::AbstractRequest do
19
- it "should symbolize known auth schemes" do
20
- env = Rack::MockRequest.env_for('/')
21
- env['HTTP_AUTHORIZATION'] = 'Basic aXJyZXNwb25zaWJsZQ=='
22
- req = Rack::Auth::AbstractRequest.new(env)
23
- req.scheme.should.equal :basic
24
-
25
-
26
- env['HTTP_AUTHORIZATION'] = 'Digest aXJyZXNwb25zaWJsZQ=='
27
- req = Rack::Auth::AbstractRequest.new(env)
28
- req.scheme.should.equal :digest
29
-
30
- env['HTTP_AUTHORIZATION'] = 'Bearer aXJyZXNwb25zaWJsZQ=='
31
- req = Rack::Auth::AbstractRequest.new(env)
32
- req.scheme.should.equal :bearer
33
-
34
- env['HTTP_AUTHORIZATION'] = 'MAC aXJyZXNwb25zaWJsZQ=='
35
- req = Rack::Auth::AbstractRequest.new(env)
36
- req.scheme.should.equal :mac
37
-
38
- env['HTTP_AUTHORIZATION'] = 'Token aXJyZXNwb25zaWJsZQ=='
39
- req = Rack::Auth::AbstractRequest.new(env)
40
- req.scheme.should.equal :token
41
-
42
- env['HTTP_AUTHORIZATION'] = 'OAuth aXJyZXNwb25zaWJsZQ=='
43
- req = Rack::Auth::AbstractRequest.new(env)
44
- req.scheme.should.equal :oauth
45
-
46
- env['HTTP_AUTHORIZATION'] = 'OAuth2 aXJyZXNwb25zaWJsZQ=='
47
- req = Rack::Auth::AbstractRequest.new(env)
48
- req.scheme.should.equal :oauth2
49
- end
50
-
51
- it "should not symbolize unknown auth schemes" do
52
- env = Rack::MockRequest.env_for('/')
53
- env['HTTP_AUTHORIZATION'] = 'magic aXJyZXNwb25zaWJsZQ=='
54
- req = Rack::Auth::AbstractRequest.new(env)
55
- req.scheme.should == "magic"
56
- end
57
- 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