cache_rules 0.1.1
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.
- data/.gitignore +3 -0
- data/.travis.yml +8 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +47 -0
- data/LICENSE +340 -0
- data/NOTICE +26 -0
- data/README.md +145 -0
- data/Rakefile +14 -0
- data/cache_rules.gemspec +34 -0
- data/lib/actions.rb +40 -0
- data/lib/cache_rules.rb +188 -0
- data/lib/formatting.rb +161 -0
- data/lib/helpers.rb +326 -0
- data/lib/validations.rb +111 -0
- data/test/helper.rb +18 -0
- data/test/test_cache_rules.rb +253 -0
- data/test/test_formatting.rb +135 -0
- data/test/test_helpers.rb +396 -0
- data/test/test_tables.rb +75 -0
- data/test/test_validations.rb +206 -0
- metadata +132 -0
data/lib/validations.rb
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
4
|
+
#
|
|
5
|
+
# Copyright (c) 2014-2015 Alexander Williams, Unscramble <license@unscramble.jp>
|
|
6
|
+
|
|
7
|
+
module CacheRules
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
# HTTP Header Validators
|
|
11
|
+
#
|
|
12
|
+
# Parameters must always be normalized
|
|
13
|
+
#
|
|
14
|
+
# Return must always be 0 or 1
|
|
15
|
+
#
|
|
16
|
+
# The If-Match and If-Unmodified-Since conditional header fields are not applicable to a cache.
|
|
17
|
+
# source: https://tools.ietf.org/html/rfc7234#section-4.3.2
|
|
18
|
+
|
|
19
|
+
def validate_cached?(headers)
|
|
20
|
+
headers[:cached].length > 0 ? 1 : 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Precedence: If-None-Match (ETag), then If-Modified-Since (Last-Modified)
|
|
24
|
+
# source: https://tools.ietf.org/html/rfc7232#section-6
|
|
25
|
+
def validate_precond_match?(headers)
|
|
26
|
+
request, cached = headers.values_at :request, :cached
|
|
27
|
+
return 0 if cached.length == 0
|
|
28
|
+
|
|
29
|
+
# Return when the If-None-Match header exists, ignore If-Modified-Since
|
|
30
|
+
# source: https://tools.ietf.org/html/rfc7232#section-3.3
|
|
31
|
+
etag_match = helper_etag(request, cached)
|
|
32
|
+
return etag_match ? 1 : 0 unless etag_match.nil?
|
|
33
|
+
|
|
34
|
+
helper_last_modified(request, cached) ? 1 : 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Compare headers to see if the cached request is expired (Freshness)
|
|
38
|
+
# source: https://tools.ietf.org/html/rfc7234#section-4.2
|
|
39
|
+
def validate_expired?(headers)
|
|
40
|
+
return 0 if headers[:cached].length == 0
|
|
41
|
+
|
|
42
|
+
freshness_lifetime, current_age = helper_freshness_lifetime.call headers[:cached]
|
|
43
|
+
|
|
44
|
+
response_is_fresh = freshness_lifetime.to_i > current_age
|
|
45
|
+
|
|
46
|
+
response_is_fresh ? 0 : 1
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def validate_only_if_cached?(headers)
|
|
50
|
+
headers[:request]['Cache-Control'] && headers[:request]['Cache-Control']['only-if-cached'] ? 1 : 0
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Serving Stale Responses
|
|
54
|
+
# source: https://tools.ietf.org/html/rfc7234#section-4.2.4
|
|
55
|
+
def validate_allow_stale?(headers)
|
|
56
|
+
request, cached = headers.values_at :request, :cached
|
|
57
|
+
return 0 if cached.length == 0 || helper_validate_allow_stale(request, cached)
|
|
58
|
+
|
|
59
|
+
freshness_lifetime, current_age = helper_freshness_lifetime.call cached
|
|
60
|
+
|
|
61
|
+
max_stale = helper_max_stale.call request['Cache-Control'], freshness_lifetime, current_age
|
|
62
|
+
min_fresh = helper_min_fresh.call request['Cache-Control'], freshness_lifetime, current_age
|
|
63
|
+
|
|
64
|
+
(max_stale && min_fresh != false) || (max_stale.nil? && min_fresh) ? 1 : 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Response Cache-Control Directives
|
|
68
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2
|
|
69
|
+
def validate_must_revalidate?(headers)
|
|
70
|
+
return 1 if headers[:cached].length == 0
|
|
71
|
+
|
|
72
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.1
|
|
73
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.7
|
|
74
|
+
(( cached = headers[:cached]['Cache-Control'] )) && ( cached['must-revalidate'] || cached['proxy-revalidate'] ) ? 1 : 0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Verify if we're explicitly told not to cache the response
|
|
78
|
+
def validate_no_cache?(headers)
|
|
79
|
+
request_headers, cached_headers = headers.values_at :request, :cached
|
|
80
|
+
return 1 if cached_headers.length == 0
|
|
81
|
+
|
|
82
|
+
# Must revalidate if this request header exists
|
|
83
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.2.1.4
|
|
84
|
+
return 1 if (( request = request_headers['Cache-Control'] )) &&
|
|
85
|
+
request_headers['Cache-Control']['no-cache']
|
|
86
|
+
|
|
87
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.2
|
|
88
|
+
# source: https://tools.ietf.org/html/rfc7234#section-3.2
|
|
89
|
+
return 1 if (( cached = cached_headers['Cache-Control'] )) &&
|
|
90
|
+
helper_no_cache.call(cached_headers) ||
|
|
91
|
+
(cached['no-cache'] && cached['no-cache']['quoted_string'].nil?) ||
|
|
92
|
+
(cached['s-maxage'] && cached['s-maxage']['token'].to_s == "0") ||
|
|
93
|
+
(cached['max-age'] && cached['max-age']['token'].to_s == "0")
|
|
94
|
+
|
|
95
|
+
# source: https://tools.ietf.org/html/rfc7234#section-5.4
|
|
96
|
+
# Legacy support for HTTP/1.0 Pragma header
|
|
97
|
+
return 1 if request_headers['Pragma'] && request_headers['Pragma']['no-cache']
|
|
98
|
+
|
|
99
|
+
return 0
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def validate_is_error?(headers)
|
|
103
|
+
headers[:response]['Status'].to_i.between?(500,599) ? 1 : 0
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def validate_validator_match?(headers)
|
|
107
|
+
request, response = headers.values_at :request, :response
|
|
108
|
+
response['ETag'] && request['If-None-Match'] && request['If-None-Match'].include?(response['ETag']) ? 1 : 0
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'coveralls'
|
|
2
|
+
require 'fakeweb'
|
|
3
|
+
require 'simplecov'
|
|
4
|
+
|
|
5
|
+
SimpleCov.start do
|
|
6
|
+
add_filter '/test/'
|
|
7
|
+
add_filter '.bundle'
|
|
8
|
+
minimum_coverage 100
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
require './lib/cache_rules'
|
|
12
|
+
require 'minitest/autorun'
|
|
13
|
+
require 'minitest/unit'
|
|
14
|
+
require 'minitest/reporters'
|
|
15
|
+
|
|
16
|
+
Minitest::Reporters.use! [Minitest::Reporters::SpecReporter.new]
|
|
17
|
+
|
|
18
|
+
Coveralls.wear!
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
class TestCacheRules < MiniTest::Test
|
|
2
|
+
|
|
3
|
+
def setup
|
|
4
|
+
@request_if_none_match = CacheRules.normalize.call({ "Host" => "test.com", "If-None-Match" => "*", "Cache-Control" => "max-stale=100000000" })
|
|
5
|
+
@request_if_modified_since_yes = CacheRules.normalize.call({ "Host" => "test.com", "If-Modified-Since" => "Thu, 01 Jan 2015 07:03:45 GMT", "Cache-Control" => "max-stale=100000000" })
|
|
6
|
+
@request_if_modified_since_no = CacheRules.normalize.call({ "Host" => "test.com" })
|
|
7
|
+
@cached_headers = {
|
|
8
|
+
"Date" => {"httpdate"=>"Thu, 01 Jan 2015 07:03:45 GMT", "timestamp"=>1420095825},
|
|
9
|
+
"Cache-Control" => {
|
|
10
|
+
"public" => {"token"=>nil, "quoted_string"=>nil}
|
|
11
|
+
},
|
|
12
|
+
"X-Cache-Req-Date" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625},
|
|
13
|
+
"X-Cache-Res-Date" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}
|
|
14
|
+
}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def test_get_money
|
|
18
|
+
got_money = CacheRules::Everything::Around::Me.get_the_money
|
|
19
|
+
|
|
20
|
+
assert_equal got_money, "Dolla Dolla Bill Y'all"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_validate_column0
|
|
24
|
+
request = {"Host"=>"test.url"}
|
|
25
|
+
|
|
26
|
+
result = CacheRules.validate('http://test.url/test1', request)
|
|
27
|
+
|
|
28
|
+
assert_equal result[:code], 307
|
|
29
|
+
assert_nil result[:body]
|
|
30
|
+
assert_equal result[:headers]['Cache-Lookup'], 'MISS'
|
|
31
|
+
assert_equal result[:headers]['Location'], "http://test.url/test1"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_validate_column1
|
|
35
|
+
request = {"Host"=>"test.url","Cache-Control"=>"only-if-cached"}
|
|
36
|
+
|
|
37
|
+
result = CacheRules.validate('http://test.url/test1', request)
|
|
38
|
+
|
|
39
|
+
assert_equal result[:code], 504
|
|
40
|
+
assert_equal result[:body], 'Gateway Timeout'
|
|
41
|
+
assert_equal result[:headers]['Cache-Lookup'], 'MISS'
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_validate_column2
|
|
45
|
+
request = {"Host"=>"test.url"}
|
|
46
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Cache-Control"=>{"s-maxage"=>{"token"=>"100000000", "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}}
|
|
47
|
+
|
|
48
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
49
|
+
|
|
50
|
+
assert_equal result[:code], 200
|
|
51
|
+
assert_equal result[:body], 'cached'
|
|
52
|
+
assert_equal result[:headers]['Cache-Lookup'], 'HIT'
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def test_validate_column3
|
|
56
|
+
request = {"Host"=>"test.url", "If-None-Match" => "*"}
|
|
57
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Cache-Control"=>{"s-maxage"=>{"token"=>"100000000", "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\""}
|
|
58
|
+
|
|
59
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
60
|
+
|
|
61
|
+
assert_equal result[:code], 304
|
|
62
|
+
assert_nil result[:body]
|
|
63
|
+
assert_equal result[:headers]['Cache-Lookup'], 'HIT'
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_validate_column4
|
|
67
|
+
request = {"Host"=>"test.url", "Cache-Control"=>"max-stale=10000000"}
|
|
68
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\"", "Cache-Control"=>{"max-age"=>{"token"=>"100", "quoted_string" => nil}}}
|
|
69
|
+
|
|
70
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
71
|
+
|
|
72
|
+
assert_equal result[:code], 200
|
|
73
|
+
assert_equal result[:body], 'stale'
|
|
74
|
+
assert_equal result[:headers]['Cache-Lookup'], 'STALE'
|
|
75
|
+
assert_equal result[:headers]['Warning'], "110 - \"Response is Stale\""
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def test_validate_column5
|
|
79
|
+
request = {"Host"=>"test.url", "Cache-Control"=>"max-stale=10000000", "If-None-Match"=>"*"}
|
|
80
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\"", "Cache-Control"=>{"max-age"=>{"token"=>"100", "quoted_string" => nil}}}
|
|
81
|
+
|
|
82
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
83
|
+
|
|
84
|
+
assert_equal result[:code], 304
|
|
85
|
+
assert_nil result[:body]
|
|
86
|
+
assert_equal result[:headers]['Cache-Lookup'], 'STALE'
|
|
87
|
+
assert_equal result[:headers]['Warning'], "110 - \"Response is Stale\""
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_validate_column6
|
|
91
|
+
request = {"Host"=>"test.url", "Cache-Control"=>"max-stale=0", "If-None-Match"=>"*"}
|
|
92
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\"", "Cache-Control"=>{"max-age"=>{"token"=>"100", "quoted_string" => nil}}}
|
|
93
|
+
|
|
94
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
95
|
+
|
|
96
|
+
assert_equal result[:code], 504
|
|
97
|
+
assert_equal result[:body], 'Gateway Timeout'
|
|
98
|
+
assert_equal result[:headers]['Cache-Lookup'], 'EXPIRED'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def test_validate_column7
|
|
102
|
+
request = {"Host"=>"test.url", "Cache-Control"=>"max-stale=0", "If-None-Match"=>"*"}
|
|
103
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\"", "Cache-Control"=>{"max-age"=>{"token"=>"100", "quoted_string" => nil}, "must-revalidate"=>{"token"=>nil, "quoted_string"=>nil}}}
|
|
104
|
+
|
|
105
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["304", "Not Modified"], :date => "Sat, 03 Jan 2015 07:15:45 GMT")
|
|
106
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
107
|
+
|
|
108
|
+
assert_equal result[:code], 307
|
|
109
|
+
assert_nil result[:body]
|
|
110
|
+
assert_equal result[:headers]['Cache-Lookup'], 'EXPIRED'
|
|
111
|
+
assert_equal result[:headers]['Location'], "http://test.url/test1"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def test_validate_column8
|
|
115
|
+
request = {"Host"=>"test.url", "Cache-Control"=>"max-stale=0", "If-None-Match"=>"*"}
|
|
116
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\"", "Cache-Control"=>{"max-age"=>{"token"=>"100", "quoted_string" => nil}, "no-cache"=>{"token"=>nil, "quoted_string"=>nil}}}
|
|
117
|
+
|
|
118
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["304", "Not Modified"], :date => "Sat, 03 Jan 2015 07:15:45 GMT")
|
|
119
|
+
result = CacheRules.validate('http://test.url/test1', request, cached)
|
|
120
|
+
|
|
121
|
+
assert_equal result[:code], 307
|
|
122
|
+
assert_nil result[:body]
|
|
123
|
+
assert_equal result[:headers]['Cache-Lookup'], 'EXPIRED'
|
|
124
|
+
assert_equal result[:headers]['Location'], "http://test.url/test1"
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def test_revalidate_response_column0
|
|
128
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["*"], "Cache-Control"=>{"max-age"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
129
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Cache-Control"=>{"s-maxage"=>{"token"=>"100000000", "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}}
|
|
130
|
+
|
|
131
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["304", "Not Modified"], :date => "Sat, 03 Jan 2015 07:15:45 GMT")
|
|
132
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
133
|
+
|
|
134
|
+
assert_equal result[:code], 200
|
|
135
|
+
assert_equal result[:body], 'cached'
|
|
136
|
+
assert_equal result[:headers]['Cache-Lookup'], 'REVALIDATED'
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_revalidate_response_column1
|
|
140
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["\"validEtag\""], "Cache-Control"=>{"max-age"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
141
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Cache-Control"=>{"s-maxage"=>{"token"=>"100000000", "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\""}
|
|
142
|
+
|
|
143
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["304", "Not Modified"], :date => "Sat, 03 Jan 2015 07:15:45 GMT", :ETag => "\"validEtag\"")
|
|
144
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
145
|
+
|
|
146
|
+
assert_equal result[:code], 304
|
|
147
|
+
assert_nil result[:body]
|
|
148
|
+
assert_equal result[:headers]['Cache-Lookup'], 'REVALIDATED'
|
|
149
|
+
assert_equal result[:headers]['ETag'], "\"validEtag\""
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def test_revalidate_response_column2_5xx
|
|
153
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["\"validEtag\""], "Cache-Control"=>{"max-age"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
154
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Cache-Control"=>{"s-maxage"=>{"token"=>"100000000", "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\""}
|
|
155
|
+
|
|
156
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["504", "Gateway Timeout"], :date => "Sat, 03 Jan 2015 07:15:45 GMT", :ETag => "\"validEtag\"")
|
|
157
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
158
|
+
|
|
159
|
+
assert_equal result[:code], 504
|
|
160
|
+
assert_equal result[:body], 'Gateway Timeout'
|
|
161
|
+
assert_equal result[:headers]['Cache-Lookup'], 'EXPIRED'
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def test_revalidate_response_column2_error
|
|
165
|
+
result = CacheRules.revalidate_response('ftp://test.url/test1', {}, {})
|
|
166
|
+
|
|
167
|
+
assert_equal result[:code], 504
|
|
168
|
+
assert_equal result[:body], 'Gateway Timeout'
|
|
169
|
+
assert result[:error]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def test_revalidate_response_column3
|
|
173
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["*"], "Cache-Control"=>{"max-stale"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
174
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\""}
|
|
175
|
+
|
|
176
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["504", "Gateway Timeout"], :date => "Sat, 03 Jan 2015 07:15:45 GMT")
|
|
177
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
178
|
+
|
|
179
|
+
assert_equal result[:code], 200
|
|
180
|
+
assert_equal result[:body], 'stale'
|
|
181
|
+
assert_equal result[:headers]['Warning'], "111 - \"Revalidation Failed\""
|
|
182
|
+
assert_equal result[:headers]['Cache-Lookup'], 'STALE'
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def test_revalidate_response_column4
|
|
186
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["\"validEtag\""], "Cache-Control"=>{"max-stale"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
187
|
+
cached = {"Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "Last-Modified" => {"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "ETag" => "\"validEtag\""}
|
|
188
|
+
|
|
189
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["504", "Gateway Timeout"], :date => "Sat, 03 Jan 2015 07:15:45 GMT", :ETag => "\"validEtag\"")
|
|
190
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
191
|
+
|
|
192
|
+
assert_equal result[:code], 304
|
|
193
|
+
assert_nil result[:body]
|
|
194
|
+
assert_equal result[:headers]['Warning'], "111 - \"Revalidation Failed\""
|
|
195
|
+
assert_equal result[:headers]['Cache-Lookup'], 'STALE'
|
|
196
|
+
assert_equal result[:headers]['ETag'], "\"validEtag\""
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def test_revalidate_response_column5
|
|
200
|
+
request = {"Host"=>"test.url", "If-None-Match"=>["*"], "Cache-Control"=>{"max-stale"=>{"token"=>"100000000", "quoted_string"=>nil}}}
|
|
201
|
+
cached = {"Date"=>{"httpdate"=>"Thu, 01 Jan 2015 07:03:45 GMT", "timestamp"=>1420095825}, "Cache-Control"=>{"public"=>{"token"=>nil, "quoted_string"=>nil}}, "X-Cache-Req-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}, "X-Cache-Res-Date"=>{"httpdate"=>"Sat, 03 Jan 2015 07:03:45 GMT", "timestamp"=>1420268625}}
|
|
202
|
+
|
|
203
|
+
FakeWeb.register_uri(:head, "http://test.url/test1", :status => ["200", "OK"])
|
|
204
|
+
result = CacheRules.revalidate_response('http://test.url/test1', request, cached)
|
|
205
|
+
|
|
206
|
+
assert_equal result[:code], 307
|
|
207
|
+
assert_nil result[:body]
|
|
208
|
+
assert_equal result[:headers]['Cache-Lookup'], 'EXPIRED'
|
|
209
|
+
assert_equal result[:headers]['Location'], "http://test.url/test1"
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def test_make_http_request_with_entity_tag
|
|
213
|
+
result = CacheRules.make_request.call('http://test.url', @request_if_none_match, @cached_headers)
|
|
214
|
+
http = eval "http", result.binding
|
|
215
|
+
request = eval "request", result.binding
|
|
216
|
+
|
|
217
|
+
assert_kind_of Proc, result
|
|
218
|
+
assert_kind_of FalseClass, http.use_ssl?
|
|
219
|
+
assert_equal http.address, 'test.url'
|
|
220
|
+
assert_equal http.port, 80
|
|
221
|
+
assert_equal request.method, 'HEAD'
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def test_make_http_request_with_last_modified
|
|
225
|
+
result = CacheRules.make_request.call('http://test.url', @request_if_modified_since_yes, @cached_headers)
|
|
226
|
+
http = eval "http", result.binding
|
|
227
|
+
request = eval "request", result.binding
|
|
228
|
+
|
|
229
|
+
assert_kind_of Proc, result
|
|
230
|
+
assert_kind_of FalseClass, http.use_ssl?
|
|
231
|
+
assert_equal http.address, 'test.url'
|
|
232
|
+
assert_equal http.port, 80
|
|
233
|
+
assert_equal request.method, 'HEAD'
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def test_make_http_request_without_preconditions
|
|
237
|
+
result = CacheRules.make_request.call('https://test.url', @request_if_modified_since_no, @cached_headers)
|
|
238
|
+
http = eval "http", result.binding
|
|
239
|
+
request = eval "request", result.binding
|
|
240
|
+
|
|
241
|
+
assert_kind_of Proc, result
|
|
242
|
+
assert_kind_of TrueClass, http.use_ssl?
|
|
243
|
+
assert_equal http.address, 'test.url'
|
|
244
|
+
assert_equal http.port, 443
|
|
245
|
+
assert_equal request.method, 'HEAD'
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def test_raises_an_error_if_the_url_is_invalid
|
|
249
|
+
assert_raises(SocketError) { CacheRules.make_request.call('http://test.urlzzz', {}, {}).call }
|
|
250
|
+
assert_raises(NoMethodError) { CacheRules.make_request.call('ftp://test.urlzzz', {}, {}).call }
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
end
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
class TestFormatting < MiniTest::Test
|
|
2
|
+
|
|
3
|
+
def setup
|
|
4
|
+
@request_headers = {
|
|
5
|
+
"Version" => "HTTP/1.1",
|
|
6
|
+
"user-agent" => "test user agent",
|
|
7
|
+
"Cookie" => "testcookie=deleteme",
|
|
8
|
+
"If-Modified-Since" => "Thu, 01 Jan 2015 07:03:42 GMT",
|
|
9
|
+
"Cache-Control" => "max-stale=1000, no-cache=\"Cookie\", no-store",
|
|
10
|
+
"Referer" => "http://some.url"
|
|
11
|
+
}
|
|
12
|
+
@cached_headers = {
|
|
13
|
+
"Retry-After" => 60,
|
|
14
|
+
"Cache-Control" => {
|
|
15
|
+
"public" => {"token"=>nil, "quoted_string"=>nil},
|
|
16
|
+
"max-stale" => {"token"=>"1000", "quoted_string"=>nil},
|
|
17
|
+
"no-cache" => {"token"=>nil, "quoted_string"=>"Cookie"}
|
|
18
|
+
},
|
|
19
|
+
"Last-Modified" => {"httpdate"=>"Thu, 01 Jan 2015 07:03:42 GMT", "timestamp"=>1420095822},
|
|
20
|
+
"ETag" => "\"validEtag\""
|
|
21
|
+
}
|
|
22
|
+
@nocache_headers = {"Version"=>"HTTP/1.1", "Set-Cookie"=>"test", "Cookie"=>"test", "Accept-Ranges"=>"test", "Range"=>"test", "If-Range"=>"test", "Content-Range"=>"test", "Referer"=>"http://test.url", "From"=>"test", "Authorization"=>"test", "Proxy-Authorization"=>"test", "User-Agent"=>"test", "If-Modified-Since"=>"invalid date"}
|
|
23
|
+
@duplicate_headers = [
|
|
24
|
+
['Accept', 'text/html'],
|
|
25
|
+
['Accept', 'application/json']
|
|
26
|
+
]
|
|
27
|
+
@non_duplicate_headers = [
|
|
28
|
+
['Age', '12345'],
|
|
29
|
+
['Age', '67890']
|
|
30
|
+
]
|
|
31
|
+
@normalized = {"Version"=>"HTTP/1.1", "User-Agent"=>"test user agent", "If-Modified-Since"=>{"httpdate"=>"Thu, 01 Jan 2015 07:03:42 GMT", "timestamp"=>1420095822}, "Cache-Control"=>{"max-stale"=>{"token"=>"1000", "quoted_string"=>nil}, "no-cache"=>{"token"=>nil, "quoted_string"=>"Cookie"}, "no-store"=>{"token"=>nil, "quoted_string"=>nil}}}
|
|
32
|
+
@normalized_fields = [["Version", "HTTP/1.1"], ["user-agent", "test user agent"], ["Cookie", "testcookie=deleteme"], ["If-Modified-Since", {"httpdate"=>"Thu, 01 Jan 2015 07:03:42 GMT", "timestamp"=>1420095822}], ["Cache-Control", {"max-stale"=>{"token"=>"1000", "quoted_string"=>nil}, "no-cache"=>{"token"=>nil, "quoted_string"=>"Cookie"}, "no-store"=>{"token"=>nil, "quoted_string"=>nil}}], ["Referer", "http://some.url"]]
|
|
33
|
+
@unnormalized_fields = {"Retry-After"=>"60", "Cache-Control"=>"public, max-stale=1000, no-cache=\"Cookie\"", "Last-Modified"=>"Thu, 01 Jan 2015 07:03:42 GMT", "ETag"=>"\"validEtag\""}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_normalize
|
|
37
|
+
normalized = CacheRules.normalize.call @request_headers
|
|
38
|
+
|
|
39
|
+
assert_kind_of Hash, normalized
|
|
40
|
+
|
|
41
|
+
assert_includes normalized, 'Version'
|
|
42
|
+
assert_includes normalized, 'User-Agent'
|
|
43
|
+
assert_includes normalized, 'If-Modified-Since'
|
|
44
|
+
assert_includes normalized, 'Cache-Control'
|
|
45
|
+
|
|
46
|
+
assert_equal normalized, @normalized
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def test_format_key
|
|
50
|
+
etag = CacheRules.format_key.call 'etag', 'test value'
|
|
51
|
+
user = CacheRules.format_key.call 'user-agent', 'test user agent'
|
|
52
|
+
vary = CacheRules.format_key.call 'Vary', '*'
|
|
53
|
+
|
|
54
|
+
assert_equal etag, ['ETag', 'test value']
|
|
55
|
+
assert_equal user, ['User-Agent', 'test user agent']
|
|
56
|
+
assert_equal vary, ['Vary', '*']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def test_clean
|
|
60
|
+
cleaned = CacheRules.clean.call @nocache_headers
|
|
61
|
+
# if_modified = CacheRules.clean.call @nocache_headers
|
|
62
|
+
assert_equal cleaned, [['Version', 'HTTP/1.1'], ['User-Agent', 'test']]
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_combine
|
|
66
|
+
combined = CacheRules.combine.call @duplicate_headers
|
|
67
|
+
not_combined = CacheRules.combine.call @non_duplicate_headers
|
|
68
|
+
|
|
69
|
+
assert_equal combined, [['Accept', 'text/html, application/json']]
|
|
70
|
+
assert_equal not_combined, [['Age', '12345']]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def test_normalize_fields
|
|
74
|
+
normalized = CacheRules.normalize_fields.call @request_headers
|
|
75
|
+
|
|
76
|
+
assert_kind_of Array, normalized
|
|
77
|
+
assert_equal normalized, @normalized_fields
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_unnormalize_fields
|
|
81
|
+
unnormalized = CacheRules.unnormalize_fields.call @cached_headers
|
|
82
|
+
|
|
83
|
+
assert_kind_of Hash, unnormalized
|
|
84
|
+
assert_equal unnormalized, @unnormalized_fields
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def test_format_field
|
|
88
|
+
httpdate = CacheRules.format_field.call 'If-Modified-Since', @request_headers['If-Modified-Since']
|
|
89
|
+
cache_control = CacheRules.format_field.call 'Cache-Control', @request_headers['Cache-Control']
|
|
90
|
+
csv = CacheRules.format_field.call 'Accept', 'text/html, application/json'
|
|
91
|
+
retry_after = CacheRules.format_field.call 'Retry-After', 60
|
|
92
|
+
retry_after_abs = CacheRules.format_field.call 'Retry-After', -100
|
|
93
|
+
retry_after_date= CacheRules.format_field.call 'Retry-After', 'Thu, 01 Jan 2015 07:03:42 GMT', false
|
|
94
|
+
other = CacheRules.format_field.call 'Version', @request_headers['Version'], false
|
|
95
|
+
|
|
96
|
+
cur_time = Time.now.gmtime.to_i
|
|
97
|
+
baddate = CacheRules.format_field.call 'If-Modified-Since', 'invalid date'
|
|
98
|
+
|
|
99
|
+
assert_kind_of Array, baddate
|
|
100
|
+
|
|
101
|
+
assert_equal httpdate, ["If-Modified-Since", {"httpdate"=>"Thu, 01 Jan 2015 07:03:42 GMT", "timestamp"=>1420095822}]
|
|
102
|
+
assert_equal cache_control, ["Cache-Control", {"max-stale"=>{"token"=>"1000", "quoted_string"=>nil}, "no-cache"=>{"token"=>nil, "quoted_string"=>"Cookie"}, "no-store"=>{"token"=>nil, "quoted_string"=>nil}}]
|
|
103
|
+
assert_equal csv, ["Accept", ["text/html", "application/json"]]
|
|
104
|
+
assert_equal retry_after, ["Retry-After", 60]
|
|
105
|
+
assert_equal retry_after_abs, ["Retry-After", 100]
|
|
106
|
+
assert_equal retry_after_date, ["Retry-After", {"httpdate"=>"Thu, 01 Jan 2015 07:03:42 GMT", "timestamp"=>1420095822}]
|
|
107
|
+
assert_equal other, ["Version", "HTTP/1.1"]
|
|
108
|
+
|
|
109
|
+
assert_in_delta baddate[1]["timestamp"], (cur_time - 300), 2, "Check the httpdate_helper, ensure it's 300 seconds, or increase the delta"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_format_field_string
|
|
113
|
+
cache_control = CacheRules.format_field.call 'Cache-Control', @cached_headers['Cache-Control'], true
|
|
114
|
+
csv = CacheRules.format_field.call 'Accept', ["text/html", "application/json"], true
|
|
115
|
+
|
|
116
|
+
assert_equal cache_control, ["Cache-Control", "public, max-stale=1000, no-cache=\"Cookie\""]
|
|
117
|
+
assert_equal csv, ["Accept", "text/html, application/json"]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_httpdate_helper
|
|
121
|
+
httpdate = "Sun, 06 Nov 1994 08:49:37 GMT"
|
|
122
|
+
rfc850 = "Sunday, 06-Nov-94 08:49:37 GMT"
|
|
123
|
+
ansi_c = "Sun Nov 6 08:49:37 1994"
|
|
124
|
+
result = 784111777
|
|
125
|
+
|
|
126
|
+
timestamp1 = CacheRules.httpdate_helper httpdate
|
|
127
|
+
timestamp2 = CacheRules.httpdate_helper rfc850
|
|
128
|
+
timestamp3 = CacheRules.httpdate_helper ansi_c
|
|
129
|
+
|
|
130
|
+
assert_equal timestamp1, result
|
|
131
|
+
assert_equal timestamp2, result
|
|
132
|
+
assert_equal timestamp3, result
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
end
|