Class: Rack::Cache::Response

Inherits:
Object
  • Object
show all
Includes:
Response::Helpers
Defined in:
lib/rack/cache/response.rb

Overview

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.

Constant Summary collapse

CACHEABLE_RESPONSE_CODES =

Status codes of responses that MAY be stored by a cache or used in reply to a subsequent request.

tools.ietf.org/html/rfc2616#section-13.4

[
  200, # OK
  203, # Non-Authoritative Information
  300, # Multiple Choices
  301, # Moved Permanently
  302, # Found
  404, # Not Found
  410  # Gone
].to_set
NOT_MODIFIED_OMIT_HEADERS =

Headers that MUST NOT be included with 304 Not Modified responses.

tools.ietf.org/html/rfc2616#section-10.3.5

%w[
  Allow
  Content-Encoding
  Content-Language
  Content-Length
  Content-MD5
  Content-Type
  Last-Modified
].to_set

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(status, headers, body) ⇒ Response

Create a Response instance given the response status code, header hash, and body.



32
33
34
35
36
37
38
# File 'lib/rack/cache/response.rb', line 32

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

Instance Attribute Details

#bodyObject

Rack response tuple accessors.



25
26
27
# File 'lib/rack/cache/response.rb', line 25

def body
  @body
end

#headersObject

Rack response tuple accessors.



25
26
27
# File 'lib/rack/cache/response.rb', line 25

def headers
  @headers
end

#nowObject (readonly)

The time when the Response object was instantiated.



28
29
30
# File 'lib/rack/cache/response.rb', line 28

def now
  @now
end

#statusObject

Rack response tuple accessors.



25
26
27
# File 'lib/rack/cache/response.rb', line 25

def status
  @status
end

Instance Method Details

#ageObject

The age of the response.



152
153
154
# File 'lib/rack/cache/response.rb', line 152

def age
  (headers['Age'] ||  [(now - date).to_i, 0].max).to_i
end

#cache_controlObject

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.



69
70
71
# File 'lib/rack/cache/response.rb', line 69

def cache_control
  @cache_control ||= CacheControl.new(headers['Cache-Control'])
end

#cache_control=(value) ⇒ Object

Set the Cache-Control header to the values specified by the Hash. See the #cache_control method for information on expected Hash structure.



75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/rack/cache/response.rb', line 75

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

#cacheable?Boolean

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.

Returns:

  • (Boolean)


103
104
105
106
107
# File 'lib/rack/cache/response.rb', line 103

def cacheable?
  return false unless CACHEABLE_RESPONSE_CODES.include?(status)
  return false if cache_control.no_store? || cache_control.private?
  validateable? || fresh?
end

#client_ttl=(seconds) ⇒ Object

Set the response’s time-to-live for private/client caches. This adjusts the Cache-Control/max-age directive.



209
210
211
# File 'lib/rack/cache/response.rb', line 209

def client_ttl=(seconds)
  self.max_age = age + seconds
end

#dateObject

The date, as specified by the Date header. When no Date header is present or is unparseable, set the Date header to Time.now and return.



139
140
141
142
143
144
145
146
147
148
149
# File 'lib/rack/cache/response.rb', line 139

def date
  if date = headers['Date']
    Time.httpdate(date)
  else
    headers['Date'] = now.httpdate unless headers.frozen?
    now
  end
rescue ArgumentError
  headers['Date'] = now.httpdate unless headers.frozen?
  now
end

#etagObject

The literal value of ETag HTTP header or nil if no ETag is specified.



220
221
222
# File 'lib/rack/cache/response.rb', line 220

def etag
  headers['ETag']
end

#expire!Object

Mark the response stale by setting the Age header to be equal to the maximum age of the response.



133
134
135
# File 'lib/rack/cache/response.rb', line 133

def expire!
  headers['Age'] = max_age.to_s if fresh?
end

#expiresObject

The value of the Expires header as a Time object.



169
170
171
172
173
# File 'lib/rack/cache/response.rb', line 169

def expires
  headers['Expires'] && Time.httpdate(headers['Expires'])
rescue ArgumentError
  nil
end

#fresh?Boolean

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.

Returns:

  • (Boolean)


