This repository has been archived by the owner on Dec 3, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
validations.rb
129 lines (99 loc) · 5.11 KB
/
validations.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# Copyright (c) 2014-2016 Alexander Williams, Unscramble <license@unscramble.jp>
module CacheRules
extend self
# HTTP Header Validators
#
# Parameters must always be normalized
#
# Return must always be 0 or 1
#
# The If-Match and If-Unmodified-Since conditional header fields are not applicable to a cache.
# source: https://tools.ietf.org/html/rfc7234#section-4.3.2
def to_bit(&predicate)
predicate.call ? 1 : 0
end
def validate_cached?(headers)
to_bit { headers[:cached].length > 0 }
end
# Precedence: If-None-Match (ETag), then If-Modified-Since (Last-Modified)
# source: https://tools.ietf.org/html/rfc7232#section-6
def validate_precond_match?(headers)
request, cached = headers.values_at :request, :cached
return 0 if cached.length == 0
# Return when the If-None-Match header exists, ignore If-Modified-Since
# source: https://tools.ietf.org/html/rfc7232#section-3.3
etag_match = helper_etag(request, cached)
return to_bit { etag_match } unless etag_match.nil?
to_bit { helper_last_modified(request, cached) }
end
# Compare headers to see if the cached request is expired (Freshness)
# source: https://tools.ietf.org/html/rfc7234#section-4.2
def validate_expired?(headers)
return 0 if headers[:cached].length == 0
freshness_lifetime, current_age = helper_freshness_lifetime.call headers[:cached]
response_is_fresh = freshness_lifetime.to_i > current_age
return 1 if headers[:cached]['Cache-Control'] &&
headers[:cached]['Cache-Control']['max-age'] &&
current_age > headers[:cached]['Cache-Control']['max-age']['token'].to_i
to_bit { (response_is_fresh != true) }
end
def validate_only_if_cached?(headers)
to_bit { headers[:request]['Cache-Control'] && headers[:request]['Cache-Control']['only-if-cached'] }
end
# Serving Stale Responses
# source: https://tools.ietf.org/html/rfc7234#section-4.2.4
def validate_allow_stale?(headers)
request, cached = headers.values_at :request, :cached
return 0 if cached.length == 0 || helper_validate_allow_stale(request, cached)
freshness_lifetime, current_age = helper_freshness_lifetime.call cached
max_stale = helper_max_stale.call request['Cache-Control'], freshness_lifetime, current_age
min_fresh = helper_min_fresh.call request['Cache-Control'], freshness_lifetime, current_age
to_bit { (max_stale && min_fresh != false) || (max_stale.nil? && min_fresh) }
end
# Response Cache-Control Directives
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2
def validate_must_revalidate?(headers)
return 1 if headers[:cached].length == 0
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.1
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.7
to_bit { (( cached = headers[:cached]['Cache-Control'] )) && ( cached['must-revalidate'] || cached['proxy-revalidate'] ) }
end
# Verify if we're explicitly told not to serve a response without revalidation
def validate_no_cache?(headers)
request_headers, cached_headers = headers.values_at :request, :cached
return 1 if cached_headers.length == 0
# Must revalidate if this request header exists
# source: https://tools.ietf.org/html/rfc7234#section-5.2.1.4
return 1 if (request_headers['Cache-Control'] && request_headers['Cache-Control']['no-cache'])
_, current_age = helper_freshness_lifetime.call cached_headers
# If max-age is 0 or if the current age is above the max-age
# source: https://tools.ietf.org/html/rfc7234#section-5.2.1.1
return 1 if request_headers['Cache-Control'] &&
request_headers['Cache-Control']['max-age'] &&
(request_headers['Cache-Control']['max-age']['token'].to_s == "0" || current_age > request_headers['Cache-Control']['max-age']['token'].to_i)
# source: https://tools.ietf.org/html/rfc7234#section-5.2.2.2
# source: https://tools.ietf.org/html/rfc7234#section-3.2
if cached_headers['Cache-Control']
return 1 if (( cached = cached_headers['Cache-Control'] )) &&
helper_no_cache.call(cached_headers) ||
(cached['no-cache'] && cached['no-cache']['quoted_string'].nil?) ||
(cached['s-maxage'] && cached['s-maxage']['token'].to_s == "0") ||
(cached['max-age'] && cached['max-age']['token'].to_s == "0")
end
# source: https://tools.ietf.org/html/rfc7234#section-5.4
# Legacy support for HTTP/1.0 Pragma header
return 1 if request_headers['Pragma'] && request_headers['Pragma']['no-cache']
return 0
end
def validate_is_error?(headers)
to_bit { headers[:response]['Status'].to_i.between?(500,599) }
end
def validate_validator_match?(headers)
request, response = headers.values_at :request, :response
to_bit { response['ETag'] && request['If-None-Match'] && (request['If-None-Match'].include?(response['ETag']) || request['If-None-Match'].include?("*")) }
end
end