class Locomotive::Steam::Middlewares::Cache

Constants

CACHEABLE_REQUEST_METHODS
CACHEABLE_RESPONSE_CODES
DEFAULT_CACHE_CONTROL
DEFAULT_CACHE_VARY
NO_CACHE_CONTROL

Public Instance Methods

_call() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 18
def _call
  if cacheable?
    key = cache_key

    # TODO: only for debugging
    # log("HTTP keys: #{env.select { |key, _| key.starts_with?('HTTP_') }}".light_blue)

    # Test if the ETag or Last Modified has been modified. If not, return a 304 response
    if stale?(key)
      render_response(nil, 304, nil)
      return
    end

    # we have to tell the CDN (or any proxy) what the expiration & validation strategy are
    env['steam.cache_control']        = cache_control
    env['steam.cache_vary']           = cache_vary
    env['steam.cache_etag']           = key
    env['steam.cache_last_modified']  = site.last_modified_at.httpdate

    # retrieve the response from the cache.
    # This is useful if no CDN is being used.
    code, headers, _ = response = fetch_cached_response(key)

    unless CACHEABLE_RESPONSE_CODES.include?(code.to_i)
      env['steam.cache_control'] = headers['Cache-Control'] = NO_CACHE_CONTROL
      env['steam.cache_vary'] = headers['Vary'] = nil
    end

    # we don't want to render twice the page
    @next_response = response
  else
    env['steam.cache_control']  = NO_CACHE_CONTROL
  end
end

Private Instance Methods

cache() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 110
def cache
  services.cache
end
cache_control() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 83
def cache_control
  page.try(:cache_control).presence || site.try(:cache_control).presence || DEFAULT_CACHE_CONTROL
end
cache_key() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 77
def cache_key
  site, path, query = env['steam.site'], env['PATH_INFO'], env['QUERY_STRING']
  key = "#{Locomotive::Steam::VERSION}/site/#{site._id}/#{site.last_modified_at.to_i}/page/#{path}/#{query}"
  Digest::MD5.hexdigest(key)
end
cache_vary() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 87
def cache_vary
  page.try(:cache_vary).presence || site.try(:cache_vary).presence || DEFAULT_CACHE_VARY
end
cacheable?() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 69
def cacheable?
  CACHEABLE_REQUEST_METHODS.include?(env['REQUEST_METHOD']) &&
  !live_editing? &&
  site.try(:cache_enabled) &&
  page.try(:cache_enabled) &&
  is_redirect_url?
end
fetch_cached_response(key) click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 55
def fetch_cached_response(key)
  log("Cache key = #{key.inspect}")
  if marshaled = cache.read(key)
    log('Cache HIT')
    Marshal.load(marshaled)
  else
    log('Cache MISS')
    self.next.tap do |response|
      # cache the HTML for further validations (+ optimization)
      cache.write(key, marshal(response))
    end
  end
end
is_redirect_url?() click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 91
def is_redirect_url?
  return false if page.nil?
  page.try(:redirect_url).blank?
end
marshal(response) click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 96
def marshal(response)
  code, headers, body = response

  # only keep string value headers
  _headers = headers.reject { |key, val| !val.respond_to?(:to_str) }

  Marshal.dump([code, _headers, body])
end
stale?(key) click to toggle source
# File lib/locomotive/steam/middlewares/cache.rb, line 105
def stale?(key)
  env['HTTP_IF_NONE_MATCH'] == key ||
  env['HTTP_IF_MODIFIED_SINCE'] == site.last_modified_at.httpdate
end