rack-contrib 0.9.0 → 0.9.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rack-contrib might be problematic. Click here for more details.
- data/README.rdoc +16 -0
- data/Rakefile +6 -1
- data/lib/rack/contrib.rb +10 -1
- data/lib/rack/contrib/accept_format.rb +46 -0
- data/lib/rack/contrib/backstage.rb +20 -0
- data/lib/rack/contrib/config.rb +16 -0
- data/lib/rack/contrib/csshttprequest.rb +39 -0
- data/lib/rack/contrib/deflect.rb +137 -0
- data/lib/rack/contrib/jsonp.rb +18 -15
- data/lib/rack/contrib/not_found.rb +18 -0
- data/lib/rack/contrib/post_body_content_type_parser.rb +10 -10
- data/lib/rack/contrib/relative_redirect.rb +44 -0
- data/lib/rack/contrib/response_cache.rb +59 -0
- data/lib/rack/contrib/route_exceptions.rb +20 -19
- data/lib/rack/contrib/signals.rb +63 -0
- data/rack-contrib.gemspec +22 -3
- data/test/404.html +1 -0
- data/test/Maintenance.html +1 -0
- data/test/spec_rack_accept_format.rb +72 -0
- data/test/spec_rack_backstage.rb +26 -0
- data/test/spec_rack_callbacks.rb +2 -2
- data/test/spec_rack_config.rb +22 -0
- data/test/spec_rack_csshttprequest.rb +66 -0
- data/test/spec_rack_deflect.rb +107 -0
- data/test/spec_rack_jsonp.rb +23 -10
- data/test/spec_rack_lighttpd_script_name_fix.rb +1 -1
- data/test/spec_rack_not_found.rb +17 -0
- data/test/spec_rack_post_body_content_type_parser.rb +5 -5
- data/test/spec_rack_profiler.rb +31 -26
- data/test/spec_rack_relative_redirect.rb +78 -0
- data/test/spec_rack_response_cache.rb +137 -0
- metadata +30 -3
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'csshttprequest'
|
6
|
+
require 'rack/contrib/csshttprequest'
|
7
|
+
|
8
|
+
context "Rack::CSSHTTPRequest" do
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
@test_body = '{"bar":"foo"}'
|
12
|
+
@test_headers = {'Content-Type' => 'text/plain'}
|
13
|
+
@encoded_body = CSSHTTPRequest.encode(@test_body)
|
14
|
+
@app = lambda { |env| [200, @test_headers, [@test_body]] }
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "env['csshttprequest.chr'] should be set to true when \
|
18
|
+
PATH_INFO ends with '.chr'" do
|
19
|
+
request = Rack::MockRequest.env_for("/blah.chr", :lint => true, :fatal => true)
|
20
|
+
Rack::CSSHTTPRequest.new(@app).call(request)
|
21
|
+
request['csshttprequest.chr'].should.equal true
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "env['csshttprequest.chr'] should be set to true when \
|
25
|
+
request parameter _format == 'chr'" do
|
26
|
+
request = Rack::MockRequest.env_for("/?_format=chr", :lint => true, :fatal => true)
|
27
|
+
Rack::CSSHTTPRequest.new(@app).call(request)
|
28
|
+
request['csshttprequest.chr'].should.equal true
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should not change the headers or response when !env['csshttprequest.chr']" do
|
32
|
+
request = Rack::MockRequest.env_for("/", :lint => true, :fatal => true)
|
33
|
+
status, headers, response = Rack::CSSHTTPRequest.new(@app).call(request)
|
34
|
+
headers.should.equal @test_headers
|
35
|
+
response.join.should.equal @test_body
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when env['csshttprequest.chr']" do
|
39
|
+
before(:each) do
|
40
|
+
@request = Rack::MockRequest.env_for("/",
|
41
|
+
'csshttprequest.chr' => true, :lint => true, :fatal => true)
|
42
|
+
end
|
43
|
+
|
44
|
+
specify "should modify the content length to the correct value" do
|
45
|
+
headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
|
46
|
+
headers['Content-Length'].should.equal @encoded_body.length.to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
specify "should modify the content type to the correct value" do
|
50
|
+
headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
|
51
|
+
headers['Content-Type'].should.equal 'text/css'
|
52
|
+
end
|
53
|
+
|
54
|
+
specify "should not modify any other headers" do
|
55
|
+
headers = Rack::CSSHTTPRequest.new(@app).call(@request)[1]
|
56
|
+
headers.should.equal @test_headers.merge({
|
57
|
+
'Content-Type' => 'text/css',
|
58
|
+
'Content-Length' => @encoded_body.length.to_s
|
59
|
+
})
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
rescue LoadError => boom
|
65
|
+
STDERR.puts "WARN: Skipping Rack::CSSHTTPRequest tests (nbio-csshttprequest not installed)"
|
66
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/deflect'
|
4
|
+
|
5
|
+
context "Rack::Deflect" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
@app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, 'cookies'] }
|
9
|
+
@mock_addr_1 = '111.111.111.111'
|
10
|
+
@mock_addr_2 = '222.222.222.222'
|
11
|
+
@mock_addr_3 = '333.333.333.333'
|
12
|
+
end
|
13
|
+
|
14
|
+
def mock_env remote_addr, path = '/'
|
15
|
+
Rack::MockRequest.env_for path, { 'REMOTE_ADDR' => remote_addr }
|
16
|
+
end
|
17
|
+
|
18
|
+
def mock_deflect options = {}
|
19
|
+
Rack::Deflect.new @app, options
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should allow regular requests to follow through" do
|
23
|
+
app = mock_deflect
|
24
|
+
status, headers, body = app.call mock_env(@mock_addr_1)
|
25
|
+
status.should.equal 200
|
26
|
+
body.should.equal 'cookies'
|
27
|
+
end
|
28
|
+
|
29
|
+
specify "should deflect requests exceeding the request threshold" do
|
30
|
+
log = StringIO.new
|
31
|
+
app = mock_deflect :request_threshold => 5, :interval => 10, :block_duration => 10, :log => log
|
32
|
+
env = mock_env @mock_addr_1
|
33
|
+
|
34
|
+
# First 5 should be fine
|
35
|
+
5.times do
|
36
|
+
status, headers, body = app.call env
|
37
|
+
status.should.equal 200
|
38
|
+
body.should.equal 'cookies'
|
39
|
+
end
|
40
|
+
|
41
|
+
# Remaining requests should fail for 10 seconds
|
42
|
+
10.times do
|
43
|
+
status, headers, body = app.call env
|
44
|
+
status.should.equal 403
|
45
|
+
body.should.equal ''
|
46
|
+
end
|
47
|
+
|
48
|
+
# Log should reflect that we have blocked an address
|
49
|
+
log.string.should.match(/^deflect\(\d+\/\d+\/\d+\): blocked 111.111.111.111\n/)
|
50
|
+
end
|
51
|
+
|
52
|
+
specify "should expire blocking" do
|
53
|
+
log = StringIO.new
|
54
|
+
app = mock_deflect :request_threshold => 5, :interval => 2, :block_duration => 2, :log => log
|
55
|
+
env = mock_env @mock_addr_1
|
56
|
+
|
57
|
+
# First 5 should be fine
|
58
|
+
5.times do
|
59
|
+
status, headers, body = app.call env
|
60
|
+
status.should.equal 200
|
61
|
+
body.should.equal 'cookies'
|
62
|
+
end
|
63
|
+
|
64
|
+
# Exceeds request threshold
|
65
|
+
status, headers, body = app.call env
|
66
|
+
status.should.equal 403
|
67
|
+
body.should.equal ''
|
68
|
+
|
69
|
+
# Allow block to expire
|
70
|
+
sleep 3
|
71
|
+
|
72
|
+
# Another 5 is fine now
|
73
|
+
5.times do
|
74
|
+
status, headers, body = app.call env
|
75
|
+
status.should.equal 200
|
76
|
+
body.should.equal 'cookies'
|
77
|
+
end
|
78
|
+
|
79
|
+
# Log should reflect block and release
|
80
|
+
log.string.should.match(/deflect.*: blocked 111\.111\.111\.111\ndeflect.*: released 111\.111\.111\.111\n/)
|
81
|
+
end
|
82
|
+
|
83
|
+
specify "should allow whitelisting of remote addresses" do
|
84
|
+
app = mock_deflect :whitelist => [@mock_addr_1], :request_threshold => 5, :interval => 2
|
85
|
+
env = mock_env @mock_addr_1
|
86
|
+
|
87
|
+
# Whitelisted addresses are always fine
|
88
|
+
10.times do
|
89
|
+
status, headers, body = app.call env
|
90
|
+
status.should.equal 200
|
91
|
+
body.should.equal 'cookies'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
specify "should allow blacklisting of remote addresses" do
|
96
|
+
app = mock_deflect :blacklist => [@mock_addr_2]
|
97
|
+
|
98
|
+
status, headers, body = app.call mock_env(@mock_addr_1)
|
99
|
+
status.should.equal 200
|
100
|
+
body.should.equal 'cookies'
|
101
|
+
|
102
|
+
status, headers, body = app.call mock_env(@mock_addr_2)
|
103
|
+
status.should.equal 403
|
104
|
+
body.should.equal ''
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
data/test/spec_rack_jsonp.rb
CHANGED
@@ -3,19 +3,32 @@ require 'rack/mock'
|
|
3
3
|
require 'rack/contrib/jsonp'
|
4
4
|
|
5
5
|
context "Rack::JSONP" do
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
|
7
|
+
context "when a callback parameter is provided" do
|
8
|
+
specify "should wrap the response body in the Javascript callback" do
|
9
|
+
test_body = '{"bar":"foo"}'
|
10
|
+
callback = 'foo'
|
11
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, [test_body]] }
|
12
|
+
request = Rack::MockRequest.env_for("/", :input => "foo=bar&callback=#{callback}")
|
13
|
+
body = Rack::JSONP.new(app).call(request).last
|
14
|
+
body.join.should.equal "#{callback}(#{test_body})"
|
15
|
+
end
|
16
|
+
|
17
|
+
specify "should modify the content length to the correct value" do
|
18
|
+
test_body = '{"bar":"foo"}'
|
19
|
+
callback = 'foo'
|
20
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, [test_body]] }
|
21
|
+
request = Rack::MockRequest.env_for("/", :input => "foo=bar&callback=#{callback}")
|
22
|
+
headers = Rack::JSONP.new(app).call(request)[1]
|
23
|
+
headers['Content-Length'].should.equal((test_body.length + callback.length + 2).to_s) # 2 parentheses
|
24
|
+
end
|
12
25
|
end
|
13
|
-
|
14
|
-
specify "should not change anything if no
|
26
|
+
|
27
|
+
specify "should not change anything if no callback param is provided" do
|
15
28
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, '{"bar":"foo"}'] }
|
16
29
|
request = Rack::MockRequest.env_for("/", :input => "foo=bar")
|
17
30
|
body = Rack::JSONP.new(app).call(request).last
|
18
|
-
body.should
|
31
|
+
body.join.should.equal '{"bar":"foo"}'
|
19
32
|
end
|
20
|
-
|
33
|
+
|
21
34
|
end
|
@@ -8,7 +8,7 @@ context "Rack::LighttpdScriptNameFix" do
|
|
8
8
|
"PATH_INFO" => "/foo/bar/baz",
|
9
9
|
"SCRIPT_NAME" => "/hello"
|
10
10
|
}
|
11
|
-
app = lambda { |
|
11
|
+
app = lambda { |_| [200, {'Content-Type' => 'text/plain'}, ["Hello, World!"]] }
|
12
12
|
response = Rack::LighttpdScriptNameFix.new(app).call(env)
|
13
13
|
env['SCRIPT_NAME'].should.be.empty
|
14
14
|
env['PATH_INFO'].should.equal '/hello/foo/bar/baz'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/not_found'
|
4
|
+
|
5
|
+
context "Rack::NotFound" do
|
6
|
+
|
7
|
+
specify "should render the file at the given path for all requests" do
|
8
|
+
app = Rack::Builder.new do
|
9
|
+
use Rack::Lint
|
10
|
+
run Rack::NotFound.new('test/404.html')
|
11
|
+
end
|
12
|
+
response = Rack::MockRequest.new(app).get('/')
|
13
|
+
response.body.should.equal('Not Found')
|
14
|
+
response.status.should.equal(404)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -3,9 +3,9 @@ require 'rack/mock'
|
|
3
3
|
|
4
4
|
begin
|
5
5
|
require 'rack/contrib/post_body_content_type_parser'
|
6
|
-
|
6
|
+
|
7
7
|
context "Rack::PostBodyContentTypeParser" do
|
8
|
-
|
8
|
+
|
9
9
|
specify "should handle requests with POST body Content-Type of application/json" do
|
10
10
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST] }
|
11
11
|
env = env_for_post_with_headers('/', {'Content_Type'.upcase => 'application/json'}, {:body => "asdf", :status => "12"}.to_json)
|
@@ -13,16 +13,16 @@ begin
|
|
13
13
|
body['body'].should.equal "asdf"
|
14
14
|
body['status'].should.equal "12"
|
15
15
|
end
|
16
|
-
|
16
|
+
|
17
17
|
specify "should change nothing when the POST body content type isn't application/json" do
|
18
18
|
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST] }
|
19
19
|
body = app.call(Rack::MockRequest.env_for("/", :input => "body=asdf&status=12")).last
|
20
20
|
body['body'].should.equal "asdf"
|
21
21
|
body['status'].should.equal "12"
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
end
|
25
|
-
|
25
|
+
|
26
26
|
def env_for_post_with_headers(path, headers, body)
|
27
27
|
Rack::MockRequest.env_for(path, {:method => "POST", :input => body}.merge(headers))
|
28
28
|
end
|
data/test/spec_rack_profiler.rb
CHANGED
@@ -1,32 +1,37 @@
|
|
1
1
|
require 'test/spec'
|
2
2
|
require 'rack/mock'
|
3
|
-
require 'rack/contrib/profiler'
|
4
3
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
4
|
+
begin
|
5
|
+
require 'rack/contrib/profiler'
|
6
|
+
|
7
|
+
context 'Rack::Profiler' do
|
8
|
+
app = lambda { |env| [200, {'Content-Type' => 'text/plain'}, 'Oh hai der'] }
|
9
|
+
request = Rack::MockRequest.env_for("/", :input => "profile=process_time")
|
10
|
+
|
11
|
+
specify 'printer defaults to RubyProf::CallTreePrinter' do
|
12
|
+
profiler = Rack::Profiler.new(nil)
|
13
|
+
profiler.instance_variable_get('@printer').should.equal RubyProf::CallTreePrinter
|
14
|
+
profiler.instance_variable_get('@times').should.equal 1
|
15
|
+
end
|
16
|
+
|
17
|
+
specify 'CallTreePrinter has correct headers' do
|
18
|
+
headers = Rack::Profiler.new(app).call(request)[1]
|
19
|
+
headers.should.equal "Content-Disposition"=>"attachment; filename=\"/.process_time.tree\"", "Content-Type"=>"application/octet-stream"
|
20
|
+
end
|
21
|
+
|
22
|
+
specify 'FlatPrinter and GraphPrinter has Content-Type text/plain' do
|
23
|
+
%w(flat graph).each do |printer|
|
24
|
+
headers = Rack::Profiler.new(app, :printer => printer.to_sym).call(request)[1]
|
25
|
+
headers.should.equal "Content-Type"=>"text/plain"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
specify 'GraphHtmlPrinter has Content-Type text/html' do
|
30
|
+
headers = Rack::Profiler.new(app, :printer => :graph_html).call(request)[1]
|
31
|
+
headers.should.equal "Content-Type"=>"text/html"
|
25
32
|
end
|
26
33
|
end
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
headers.should == {"Content-Type"=>"text/html"}
|
31
|
-
end
|
34
|
+
|
35
|
+
rescue LoadError => boom
|
36
|
+
$stderr.puts "WARN: Skipping Rack::Profiler tests (ruby-prof not installed)"
|
32
37
|
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/relative_redirect'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
context Rack::RelativeRedirect do
|
7
|
+
def request(opts={}, &block)
|
8
|
+
@def_status = opts[:status] if opts[:status]
|
9
|
+
@def_location = opts[:location] if opts[:location]
|
10
|
+
yield Rack::MockRequest.new(Rack::RelativeRedirect.new(@def_app, &opts[:block])).get(opts[:path]||@def_path, opts[:headers]||{})
|
11
|
+
end
|
12
|
+
|
13
|
+
setup do
|
14
|
+
@def_path = '/path/to/blah'
|
15
|
+
@def_status = 301
|
16
|
+
@def_location = '/redirect/to/blah'
|
17
|
+
@def_app = lambda { |env| [@def_status, {'Location' => @def_location}, [""]]}
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "should make the location url an absolute url if currently a relative url" do
|
21
|
+
request do |r|
|
22
|
+
r.status.should.equal(301)
|
23
|
+
r.headers['Location'].should.equal('http://example.org/redirect/to/blah')
|
24
|
+
end
|
25
|
+
request(:status=>302, :location=>'/redirect') do |r|
|
26
|
+
r.status.should.equal(302)
|
27
|
+
r.headers['Location'].should.equal('http://example.org/redirect')
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should use the request path if the relative url is given and doesn't start with a slash" do
|
32
|
+
request(:status=>303, :location=>'redirect/to/blah') do |r|
|
33
|
+
r.status.should.equal(303)
|
34
|
+
r.headers['Location'].should.equal('http://example.org/path/to/redirect/to/blah')
|
35
|
+
end
|
36
|
+
request(:status=>303, :location=>'redirect') do |r|
|
37
|
+
r.status.should.equal(303)
|
38
|
+
r.headers['Location'].should.equal('http://example.org/path/to/redirect')
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
specify "should use a given block to make the url absolute" do
|
43
|
+
request(:block=>proc{|env, res| "https://example.org"}) do |r|
|
44
|
+
r.status.should.equal(301)
|
45
|
+
r.headers['Location'].should.equal('https://example.org/redirect/to/blah')
|
46
|
+
end
|
47
|
+
request(:status=>303, :location=>'/redirect', :block=>proc{|env, res| "https://e.org:9999/blah"}) do |r|
|
48
|
+
r.status.should.equal(303)
|
49
|
+
r.headers['Location'].should.equal('https://e.org:9999/blah/redirect')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
specify "should not modify the location url unless the response is a redirect" do
|
54
|
+
status = 200
|
55
|
+
@def_app = lambda { |env| [status, {'Content-Type' => "text/html"}, [""]]}
|
56
|
+
request do |r|
|
57
|
+
r.status.should.equal(200)
|
58
|
+
r.headers.should.not.include?('Location')
|
59
|
+
end
|
60
|
+
status = 404
|
61
|
+
@def_app = lambda { |env| [status, {'Content-Type' => "text/html", 'Location' => 'redirect'}, [""]]}
|
62
|
+
request do |r|
|
63
|
+
r.status.should.equal(404)
|
64
|
+
r.headers['Location'].should.equal('redirect')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "should not modify the location url if it is already an absolute url" do
|
69
|
+
request(:location=>'https://example.org/') do |r|
|
70
|
+
r.status.should.equal(301)
|
71
|
+
r.headers['Location'].should.equal('https://example.org/')
|
72
|
+
end
|
73
|
+
request(:status=>302, :location=>'https://e.org:9999/redirect') do |r|
|
74
|
+
r.status.should.equal(302)
|
75
|
+
r.headers['Location'].should.equal('https://e.org:9999/redirect')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'test/spec'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'rack/contrib/response_cache'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
context Rack::ResponseCache do
|
7
|
+
F = ::File
|
8
|
+
|
9
|
+
def request(opts={}, &block)
|
10
|
+
Rack::MockRequest.new(Rack::ResponseCache.new(block||@def_app, opts[:cache]||@cache, &opts[:rc_block])).send(opts[:meth]||:get, opts[:path]||@def_path, opts[:headers]||{})
|
11
|
+
end
|
12
|
+
|
13
|
+
setup do
|
14
|
+
@cache = {}
|
15
|
+
@def_disk_cache = F.join(F.dirname(__FILE__), 'response_cache_test_disk_cache')
|
16
|
+
@def_value = ["rack-response-cache"]
|
17
|
+
@def_path = '/path/to/blah'
|
18
|
+
@def_app = lambda { |env| [200, {'Content-Type' => env['CT'] || 'text/html'}, @def_value]}
|
19
|
+
end
|
20
|
+
teardown do
|
21
|
+
FileUtils.rm_rf(@def_disk_cache)
|
22
|
+
end
|
23
|
+
|
24
|
+
specify "should cache results to disk if cache is a string" do
|
25
|
+
request(:cache=>@def_disk_cache)
|
26
|
+
F.read(F.join(@def_disk_cache, 'path', 'to', 'blah.html')).should.equal @def_value.first
|
27
|
+
request(:path=>'/path/3', :cache=>@def_disk_cache)
|
28
|
+
F.read(F.join(@def_disk_cache, 'path', '3.html')).should.equal @def_value.first
|
29
|
+
end
|
30
|
+
|
31
|
+
specify "should cache results to given cache if cache is not a string" do
|
32
|
+
request
|
33
|
+
@cache.should.equal('/path/to/blah.html'=>@def_value)
|
34
|
+
request(:path=>'/path/3')
|
35
|
+
@cache.should.equal('/path/to/blah.html'=>@def_value, '/path/3.html'=>@def_value)
|
36
|
+
end
|
37
|
+
|
38
|
+
specify "should not CACHE RESults if request method is not GET" do
|
39
|
+
request(:meth=>:post)
|
40
|
+
@cache.should.equal({})
|
41
|
+
request(:meth=>:put)
|
42
|
+
@cache.should.equal({})
|
43
|
+
request(:meth=>:delete)
|
44
|
+
@cache.should.equal({})
|
45
|
+
end
|
46
|
+
|
47
|
+
specify "should not cache results if there is a query string" do
|
48
|
+
request(:path=>'/path/to/blah?id=1')
|
49
|
+
@cache.should.equal({})
|
50
|
+
request(:path=>'/path/to/?id=1')
|
51
|
+
@cache.should.equal({})
|
52
|
+
request(:path=>'/?id=1')
|
53
|
+
@cache.should.equal({})
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "should cache results if there is an empty query string" do
|
57
|
+
request(:path=>'/?')
|
58
|
+
@cache.should.equal('/index.html'=>@def_value)
|
59
|
+
end
|
60
|
+
|
61
|
+
specify "should not cache results if the request is not sucessful (status 200)" do
|
62
|
+
request{|env| [404, {'Content-Type' => 'text/html'}, ['']]}
|
63
|
+
@cache.should.equal({})
|
64
|
+
request{|env| [500, {'Content-Type' => 'text/html'}, ['']]}
|
65
|
+
@cache.should.equal({})
|
66
|
+
request{|env| [302, {'Content-Type' => 'text/html'}, ['']]}
|
67
|
+
@cache.should.equal({})
|
68
|
+
end
|
69
|
+
|
70
|
+
specify "should not cache results if the block returns nil or false" do
|
71
|
+
request(:rc_block=>proc{false})
|
72
|
+
@cache.should.equal({})
|
73
|
+
request(:rc_block=>proc{nil})
|
74
|
+
@cache.should.equal({})
|
75
|
+
end
|
76
|
+
|
77
|
+
specify "should cache results to path returned by block" do
|
78
|
+
request(:rc_block=>proc{"1"})
|
79
|
+
@cache.should.equal("1"=>@def_value)
|
80
|
+
request(:rc_block=>proc{"2"})
|
81
|
+
@cache.should.equal("1"=>@def_value, "2"=>@def_value)
|
82
|
+
end
|
83
|
+
|
84
|
+
specify "should pass the environment and response to the block" do
|
85
|
+
e, r = nil, nil
|
86
|
+
request(:rc_block=>proc{|env,res| e, r = env, res; nil})
|
87
|
+
e['PATH_INFO'].should.equal @def_path
|
88
|
+
e['REQUEST_METHOD'].should.equal 'GET'
|
89
|
+
e['QUERY_STRING'].should.equal ''
|
90
|
+
r.should.equal([200, {"Content-Type"=>"text/html"}, ["rack-response-cache"]])
|
91
|
+
end
|
92
|
+
|
93
|
+
specify "should unescape the path by default" do
|
94
|
+
request(:path=>'/path%20with%20spaces')
|
95
|
+
@cache.should.equal('/path with spaces.html'=>@def_value)
|
96
|
+
request(:path=>'/path%3chref%3e')
|
97
|
+
@cache.should.equal('/path with spaces.html'=>@def_value, '/path<href>.html'=>@def_value)
|
98
|
+
end
|
99
|
+
|
100
|
+
specify "should cache html, css, and xml responses by default" do
|
101
|
+
request(:path=>'/a')
|
102
|
+
@cache.should.equal('/a.html'=>@def_value)
|
103
|
+
request(:path=>'/b', :headers=>{'CT'=>'text/xml'})
|
104
|
+
@cache.should.equal('/a.html'=>@def_value, '/b.xml'=>@def_value)
|
105
|
+
request(:path=>'/c', :headers=>{'CT'=>'text/css'})
|
106
|
+
@cache.should.equal('/a.html'=>@def_value, '/b.xml'=>@def_value, '/c.css'=>@def_value)
|
107
|
+
end
|
108
|
+
|
109
|
+
specify "should cache responses by default with the extension added if not already present" do
|
110
|
+
request(:path=>'/a.html')
|
111
|
+
@cache.should.equal('/a.html'=>@def_value)
|
112
|
+
request(:path=>'/b.xml', :headers=>{'CT'=>'text/xml'})
|
113
|
+
@cache.should.equal('/a.html'=>@def_value, '/b.xml'=>@def_value)
|
114
|
+
request(:path=>'/c.css', :headers=>{'CT'=>'text/css'})
|
115
|
+
@cache.should.equal('/a.html'=>@def_value, '/b.xml'=>@def_value, '/c.css'=>@def_value)
|
116
|
+
end
|
117
|
+
|
118
|
+
specify "should not delete existing extensions" do
|
119
|
+
request(:path=>'/d.css', :headers=>{'CT'=>'text/html'})
|
120
|
+
@cache.should.equal('/d.css.html'=>@def_value)
|
121
|
+
end
|
122
|
+
|
123
|
+
specify "should cache html responses with empty basename to index.html by default" do
|
124
|
+
request(:path=>'/')
|
125
|
+
@cache.should.equal('/index.html'=>@def_value)
|
126
|
+
request(:path=>'/blah/')
|
127
|
+
@cache.should.equal('/index.html'=>@def_value, '/blah/index.html'=>@def_value)
|
128
|
+
request(:path=>'/blah/2/')
|
129
|
+
@cache.should.equal('/index.html'=>@def_value, '/blah/index.html'=>@def_value, '/blah/2/index.html'=>@def_value)
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "should raise an error if a cache argument is not provided" do
|
133
|
+
app = Rack::Builder.new{use Rack::ResponseCache; run lambda { |env| [200, {'Content-Type' => 'text/plain'}, Rack::Request.new(env).POST]}}
|
134
|
+
proc{Rack::MockRequest.new(app).get('/')}.should.raise(ArgumentError)
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|