/usr/lib/ruby/vendor_ruby/rack/cache/response.rb is in ruby-rack-cache 1.2-4.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | require 'time'
require 'set'
require 'rack/response'
require 'rack/utils'
require 'rack/cache/cachecontrol'
module Rack::Cache
# Provides access to the response generated by the downstream application. The
# +response+, +original_response+, and +entry+ objects exposed by the Core
# caching engine are instances of this class.
#
# Response objects respond to a variety of convenience methods, including
# those defined in Rack::Response::Helpers, Rack::Cache::Headers,
# and Rack::Cache::ResponseHeaders.
#
# Note that Rack::Cache::Response is not a subclass of Rack::Response and does
# not perform many of the same initialization and finalization tasks. For
# example, the body is not slurped during initialization and there are no
# facilities for generating response output.
class Response
include Rack::Response::Helpers
# Rack response tuple accessors.
attr_accessor :status, :headers, :body
# The time when the Response object was instantiated.
attr_reader :now
# Create a Response instance given the response status code, header hash,
# and body.
def initialize(status, headers, body)
@status = status.to_i
@headers = Rack::Utils::HeaderHash.new(headers)
@body = body
@now = Time.now
@headers['Date'] ||= @now.httpdate
end
def initialize_copy(other)
super
@headers = other.headers.dup
end
# Return the status, headers, and body in a three-tuple.
def to_a
[status, headers.to_hash, body]
end
# Status codes of responses that MAY be stored by a cache or used in reply
# to a subsequent request.
#
# http://tools.ietf.org/html/rfc2616#section-13.4
CACHEABLE_RESPONSE_CODES = [
200, # OK
203, # Non-Authoritative Information
300, # Multiple Choices
301, # Moved Permanently
302, # Found
404, # Not Found
410 # Gone
].to_set
# A Hash of name=value pairs that correspond to the Cache-Control header.
# Valueless parameters (e.g., must-revalidate, no-store) have a Hash value
# of true. This method always returns a Hash, empty if no Cache-Control
# header is present.
def cache_control
@cache_control ||= CacheControl.new(headers['Cache-Control'])
end
# Set the Cache-Control header to the values specified by the Hash. See
# the #cache_control method for information on expected Hash structure.
def cache_control=(value)
if value.respond_to? :to_hash
cache_control.clear
cache_control.merge!(value)
value = cache_control.to_s
end
if value.nil? || value.empty?
headers.delete('Cache-Control')
else
headers['Cache-Control'] = value
end
end
# Determine if the response is "fresh". Fresh responses may be served from
# cache without any interaction with the origin. A response is considered
# fresh when it includes a Cache-Control/max-age indicator or Expiration
# header and the calculated age is less than the freshness lifetime.
def fresh?
ttl && ttl > 0
end
# Determine if the response is worth caching under any circumstance. Responses
# marked "private" with an explicit Cache-Control directive are considered
# uncacheable
#
# Responses with neither a freshness lifetime (Expires, max-age) nor cache
# validator (Last-Modified, ETag) are considered uncacheable.
def cacheable?
return false unless CACHEABLE_RESPONSE_CODES.include?(status)
return false if cache_control.no_store? || cache_control.private?
validateable? || fresh?
end
# Determine if the response includes headers that can be used to validate
# the response with the origin using a conditional GET request.
def validateable?
headers.key?('Last-Modified') || headers.key?('ETag')
end
# Mark the response "private", making it ineligible for serving other
# clients.
def private=(value)
value = value ? true : nil
self.cache_control = cache_control.
merge('public' => !value, 'private' => value)
end
# Indicates that the cache must not serve a stale response in any
# circumstance without first revalidating with the origin. When present,
# the TTL of the response should not be overriden to be greater than the
# value provided by the origin.
def must_revalidate?
cache_control.must_revalidate || cache_control.proxy_revalidate
end
# Mark the response stale by setting the Age header to be equal to the
# maximum age of the response.
def expire!
headers['Age'] = max_age.to_s if fresh?
end
# The date, as specified by the Date header. When no Date header is present,
# set the Date header to Time.now and return.
def date
if date = headers['Date']
Time.httpdate(date)
else
headers['Date'] = now.httpdate unless headers.frozen?
now
end
end
# The age of the response.
def age
(headers['Age'] || [(now - date).to_i, 0].max).to_i
end
# The number of seconds after the time specified in the response's Date
# header when the the response should no longer be considered fresh. First
# check for a s-maxage directive, then a max-age directive, and then fall
# back on an expires header; return nil when no maximum age can be
# established.
def max_age
cache_control.shared_max_age ||
cache_control.max_age ||
(expires && (expires - date))
end
# The value of the Expires header as a Time object.
def expires
headers['Expires'] && Time.httpdate(headers['Expires'])
end
# The number of seconds after which the response should no longer
# be considered fresh. Sets the Cache-Control max-age directive.
def max_age=(value)
self.cache_control = cache_control.merge('max-age' => value.to_s)
end
# Like #max_age= but sets the s-maxage directive, which applies only
# to shared caches.
def shared_max_age=(value)
self.cache_control = cache_control.merge('s-maxage' => value.to_s)
end
# The response's time-to-live in seconds, or nil when no freshness
# information is present in the response. When the responses #ttl
# is <= 0, the response may not be served from cache without first
# revalidating with the origin.
def ttl
max_age - age if max_age
end
# Set the response's time-to-live for shared caches to the specified number
# of seconds. This adjusts the Cache-Control/s-maxage directive.
def ttl=(seconds)
self.shared_max_age = age + seconds
end
# Set the response's time-to-live for private/client caches. This adjusts
# the Cache-Control/max-age directive.
def client_ttl=(seconds)
self.max_age = age + seconds
end
# The String value of the Last-Modified header exactly as it appears
# in the response (i.e., no date parsing / conversion is performed).
def last_modified
headers['Last-Modified']
end
# The literal value of ETag HTTP header or nil if no ETag is specified.
def etag
headers['ETag']
end
# Headers that MUST NOT be included with 304 Not Modified responses.
#
# http://tools.ietf.org/html/rfc2616#section-10.3.5
NOT_MODIFIED_OMIT_HEADERS = %w[
Allow
Content-Encoding
Content-Language
Content-Length
Content-MD5
Content-Type
Last-Modified
].to_set
# Modify the response so that it conforms to the rules defined for
# '304 Not Modified'. This sets the status, removes the body, and
# discards any headers that MUST NOT be included in 304 responses.
#
# http://tools.ietf.org/html/rfc2616#section-10.3.5
def not_modified!
body.close if body.respond_to?(:close)
self.status = 304
self.body = []
NOT_MODIFIED_OMIT_HEADERS.each { |name| headers.delete(name) }
nil
end
# The literal value of the Vary header, or nil when no header is present.
def vary
headers['Vary']
end
# Does the response include a Vary header?
def vary?
! vary.nil?
end
# An array of header names given in the Vary header or an empty
# array when no Vary header is present.
def vary_header_names
return [] unless vary = headers['Vary']
vary.split(/[\s,]+/)
end
end
end
|