93
94
95
# File 'lib/rack/cache/response.rb', line 93

def fresh?
  ttl && ttl > 0
end

#initialize_copy(other) ⇒ Object



40
41
42
43
44
# File 'lib/rack/cache/response.rb', line 40

def initialize_copy(other)
  super
  @headers = other.headers.dup
  @cache_control = nil
end

#last_modifiedObject

The String value of the Last-Modified header exactly as it appears in the response (i.e., no date parsing / conversion is performed).



215
216
217
# File 'lib/rack/cache/response.rb', line 215

def last_modified
  headers['Last-Modified']
end

#max_ageObject

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 r-maxage directive, then 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.



161
162
163
164
165
166
# File 'lib/rack/cache/response.rb', line 161

def max_age
  cache_control.reverse_max_age ||
    cache_control.shared_max_age ||
      cache_control.max_age ||
       (expires && (expires - date))
end

#max_age=(value) ⇒ Object

The number of seconds after which the response should no longer be considered fresh. Sets the Cache-Control max-age directive.



177
178
179
# File 'lib/rack/cache/response.rb', line 177

def max_age=(value)
  self.cache_control = cache_control.merge('max-age' => value.to_s)
end

#must_revalidate?Boolean

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.

Returns:

  • (Boolean)


127
128
129
# File 'lib/rack/cache/response.rb', line 127

def must_revalidate?
  cache_control.must_revalidate || cache_control.proxy_revalidate
end

#not_modified!Object

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.

tools.ietf.org/html/rfc2616#section-10.3.5



242
243
244
245
246
247
248
# File 'lib/rack/cache/response.rb', line 242

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

#private=(value) ⇒ Object

Mark the response “private”, making it ineligible for serving other clients.



117
118
119
120
121
# File 'lib/rack/cache/response.rb', line 117

def private=(value)
  value = value ? true : nil
  self.cache_control = cache_control.
    merge('public' => !value, 'private' => value)
end

#reverse_max_age=(value) ⇒ Object

Like #shared_max_age= but sets the r-maxage directive, which applies only to reverse caches.



189
190
191
# File 'lib/rack/cache/response.rb', line 189

def reverse_max_age=(value)
  self.cache_control = cache_control.merge('r-maxage' => value.to_s)
end

#shared_max_age=(value) ⇒ Object

Like #max_age= but sets the s-maxage directive, which applies only to shared caches.



183
184
185
# File 'lib/rack/cache/response.rb', line 183

def shared_max_age=(value)
  self.cache_control = cache_control.merge('s-maxage' => value.to_s)
end

#to_aObject

Return the status, headers, and body in a three-tuple.



47
48
49
# File 'lib/rack/cache/response.rb', line 47

def to_a
  [status, headers.to_hash, body]
end

#ttlObject

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.



197
198
199
# File 'lib/rack/cache/response.rb', line 197

def ttl
  max_age - age if max_age
end

#ttl=(seconds) ⇒ Object

Set the response’s time-to-live for shared caches to the specified number of seconds. This adjusts the Cache-Control/s-maxage directive.



203
204
205
# File 'lib/rack/cache/response.rb', line 203

def ttl=(seconds)
  self.shared_max_age = age + seconds
end

#validateable?Boolean

Determine if the response includes headers that can be used to validate the response with the origin using a conditional GET request.

Returns:

  • (Boolean)


111
112
113
# File 'lib/rack/cache/response.rb', line 111

def validateable?
  headers.key?('Last-Modified') || headers.key?('ETag')
end

#varyObject

The literal value of the Vary header, or nil when no header is present.



251
252
253
# File 'lib/rack/cache/response.rb', line 251

def vary
  headers['Vary']
end

#vary?Boolean

Does the response include a Vary header?

Returns:

  • (Boolean)


256
257
258
# File 'lib/rack/cache/response.rb', line 256

def vary?
  ! vary.nil?
end

#vary_header_namesObject

An array of header names given in the Vary header or an empty array when no Vary header is present.



262
263
264
265
# File 'lib/rack/cache/response.rb', line 262

def vary_header_names
  return [] unless vary = headers['Vary']
  vary.split(/[\s,]+/)
end