sinatra 0.2.1 → 0.2.2

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

Potentially problematic release.


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

Files changed (83) hide show
  1. data/CHANGELOG +1 -0
  2. data/LICENSE +22 -0
  3. data/Manifest +78 -1
  4. data/lib/sinatra.rb +12 -1
  5. data/sinatra.gemspec +7 -14
  6. data/vendor/rack/AUTHORS +7 -0
  7. data/vendor/rack/COPYING +18 -0
  8. data/vendor/rack/KNOWN-ISSUES +18 -0
  9. data/vendor/rack/README +242 -0
  10. data/vendor/rack/Rakefile +174 -0
  11. data/vendor/rack/bin/rackup +153 -0
  12. data/vendor/rack/contrib/rack_logo.svg +111 -0
  13. data/vendor/rack/example/lobster.ru +4 -0
  14. data/vendor/rack/example/protectedlobster.rb +14 -0
  15. data/vendor/rack/example/protectedlobster.ru +8 -0
  16. data/vendor/rack/lib/rack.rb +92 -0
  17. data/vendor/rack/lib/rack/adapter/camping.rb +22 -0
  18. data/vendor/rack/lib/rack/auth/abstract/handler.rb +28 -0
  19. data/vendor/rack/lib/rack/auth/abstract/request.rb +37 -0
  20. data/vendor/rack/lib/rack/auth/basic.rb +58 -0
  21. data/vendor/rack/lib/rack/auth/digest/md5.rb +124 -0
  22. data/vendor/rack/lib/rack/auth/digest/nonce.rb +51 -0
  23. data/vendor/rack/lib/rack/auth/digest/params.rb +55 -0
  24. data/vendor/rack/lib/rack/auth/digest/request.rb +40 -0
  25. data/vendor/rack/lib/rack/auth/openid.rb +116 -0
  26. data/vendor/rack/lib/rack/builder.rb +56 -0
  27. data/vendor/rack/lib/rack/cascade.rb +36 -0
  28. data/vendor/rack/lib/rack/commonlogger.rb +56 -0
  29. data/vendor/rack/lib/rack/file.rb +112 -0
  30. data/vendor/rack/lib/rack/handler/cgi.rb +57 -0
  31. data/vendor/rack/lib/rack/handler/fastcgi.rb +83 -0
  32. data/vendor/rack/lib/rack/handler/lsws.rb +52 -0
  33. data/vendor/rack/lib/rack/handler/mongrel.rb +78 -0
  34. data/vendor/rack/lib/rack/handler/scgi.rb +57 -0
  35. data/vendor/rack/lib/rack/handler/webrick.rb +57 -0
  36. data/vendor/rack/lib/rack/lint.rb +394 -0
  37. data/vendor/rack/lib/rack/lobster.rb +65 -0
  38. data/vendor/rack/lib/rack/mock.rb +160 -0
  39. data/vendor/rack/lib/rack/recursive.rb +57 -0
  40. data/vendor/rack/lib/rack/reloader.rb +64 -0
  41. data/vendor/rack/lib/rack/request.rb +197 -0
  42. data/vendor/rack/lib/rack/response.rb +166 -0
  43. data/vendor/rack/lib/rack/session/abstract/id.rb +126 -0
  44. data/vendor/rack/lib/rack/session/cookie.rb +71 -0
  45. data/vendor/rack/lib/rack/session/memcache.rb +83 -0
  46. data/vendor/rack/lib/rack/session/pool.rb +67 -0
  47. data/vendor/rack/lib/rack/showexceptions.rb +344 -0
  48. data/vendor/rack/lib/rack/showstatus.rb +103 -0
  49. data/vendor/rack/lib/rack/static.rb +38 -0
  50. data/vendor/rack/lib/rack/urlmap.rb +48 -0
  51. data/vendor/rack/lib/rack/utils.rb +240 -0
  52. data/vendor/rack/test/cgi/lighttpd.conf +20 -0
  53. data/vendor/rack/test/cgi/test +9 -0
  54. data/vendor/rack/test/cgi/test.fcgi +7 -0
  55. data/vendor/rack/test/cgi/test.ru +7 -0
  56. data/vendor/rack/test/spec_rack_auth_basic.rb +69 -0
  57. data/vendor/rack/test/spec_rack_auth_digest.rb +169 -0
  58. data/vendor/rack/test/spec_rack_builder.rb +50 -0
  59. data/vendor/rack/test/spec_rack_camping.rb +47 -0
  60. data/vendor/rack/test/spec_rack_cascade.rb +50 -0
  61. data/vendor/rack/test/spec_rack_cgi.rb +91 -0
  62. data/vendor/rack/test/spec_rack_commonlogger.rb +32 -0
  63. data/vendor/rack/test/spec_rack_fastcgi.rb +91 -0
  64. data/vendor/rack/test/spec_rack_file.rb +40 -0
  65. data/vendor/rack/test/spec_rack_lint.rb +317 -0
  66. data/vendor/rack/test/spec_rack_lobster.rb +45 -0
  67. data/vendor/rack/test/spec_rack_mock.rb +152 -0
  68. data/vendor/rack/test/spec_rack_mongrel.rb +165 -0
  69. data/vendor/rack/test/spec_rack_recursive.rb +77 -0
  70. data/vendor/rack/test/spec_rack_request.rb +384 -0
  71. data/vendor/rack/test/spec_rack_response.rb +167 -0
  72. data/vendor/rack/test/spec_rack_session_cookie.rb +49 -0
  73. data/vendor/rack/test/spec_rack_session_memcache.rb +100 -0
  74. data/vendor/rack/test/spec_rack_session_pool.rb +84 -0
  75. data/vendor/rack/test/spec_rack_showexceptions.rb +21 -0
  76. data/vendor/rack/test/spec_rack_showstatus.rb +71 -0
  77. data/vendor/rack/test/spec_rack_static.rb +37 -0
  78. data/vendor/rack/test/spec_rack_urlmap.rb +175 -0
  79. data/vendor/rack/test/spec_rack_utils.rb +57 -0
  80. data/vendor/rack/test/spec_rack_webrick.rb +106 -0
  81. data/vendor/rack/test/testrequest.rb +43 -0
  82. metadata +81 -4
  83. data/Rakefile +0 -24
@@ -0,0 +1,48 @@
1
+ module Rack
2
+ # Rack::URLMap takes a hash mapping urls or paths to apps, and
3
+ # dispatches accordingly. Support for HTTP/1.1 host names exists if
4
+ # the URLs start with <tt>http://</tt> or <tt>https://</tt>.
5
+ #
6
+ # URLMap modifies the SCRIPT_NAME and PATH_INFO such that the part
7
+ # relevant for dispatch is in the SCRIPT_NAME, and the rest in the
8
+ # PATH_INFO. This should be taken care of when you need to
9
+ # reconstruct the URL in order to create links.
10
+ #
11
+ # URLMap dispatches in such a way that the longest paths are tried
12
+ # first, since they are most specific.
13
+
14
+ class URLMap
15
+ def initialize(map)
16
+ @mapping = map.map { |location, app|
17
+ if location =~ %r{\Ahttps?://(.*?)(/.*)}
18
+ host, location = $1, $2
19
+ else
20
+ host = nil
21
+ end
22
+
23
+ unless location[0] == ?/
24
+ raise ArgumentError, "paths need to start with /"
25
+ end
26
+ location = location.chomp('/')
27
+
28
+ [host, location, app]
29
+ }.sort_by { |(h, l, a)| -l.size } # Longest path first
30
+ end
31
+
32
+ def call(env)
33
+ path = env["PATH_INFO"].to_s.squeeze("/")
34
+ hHost, sName, sPort = env.values_at('HTTP_HOST','SERVER_NAME','SERVER_PORT')
35
+ @mapping.each { |host, location, app|
36
+ next unless (hHost == host || sName == host \
37
+ || (host.nil? && (hHost == sName || hHost == sName+':'+sPort)))
38
+ next unless location == path[0, location.size]
39
+ next unless path[location.size] == nil || path[location.size] == ?/
40
+ env["SCRIPT_NAME"] += location
41
+ env["PATH_INFO"] = path[location.size..-1]
42
+ return app.call(env)
43
+ }
44
+ [404, {"Content-Type" => "text/plain"}, ["Not Found: #{path}"]]
45
+ end
46
+ end
47
+ end
48
+
@@ -0,0 +1,240 @@
1
+ require 'tempfile'
2
+
3
+ module Rack
4
+ # Rack::Utils contains a grab-bag of useful methods for writing web
5
+ # applications adopted from all kinds of Ruby libraries.
6
+
7
+ module Utils
8
+ # Performs URI escaping so that you can construct proper
9
+ # query strings faster. Use this rather than the cgi.rb
10
+ # version since it's faster. (Stolen from Camping).
11
+ def escape(s)
12
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
13
+ '%'+$1.unpack('H2'*$1.size).join('%').upcase
14
+ }.tr(' ', '+')
15
+ end
16
+ module_function :escape
17
+
18
+ # Unescapes a URI escaped string. (Stolen from Camping).
19
+ def unescape(s)
20
+ s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
21
+ [$1.delete('%')].pack('H*')
22
+ }
23
+ end
24
+ module_function :unescape
25
+
26
+ # Stolen from Mongrel:
27
+ # Parses a query string by breaking it up at the '&'
28
+ # and ';' characters. You can also use this to parse
29
+ # cookies by changing the characters used in the second
30
+ # parameter (which defaults to '&;').
31
+
32
+ def parse_query(qs, d = '&;')
33
+ params = {}
34
+ (qs||'').split(/[#{d}] */n).inject(params) { |h,p|
35
+ k, v=unescape(p).split('=',2)
36
+ if cur = params[k]
37
+ if cur.class == Array
38
+ params[k] << v
39
+ else
40
+ params[k] = [cur, v]
41
+ end
42
+ else
43
+ params[k] = v
44
+ end
45
+ }
46
+
47
+ return params
48
+ end
49
+ module_function :parse_query
50
+
51
+ # Escape ampersands, brackets and quotes to their HTML/XML entities.
52
+ def escape_html(string)
53
+ string.to_s.gsub("&", "&amp;").
54
+ gsub("<", "&lt;").
55
+ gsub(">", "&gt;").
56
+ gsub("'", "&#39;").
57
+ gsub('"', "&quot;")
58
+ end
59
+ module_function :escape_html
60
+
61
+ class Context < Proc
62
+ attr_reader :for, :app
63
+ def initialize app_f=nil, app_r=nil
64
+ @for, @app = app_f, app_r
65
+ end
66
+ alias_method :old_inspect, :inspect
67
+ def inspect
68
+ "#{old_inspect} ==> #{@for.inspect} ==> #{@app.inspect}"
69
+ end
70
+ def pretty_print pp
71
+ pp.text old_inspect
72
+ pp.nest 1 do
73
+ pp.breakable
74
+ pp.text '=for> '
75
+ pp.pp @for
76
+ pp.breakable
77
+ pp.text '=app> '
78
+ pp.pp @app
79
+ end
80
+ end
81
+ end
82
+
83
+ # A case-normalizing Hash, adjusting on [] and []=.
84
+ class HeaderHash < Hash
85
+ def initialize(hash={})
86
+ hash.each { |k, v| self[k] = v }
87
+ end
88
+
89
+ def to_hash
90
+ {}.replace(self)
91
+ end
92
+
93
+ def [](k)
94
+ super capitalize(k)
95
+ end
96
+
97
+ def []=(k, v)
98
+ super capitalize(k), v
99
+ end
100
+
101
+ def capitalize(k)
102
+ k.to_s.downcase.gsub(/^.|[-_\s]./) { |x| x.upcase }
103
+ end
104
+ end
105
+
106
+ # Every standard HTTP code mapped to the appropriate message.
107
+ # Stolen from Mongrel.
108
+ HTTP_STATUS_CODES = {
109
+ 100 => 'Continue',
110
+ 101 => 'Switching Protocols',
111
+ 200 => 'OK',
112
+ 201 => 'Created',
113
+ 202 => 'Accepted',
114
+ 203 => 'Non-Authoritative Information',
115
+ 204 => 'No Content',
116
+ 205 => 'Reset Content',
117
+ 206 => 'Partial Content',
118
+ 300 => 'Multiple Choices',
119
+ 301 => 'Moved Permanently',
120
+ 302 => 'Moved Temporarily',
121
+ 303 => 'See Other',
122
+ 304 => 'Not Modified',
123
+ 305 => 'Use Proxy',
124
+ 400 => 'Bad Request',
125
+ 401 => 'Unauthorized',
126
+ 402 => 'Payment Required',
127
+ 403 => 'Forbidden',
128
+ 404 => 'Not Found',
129
+ 405 => 'Method Not Allowed',
130
+ 406 => 'Not Acceptable',
131
+ 407 => 'Proxy Authentication Required',
132
+ 408 => 'Request Time-out',
133
+ 409 => 'Conflict',
134
+ 410 => 'Gone',
135
+ 411 => 'Length Required',
136
+ 412 => 'Precondition Failed',
137
+ 413 => 'Request Entity Too Large',
138
+ 414 => 'Request-URI Too Large',
139
+ 415 => 'Unsupported Media Type',
140
+ 500 => 'Internal Server Error',
141
+ 501 => 'Not Implemented',
142
+ 502 => 'Bad Gateway',
143
+ 503 => 'Service Unavailable',
144
+ 504 => 'Gateway Time-out',
145
+ 505 => 'HTTP Version not supported'
146
+ }
147
+
148
+ # A multipart form data parser, adapted from IOWA.
149
+ #
150
+ # Usually, Rack::Request#POST takes care of calling this.
151
+
152
+ module Multipart
153
+ EOL = "\r\n"
154
+
155
+ def self.parse_multipart(env)
156
+ unless env['CONTENT_TYPE'] =~
157
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
158
+ nil
159
+ else
160
+ boundary = "--#{$1}"
161
+
162
+ params = {}
163
+ buf = ""
164
+ content_length = env['CONTENT_LENGTH'].to_i
165
+ input = env['rack.input']
166
+
167
+ boundary_size = boundary.size + EOL.size
168
+ bufsize = 16384
169
+
170
+ content_length -= boundary_size
171
+
172
+ status = input.read(boundary_size)
173
+ raise EOFError, "bad content body" unless status == boundary + EOL
174
+
175
+ rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/
176
+
177
+ loop {
178
+ head = nil
179
+ body = ''
180
+ filename = content_type = name = nil
181
+
182
+ until head && buf =~ rx
183
+ if !head && i = buf.index("\r\n\r\n")
184
+ head = buf.slice!(0, i+2) # First \r\n
185
+ buf.slice!(0, 2) # Second \r\n
186
+
187
+ filename = head[/Content-Disposition:.* filename="?([^\";]*)"?/ni, 1]
188
+ content_type = head[/Content-Type: (.*)\r\n/ni, 1]
189
+ name = head[/Content-Disposition:.* name="?([^\";]*)"?/ni, 1]
190
+
191
+ body = Tempfile.new("RackMultipart") if filename
192
+
193
+ next
194
+ end
195
+
196
+ # Save the read body part.
197
+ if head && (boundary_size+4 < buf.size)
198
+ body << buf.slice!(0, buf.size - (boundary_size+4))
199
+ end
200
+
201
+ c = input.read(bufsize < content_length ? bufsize : content_length)
202
+ raise EOFError, "bad content body" if c.nil? || c.empty?
203
+ buf << c
204
+ content_length -= c.size
205
+ end
206
+
207
+ # Save the rest.
208
+ if i = buf.index(rx)
209
+ body << buf.slice!(0, i)
210
+ buf.slice!(0, boundary_size+2)
211
+
212
+ content_length = -1 if $1 == "--"
213
+ end
214
+
215
+ if filename
216
+ body.rewind
217
+ data = {:filename => filename, :type => content_type,
218
+ :name => name, :tempfile => body, :head => head}
219
+ else
220
+ data = body
221
+ end
222
+
223
+ if name
224
+ if name =~ /\[\]\z/
225
+ params[name] ||= []
226
+ params[name] << data
227
+ else
228
+ params[name] = data
229
+ end
230
+ end
231
+
232
+ break if buf.empty? || content_length == -1
233
+ }
234
+
235
+ params
236
+ end
237
+ end
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,20 @@
1
+ server.modules = ("mod_fastcgi", "mod_cgi")
2
+ server.document-root = "."
3
+ server.errorlog = "lighttpd.errors"
4
+ server.port = 9203
5
+
6
+ server.event-handler = "freebsd-kqueue"
7
+
8
+ cgi.assign = ("/test" => "",
9
+ # ".ru" => ""
10
+ )
11
+
12
+ fastcgi.server = ("test.fcgi" => ("localhost" =>
13
+ ("min-procs" => 1,
14
+ "socket" => "/tmp/rack-test-fcgi",
15
+ "bin-path" => "test.fcgi")),
16
+ "test.ru" => ("localhost" =>
17
+ ("min-procs" => 1,
18
+ "socket" => "/tmp/rack-test-ru-fcgi",
19
+ "bin-path" => "test.ru")),
20
+ )
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- ruby -*-
3
+
4
+ $: << File.join(File.dirname(__FILE__), "..", "..", "lib")
5
+
6
+ require 'rack'
7
+ require '../testrequest'
8
+
9
+ Rack::Handler::CGI.run(Rack::Lint.new(TestRequest.new))
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- ruby -*-
3
+
4
+ require 'rack'
5
+ require '../testrequest'
6
+
7
+ Rack::Handler::FastCGI.run(Rack::Lint.new(TestRequest.new))
@@ -0,0 +1,7 @@
1
+ #!/usr/local/bin/ruby ../../bin/rackup
2
+ #\ -E deployment -I ~/projects/rack/lib
3
+ # -*- ruby -*-
4
+
5
+ require '../testrequest'
6
+
7
+ run TestRequest.new
@@ -0,0 +1,69 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/auth/basic'
4
+ require 'rack/mock'
5
+
6
+ context 'Rack::Auth::Basic' do
7
+
8
+ def realm
9
+ 'WallysWorld'
10
+ end
11
+
12
+ def unprotected_app
13
+ lambda { |env| [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ] }
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
+ setup 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="/
39
+ response.body.should.be.empty
40
+ end
41
+
42
+ specify '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
+ specify '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
+ specify '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
+ specify '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
+ end
@@ -0,0 +1,169 @@
1
+ require 'test/spec'
2
+
3
+ require 'rack/auth/digest/md5'
4
+ require 'rack/mock'
5
+
6
+ context 'Rack::Auth::Digest::MD5' do
7
+
8
+ def realm
9
+ 'WallysWorld'
10
+ end
11
+
12
+ def unprotected_app
13
+ lambda do |env|
14
+ [ 200, {'Content-Type' => 'text/plain'}, ["Hi #{env['REMOTE_USER']}"] ]
15
+ end
16
+ end
17
+
18
+ def protected_app
19
+ app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
20
+ { 'Alice' => 'correct-password' }[username]
21
+ end
22
+ app.realm = realm
23
+ app.opaque = 'this-should-be-secret'
24
+ app
25
+ end
26
+
27
+ def protected_app_with_hashed_passwords
28
+ app = Rack::Auth::Digest::MD5.new(unprotected_app) do |username|
29
+ username == 'Alice' ? Digest::MD5.hexdigest("Alice:#{realm}:correct-password") : nil
30
+ end
31
+ app.realm = realm
32
+ app.opaque = 'this-should-be-secret'
33
+ app.passwords_hashed = true
34
+ app
35
+ end
36
+
37
+ setup do
38
+ @request = Rack::MockRequest.new(protected_app)
39
+ end
40
+
41
+ def request(path, headers = {}, &block)
42
+ response = @request.get(path, headers)
43
+ block.call(response) if block
44
+ return response
45
+ end
46
+
47
+ class MockDigestRequest
48
+ def initialize(params)
49
+ @params = params
50
+ end
51
+ def method_missing(sym)
52
+ if @params.has_key? k = sym.to_s
53
+ return @params[k]
54
+ end
55
+ super
56
+ end
57
+ def method
58
+ 'GET'
59
+ end
60
+ def response(password)
61
+ Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
62
+ end
63
+ end
64
+
65
+ def request_with_digest_auth(path, username, password, options = {}, &block)
66
+ response = request('/')
67
+
68
+ return response unless response.status == 401
69
+
70
+ if wait = options.delete(:wait)
71
+ sleep wait
72
+ end
73
+
74
+ challenge = response['WWW-Authenticate'].split(' ', 2).last
75
+
76
+ params = Rack::Auth::Digest::Params.parse(challenge)
77
+
78
+ params['username'] = username
79
+ params['nc'] = '00000001'
80
+ params['cnonce'] = 'nonsensenonce'
81
+ params['uri'] = path
82
+
83
+ params.update options
84
+
85
+ params['response'] = MockDigestRequest.new(params).response(password)
86
+
87
+ request(path, { 'HTTP_AUTHORIZATION' => "Digest #{params}" }, &block)
88
+ end
89
+
90
+ def assert_digest_auth_challenge(response)
91
+ response.should.be.a.client_error
92
+ response.status.should.equal 401
93
+ response.should.include 'WWW-Authenticate'
94
+ response.headers['WWW-Authenticate'].should =~ /^Digest /
95
+ response.body.should.be.empty
96
+ end
97
+
98
+ def assert_bad_request(response)
99
+ response.should.be.a.client_error
100
+ response.status.should.equal 400
101
+ response.should.not.include 'WWW-Authenticate'
102
+ end
103
+
104
+ specify 'should challenge when no credentials are specified' do
105
+ request '/' do |response|
106
+ assert_digest_auth_challenge response
107
+ end
108
+ end
109
+
110
+ specify 'should return application output if correct credentials given' do
111
+ request_with_digest_auth '/', 'Alice', 'correct-password' do |response|
112
+ response.status.should.equal 200
113
+ response.body.to_s.should.equal 'Hi Alice'
114
+ end
115
+ end
116
+
117
+ specify 'should return application output if correct credentials given (hashed passwords)' do
118
+ @request = Rack::MockRequest.new(protected_app_with_hashed_passwords)
119
+
120
+ request_with_digest_auth '/', 'Alice', 'correct-password' do |response|
121
+ response.status.should.equal 200
122
+ response.body.to_s.should.equal 'Hi Alice'
123
+ end
124
+ end
125
+
126
+ specify 'should rechallenge if incorrect username given' do
127
+ request_with_digest_auth '/', 'Bob', 'correct-password' do |response|
128
+ assert_digest_auth_challenge response
129
+ end
130
+ end
131
+
132
+ specify 'should rechallenge if incorrect password given' do
133
+ request_with_digest_auth '/', 'Alice', 'wrong-password' do |response|
134
+ assert_digest_auth_challenge response
135
+ end
136
+ end
137
+
138
+ specify 'should rechallenge with stale parameter if nonce is stale' do
139
+ begin
140
+ Rack::Auth::Digest::Nonce.time_limit = 1
141
+
142
+ request_with_digest_auth '/', 'Alice', 'correct-password', :wait => 2 do |response|
143
+ assert_digest_auth_challenge response
144
+ response.headers['WWW-Authenticate'].should =~ /\bstale=true\b/
145
+ end
146
+ ensure
147
+ Rack::Auth::Digest::Nonce.time_limit = nil
148
+ end
149
+ end
150
+
151
+ specify 'should return 400 Bad Request if incorrect qop given' do
152
+ request_with_digest_auth '/', 'Alice', 'correct-password', 'qop' => 'auth-int' do |response|
153
+ assert_bad_request response
154
+ end
155
+ end
156
+
157
+ specify 'should return 400 Bad Request if incorrect uri given' do
158
+ request_with_digest_auth '/', 'Alice', 'correct-password', 'uri' => '/foo' do |response|
159
+ assert_bad_request response
160
+ end
161
+ end
162
+
163
+ specify 'should return 400 Bad Request if different auth scheme used' do
164
+ request '/', 'HTTP_AUTHORIZATION' => 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==' do |response|
165
+ assert_bad_request response
166
+ end
167
+ end
168
+
169
+ end