Class: Rack::SteadyETag

Inherits:
Object
  • Object
show all
Defined in:
lib/rack/steady_etag.rb

Overview

Based on Rack::Etag from rack 2.2.3 github.com/rack/rack/blob/v2.2.3/lib/rack/etag.rb

Automatically sets the ETag header on all String bodies.

The ETag header is skipped if ETag or Last-Modified headers are sent or if a sendfile body (body.responds_to :to_path) is given (since such cases should be handled by apache/nginx).

Constant Summary collapse

DEFAULT_CACHE_CONTROL =

Yes, Rack::ETag sets a default Cache-Control for responses that it can digest.

"max-age=0, private, must-revalidate"
STRIP_PATTERNS =
[
  /<meta\b[^>]*\bname=(["'])csrf-token\1[^>]+>/i,
  /<meta\b[^>]*\bname=(["'])csp-nonce\1[^>]+>/i,
  /<input\b[^>]*\bname=(["'])authenticity_token\1[^>]+>/i,
  lambda { |string| string.gsub(/(<script\b[^>]*)\bnonce=(["'])[^"']+\2+/i, '\1') }
]
STRIP_CONTENT_TYPES =
%w[
  text/html
  application/xhtml+xml
]

Instance Method Summary collapse

Constructor Details

#initialize(app, no_digest_cache_control = nil, digest_cache_control = DEFAULT_CACHE_CONTROL) ⇒ SteadyETag

Returns a new instance of SteadyETag.



32
33
34
35
36
37
38
39
40
# File 'lib/rack/steady_etag.rb', line 32

def initialize(app, no_digest_cache_control = nil, digest_cache_control = DEFAULT_CACHE_CONTROL)
  @app = app

  @digest_cache_control = digest_cache_control

  # Rails sets a default `Cache-Control: no-cache` for responses that we cannot digest.
  # See https://github.com/rails/rails/blob/d96609505511a76c618dc3adfa3ca4679317d008/railties/lib/rails/application/default_middleware_stack.rb#L81
  @no_digest_cache_control = no_digest_cache_control
end

Instance Method Details

#call(env) ⇒ Object



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
# File 'lib/rack/steady_etag.rb', line 42

def call(env)
  status, headers, body = @app.call(env)
  headers = case_insensitive_headers(headers)
  session = env['rack.session']

  if etag_status?(status) && etag_body?(body) && !skip_caching?(headers)
    original_body = body
    digest, new_body = digest_body(body, headers, session)
    body = Rack::BodyProxy.new(new_body) do
      original_body.close if original_body.respond_to?(:close)
    end
    headers['ETag'] = %(W/"#{digest}") if digest
  end

  # It would make more sense to only set a Cache-Control for responses that we process.
  # However, the original Rack::ETag sets Cache-Control: @no_digest_cache_control
  # for all responses, even responses that we don't otherwise modify.
  # Hence if we move this code into the `if` above we would remove Rails' default
  # Cache-Control headers for non-digestable responses, which would be a considerable
  # change in behavior.
  if digest
    set_cache_control_with_digest(headers)
  else
    set_cache_control_without_digest(headers)
  end

  [status, headers, body]
end