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.
- checksums.yaml +4 -4
- data/{HISTORY.md → CHANGELOG.md} +214 -164
- data/{COPYING → MIT-LICENSE} +4 -2
- data/README.rdoc +79 -133
- data/Rakefile +25 -18
- data/SPEC +9 -9
- data/bin/rackup +1 -0
- data/example/lobster.ru +2 -0
- data/example/protectedlobster.rb +3 -1
- data/example/protectedlobster.ru +2 -0
- data/lib/rack/auth/abstract/handler.rb +3 -1
- data/lib/rack/auth/abstract/request.rb +2 -0
- data/lib/rack/auth/basic.rb +4 -1
- data/lib/rack/auth/digest/md5.rb +9 -7
- data/lib/rack/auth/digest/nonce.rb +6 -3
- data/lib/rack/auth/digest/params.rb +4 -2
- data/lib/rack/auth/digest/request.rb +2 -0
- data/lib/rack/body_proxy.rb +3 -6
- data/lib/rack/builder.rb +38 -15
- data/lib/rack/cascade.rb +6 -5
- data/lib/rack/chunked.rb +29 -6
- data/lib/rack/common_logger.rb +9 -11
- data/lib/rack/conditional_get.rb +3 -1
- data/lib/rack/config.rb +2 -0
- data/lib/rack/content_length.rb +3 -1
- data/lib/rack/content_type.rb +3 -1
- data/lib/rack/core_ext/regexp.rb +14 -0
- data/lib/rack/deflater.rb +28 -17
- data/lib/rack/directory.rb +17 -14
- data/lib/rack/etag.rb +3 -1
- data/lib/rack/events.rb +5 -3
- data/lib/rack/file.rb +5 -173
- data/lib/rack/files.rb +178 -0
- data/lib/rack/handler/cgi.rb +3 -1
- data/lib/rack/handler/fastcgi.rb +4 -2
- data/lib/rack/handler/lsws.rb +3 -1
- data/lib/rack/handler/scgi.rb +9 -6
- data/lib/rack/handler/thin.rb +3 -1
- data/lib/rack/handler/webrick.rb +4 -2
- data/lib/rack/handler.rb +7 -2
- data/lib/rack/head.rb +2 -0
- data/lib/rack/lint.rb +15 -12
- data/lib/rack/lobster.rb +7 -5
- data/lib/rack/lock.rb +2 -0
- data/lib/rack/logger.rb +2 -0
- data/lib/rack/media_type.rb +10 -5
- data/lib/rack/method_override.rb +4 -2
- data/lib/rack/mime.rb +9 -1
- data/lib/rack/mock.rb +74 -15
- data/lib/rack/multipart/generator.rb +6 -7
- data/lib/rack/multipart/parser.rb +55 -62
- data/lib/rack/multipart/uploaded_file.rb +2 -0
- data/lib/rack/multipart.rb +6 -3
- data/lib/rack/null_logger.rb +2 -0
- data/lib/rack/query_parser.rb +51 -25
- data/lib/rack/recursive.rb +7 -5
- data/lib/rack/reloader.rb +10 -4
- data/lib/rack/request.rb +79 -26
- data/lib/rack/response.rb +71 -31
- data/lib/rack/rewindable_input.rb +4 -2
- data/lib/rack/runtime.rb +4 -2
- data/lib/rack/sendfile.rb +15 -8
- data/lib/rack/server.rb +88 -16
- data/lib/rack/session/abstract/id.rb +40 -22
- data/lib/rack/session/cookie.rb +10 -9
- data/lib/rack/session/memcache.rb +4 -93
- data/lib/rack/session/pool.rb +4 -2
- data/lib/rack/show_exceptions.rb +15 -9
- data/lib/rack/show_status.rb +4 -2
- data/lib/rack/static.rb +15 -10
- data/lib/rack/tempfile_reaper.rb +2 -0
- data/lib/rack/urlmap.rb +11 -2
- data/lib/rack/utils.rb +64 -93
- data/lib/rack.rb +63 -60
- data/rack.gemspec +17 -7
- metadata +33 -175
- data/test/builder/an_underscore_app.rb +0 -5
- data/test/builder/anything.rb +0 -5
- data/test/builder/comment.ru +0 -4
- data/test/builder/end.ru +0 -5
- data/test/builder/line.ru +0 -1
- data/test/builder/options.ru +0 -2
- data/test/cgi/assets/folder/test.js +0 -1
- data/test/cgi/assets/fonts/font.eot +0 -1
- data/test/cgi/assets/images/image.png +0 -1
- data/test/cgi/assets/index.html +0 -1
- data/test/cgi/assets/javascripts/app.js +0 -1
- data/test/cgi/assets/stylesheets/app.css +0 -1
- data/test/cgi/lighttpd.conf +0 -26
- data/test/cgi/rackup_stub.rb +0 -6
- data/test/cgi/sample_rackup.ru +0 -5
- data/test/cgi/test +0 -9
- data/test/cgi/test+directory/test+file +0 -1
- data/test/cgi/test.fcgi +0 -9
- data/test/cgi/test.gz +0 -0
- data/test/cgi/test.ru +0 -5
- data/test/gemloader.rb +0 -10
- data/test/helper.rb +0 -34
- data/test/multipart/bad_robots +0 -259
- data/test/multipart/binary +0 -0
- data/test/multipart/content_type_and_no_filename +0 -6
- data/test/multipart/empty +0 -10
- data/test/multipart/fail_16384_nofile +0 -814
- data/test/multipart/file1.txt +0 -1
- data/test/multipart/filename_and_modification_param +0 -7
- data/test/multipart/filename_and_no_name +0 -6
- data/test/multipart/filename_with_encoded_words +0 -7
- data/test/multipart/filename_with_escaped_quotes +0 -6
- data/test/multipart/filename_with_escaped_quotes_and_modification_param +0 -7
- data/test/multipart/filename_with_null_byte +0 -7
- data/test/multipart/filename_with_percent_escaped_quotes +0 -6
- data/test/multipart/filename_with_single_quote +0 -7
- data/test/multipart/filename_with_unescaped_percentages +0 -6
- data/test/multipart/filename_with_unescaped_percentages2 +0 -6
- data/test/multipart/filename_with_unescaped_percentages3 +0 -6
- data/test/multipart/filename_with_unescaped_quotes +0 -6
- data/test/multipart/ie +0 -6
- data/test/multipart/invalid_character +0 -6
- data/test/multipart/mixed_files +0 -21
- data/test/multipart/nested +0 -10
- data/test/multipart/none +0 -9
- data/test/multipart/quoted +0 -15
- data/test/multipart/rack-logo.png +0 -0
- data/test/multipart/semicolon +0 -6
- data/test/multipart/text +0 -15
- data/test/multipart/three_files_three_fields +0 -31
- data/test/multipart/unity3d_wwwform +0 -11
- data/test/multipart/webkit +0 -32
- data/test/rackup/config.ru +0 -31
- data/test/registering_handler/rack/handler/registering_myself.rb +0 -8
- data/test/spec_auth_basic.rb +0 -89
- data/test/spec_auth_digest.rb +0 -260
- data/test/spec_body_proxy.rb +0 -85
- data/test/spec_builder.rb +0 -233
- data/test/spec_cascade.rb +0 -63
- data/test/spec_cgi.rb +0 -84
- data/test/spec_chunked.rb +0 -103
- data/test/spec_common_logger.rb +0 -107
- data/test/spec_conditional_get.rb +0 -103
- data/test/spec_config.rb +0 -23
- data/test/spec_content_length.rb +0 -86
- data/test/spec_content_type.rb +0 -46
- data/test/spec_deflater.rb +0 -375
- data/test/spec_directory.rb +0 -148
- data/test/spec_etag.rb +0 -108
- data/test/spec_events.rb +0 -133
- data/test/spec_fastcgi.rb +0 -85
- data/test/spec_file.rb +0 -264
- data/test/spec_handler.rb +0 -57
- data/test/spec_head.rb +0 -46
- data/test/spec_lint.rb +0 -520
- data/test/spec_lobster.rb +0 -59
- data/test/spec_lock.rb +0 -204
- data/test/spec_logger.rb +0 -24
- data/test/spec_media_type.rb +0 -42
- data/test/spec_method_override.rb +0 -110
- data/test/spec_mime.rb +0 -51
- data/test/spec_mock.rb +0 -359
- data/test/spec_multipart.rb +0 -721
- data/test/spec_null_logger.rb +0 -21
- data/test/spec_recursive.rb +0 -75
- data/test/spec_request.rb +0 -1423
- data/test/spec_response.rb +0 -528
- data/test/spec_rewindable_input.rb +0 -128
- data/test/spec_runtime.rb +0 -50
- data/test/spec_sendfile.rb +0 -125
- data/test/spec_server.rb +0 -193
- data/test/spec_session_abstract_id.rb +0 -31
- data/test/spec_session_abstract_session_hash.rb +0 -45
- data/test/spec_session_cookie.rb +0 -442
- data/test/spec_session_memcache.rb +0 -357
- data/test/spec_session_persisted_secure_secure_session_hash.rb +0 -73
- data/test/spec_session_pool.rb +0 -247
- data/test/spec_show_exceptions.rb +0 -93
- data/test/spec_show_status.rb +0 -104
- data/test/spec_static.rb +0 -184
- data/test/spec_tempfile_reaper.rb +0 -64
- data/test/spec_thin.rb +0 -96
- data/test/spec_urlmap.rb +0 -237
- data/test/spec_utils.rb +0 -742
- data/test/spec_version.rb +0 -11
- data/test/spec_webrick.rb +0 -206
- data/test/static/another/index.html +0 -1
- data/test/static/foo.html +0 -1
- data/test/static/index.html +0 -1
- data/test/testrequest.rb +0 -78
- data/test/unregistered_handler/rack/handler/unregistered.rb +0 -7
- data/test/unregistered_handler/rack/handler/unregistered_long_one.rb +0 -7
data/test/multipart/file1.txt
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
contents
|
data/test/multipart/ie
DELETED
data/test/multipart/mixed_files
DELETED
@@ -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--
|
data/test/multipart/nested
DELETED
data/test/multipart/none
DELETED
data/test/multipart/quoted
DELETED
@@ -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
|
data/test/multipart/semicolon
DELETED
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
|
data/test/multipart/webkit
DELETED
@@ -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--
|
data/test/rackup/config.ru
DELETED
@@ -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
|
data/test/spec_auth_basic.rb
DELETED
@@ -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
|
data/test/spec_auth_digest.rb
DELETED
@@ -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
|