Strong ETag validators #24387

Merged
merged 1 commit into from Apr 2, 2016

Conversation

Projects
None yet
3 participants
@jeremy
Member

jeremy commented Apr 1, 2016

  • Introduce Response#strong_etag= and #weak_etag= and analogous options
    for fresh_when and stale?. Response#etag= sets a weak ETag.

    Strong ETags are desirable when you're serving byte-for-byte identical
    responses that support Range requests, like PDFs or videos (typically
    done by reproxying the response from a backend storage service).
    Also desirable when fronted by some CDNs that support strong ETags
    only, like Akamai.

  • No longer strips quotes (") from ETag values before comparing them.
    Quotes are significant, part of the ETag. A quoted ETag and an unquoted
    one are not the same entity.

  • Support If-None-Match: *. Rarely useful for GET requests; meant
    to provide some optimistic concurrency control for PUT requests.

Strong ETag validators
* Introduce `Response#strong_etag=` and `#weak_etag=` and analogous options
  for `fresh_when` and `stale?`. `Response#etag=` sets a weak ETag.

  Strong ETags are desirable when you're serving byte-for-byte identical
  responses that support Range requests, like PDFs or videos (typically
  done by reproxying the response from a backend storage service).
  Also desirable when fronted by some CDNs that support strong ETags
  only, like Akamai.

* No longer strips quotes (`"`) from ETag values before comparing them.
  Quotes are significant, part of the ETag. A quoted ETag and an unquoted
  one are not the same entity.

* Support `If-None-Match: *`. Rarely useful for GET requests; meant
  to provide some optimistic concurrency control for PUT requests.

@jeremy jeremy merged commit 863d385 into rails:master Apr 2, 2016

1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details

@jeremy jeremy deleted the jeremy:strong-etag branch Apr 2, 2016

y-yagi added a commit to y-yagi/rails that referenced this pull request Oct 24, 2016

update description of Etag [ci skip]
The document is written with "only generate weak", but it can also be used to
strong etag.
Also, add missing entory for #24387

maclover7 added a commit to maclover7/rails that referenced this pull request Nov 16, 2016

update description of Etag [ci skip]
The document is written with "only generate weak", but it can also be used to
strong etag.
Also, add missing entory for #24387
@@ -86,12 +101,16 @@ def etag(&etagger)
#
# before_action { fresh_when @article, template: 'widgets/show' }
#
- def fresh_when(object = nil, etag: object, last_modified: nil, public: false, template: nil)
+ def fresh_when(object = nil, etag: nil, weak_etag: nil, strong_etag: nil, last_modified: nil, public: false, template: nil)
+ weak_etag ||= etag || object unless strong_etag

This comment has been minimized.

@claudiob

claudiob Mar 2, 2017

Member

Hello @jeremy ! I have a question about this change (if you remember… it's almost one year old).

Before this PR, I was able to use fresh_when to cache a page based solely on last_modified, and completely ignoring the body.

For instance, I could have an action like this:

def index
  @sections = Section.all
  fresh_when @sections, etag: nil
end

which would render the page if any Section had been updated, or respond with 304 otherwise.

After this PR, I cannot find a way to not set an etag.
Even if I pass etag: nil or etag: false, this line:

weak_etag ||= etag || object unless strong_etag

ends up using object as the value of etag.

I understand the good intention of this PR: in principle, if your response has changed (and therefore Etag), you want to expire the cache. However, in my case, using last_modified is enough. If last_modified didn't change, then I want to serve the cached version without checking the Etag as well.

Am I correct in saying that this approach cannot be taken anymore after this PR?

@claudiob

claudiob Mar 2, 2017

Member

Hello @jeremy ! I have a question about this change (if you remember… it's almost one year old).

Before this PR, I was able to use fresh_when to cache a page based solely on last_modified, and completely ignoring the body.

For instance, I could have an action like this:

def index
  @sections = Section.all
  fresh_when @sections, etag: nil
end

which would render the page if any Section had been updated, or respond with 304 otherwise.

After this PR, I cannot find a way to not set an etag.
Even if I pass etag: nil or etag: false, this line:

weak_etag ||= etag || object unless strong_etag

ends up using object as the value of etag.

I understand the good intention of this PR: in principle, if your response has changed (and therefore Etag), you want to expire the cache. However, in my case, using last_modified is enough. If last_modified didn't change, then I want to serve the cached version without checking the Etag as well.

Am I correct in saying that this approach cannot be taken anymore after this PR?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment