Class: HTTP::Session::Response

Inherits:
SimpleDelegator
  • Object
show all
Defined in:
lib/http/session/response.rb,
lib/http/session/response/string_body.rb

Overview

Provides access to the HTTP response.

Mostly borrowed from rack-cache/lib/rack/cache/response.rb

Defined Under Namespace

Classes: StringBody

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.

https://datatracker.ietf.org/doc/html/rfc9110#section-15.1

[
  200, # OK
  203, # Non-Authoritative Information
  204, # No Content
  206, # Partial Content
  300, # Multiple Choices
  301, # Moved Permanently
  308, # Permanent Redirect
  404, # Not Found
  405, # Method Not Allowed
  410, # Gone
  414, # URI Too Long
  501  # Not Implemented
].to_set

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*args) ⇒ Response

Returns a new instance of Response.



36
37
38
39
40
41
# File 'lib/http/session/response.rb', line 36

def initialize(*args)
  super

  _hs_ensure_header_date
  @history = []
end

Instance Attribute Details

#historyArray<Response>

Returns a list of response objects holding the history of the redirection.

Returns:

  • (Array<Response>)

    a list of response objects holding the history of the redirection



33
34
35
# File 'lib/http/session/response.rb', line 33

def history
  @history
end

Class Method Details

.new(*args) ⇒ Object



7
8
9
# File 'lib/http/session/response.rb', line 7

def new(*args)
  args[0].is_a?(self) ? args[0] : super
end

Instance Method Details

#ageNumeric

The age of the response.

Returns:

  • (Numeric)


145
146
147
# File 'lib/http/session/response.rb', line 145

def age
  (headers[HTTP::Headers::AGE] || [(now - date).to_i, 0].max).to_i
end

#cache_controlCache::CacheControl

A CacheControl instance based on the response's cache-control header.

Returns:



52
53
54
# File 'lib/http/session/response.rb', line 52

def cache_control
  @cache_control ||= HTTP::Session::Cache::CacheControl.new(headers[HTTP::Headers::CACHE_CONTROL])
end

#cacheable?(shared:, req:) ⇒ Boolean

Determine if the response is worth caching under any circumstance.

https://datatracker.ietf.org/doc/html/rfc9111#section-3

Returns:

  • (Boolean)


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/http/session/response.rb', line 64

def cacheable?(shared:, req:)
  # the response status code is final (see Section 15 of [HTTP])
  return false unless CACHEABLE_RESPONSE_CODES.include?(status)

  # the no-store cache directive is not present in the response (see Section 5.2.2.5)
  return false if cache_control.no_store?

  # if the cache is shared
  if shared
    # the private response directive is either not present or allows a shared cache
    # to store a modified response; see Section 5.2.2.7)
    return false if cache_control.private?

    # the Authorization header field is not present in the request (see Section 11.6.2
    # of [HTTP]) or a response directive is present that explicitly allows shared
    # caching (see Section 3.5)
    return false if req.headers[HTTP::Headers::AUTHORIZATION] &&
      (!cache_control.public? && !cache_control.shared_max_age)
  end

  # responses with neither a freshness lifetime (expires, max-age) nor cache validator
  # (last-modified, etag) are considered uncacheable
  validateable? || fresh?(shared: shared)
end

#dateTime

The date of the response.

Returns:

  • (Time)


138
139
140
# File 'lib/http/session/response.rb', line 138

def date
  Time.httpdate(headers[HTTP::Headers::DATE])
end

#etagObject

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



150
151
152
# File 'lib/http/session/response.rb', line 150

def etag
  headers[HTTP::Headers::ETAG]
end

#expiresTime

The value of the expires header as a Time object.

Returns:

  • (Time)


129
130
131
132
133
# File 'lib/http/session/response.rb', line 129

def expires
  headers[HTTP::Headers::EXPIRES] && Time.httpdate(headers[HTTP::Headers::EXPIRES])
rescue
  nil
end

#fresh?(shared:) ⇒ 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)


99
100
101
# File 'lib/http/session/response.rb', line 99

def fresh?(shared:)
  ttl(shared: shared) && ttl(shared: shared) > 0
end

#from_cache?Boolean

Determine if the response is served from cache.

Returns:

  • (Boolean)


44
45
46
47
# File 'lib/http/session/response.rb', line 44

def from_cache?
  v = headers[HTTP::Session::Cache::Status::HEADER_NAME]
  HTTP::Session::Cache::Status.HIT?(v)
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).



156
157
158
# File 'lib/http/session/response.rb', line 156

def last_modified
  headers[HTTP::Headers::LAST_MODIFIED]
end

#max_age(shared:) ⇒ Numeric

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.

Returns:

  • (Numeric)


120
121
122
123
124
# File 'lib/http/session/response.rb', line 120

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

#no_cache?Boolean

True when the cache-control/no-cache directive is present.

Returns:

  • (Boolean)


57
58
59
# File 'lib/http/session/response.rb', line 57

def no_cache?
  cache_control.no_cache?
end

#nowTime

The current time.

Returns:

  • (Time)


163
164
165
# File 'lib/http/session/response.rb', line 163

def now
  Time.now
end

#ttl(shared:) ⇒ Numeric

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.

Returns:

  • (Numeric)


109
110
111
# File 'lib/http/session/response.rb', line 109

def ttl(shared:)
  max_age(shared: shared) - age if max_age(shared: shared)
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)


91
92
93
# File 'lib/http/session/response.rb', line 91

def validateable?
  headers.include?(HTTP::Headers::LAST_MODIFIED) || headers.include?(HTTP::Headers::ETAG)
end