José Valim
2008-Oct-19 13:51 UTC
request.fresh? implementation does not follow HTTP specification
According to the specification, if both Last-Modified date and Entity Tags are sent, both must be validated to consider the request fresh: http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4 Then the current implementation is wrong, because it considers the request fresh with one OR the other is fresh: def fresh?(response) not_modified?(response.last_modified) || etag_matches? (response.etag) end The correct implementation would be: def not_modified?(modified_at) modified_at && if_modified_since >= modified_at end def etag_matches?(etag) if_none_match == etag end def fresh?(response) if if_modified_since && if_none_match not_modified?(response.last_modified) && etag_matches? (response.etag) if if_modified_since not_modified?(response.last_modified) if if_none_match etag_matches?(response.etag) else false end end Why bother? Sometimes I want to cache a resource considering its timestamp, but this cache is only valid for the current user (works like scoping): def show response.last_modified = resource.updated_at response.etag = current_user if request.fresh?(response) head :not_modified else # ... end end In the current implementation, even the first example given by Ryan Daigle on his blog will fail: http://www.ryandaigle.com/articles/2008/8/14/what-s-new-in-edge-rails-simpler-conditional-get-support-etags --- José Valim http://josevalim.blogspot.com/ http://www.pagestacker.com/ --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
Michael Koziarski
2008-Oct-19 15:11 UTC
Re: request.fresh? implementation does not follow HTTP specification
On Sun, Oct 19, 2008 at 3:51 PM, José Valim <jose.valim@gmail.com> wrote:> > According to the specification, if both Last-Modified date and Entity > Tags are sent, both must be validated to consider the request fresh: > > http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4I''m not sure you''re reading that paragraph correctly. It''s intended to supporting both http 1.0 and 1.1 caches, if we made the change you''re suggesting any 1.0 caches will ignore the etags and serve your old data. Which would defeat the purpose of its inclusion. I believe the current behaviour is valid, it uses both validators, merely shortcircuiting once it''s found a match.> def show > response.last_modified = resource.updated_at > response.etag = current_user > > if request.fresh?(response) > head :not_modified > else > # ... > end > end > > In the current implementation, even the first example given by Ryan > Daigle on his blog will fail:You should include the user id in the etag along with the other freshness information. -- Cheers Koz --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
DHH
2008-Oct-20 13:53 UTC
Re: request.fresh? implementation does not follow HTTP specification
> According to the specification, if both Last-Modified date and Entity > Tags are sent, both must be validated to consider the request fresh:You''re right. The relevant part from the spec is: "An HTTP/1.1 caching proxy, upon receiving a conditional request that includes both a Last- Modified date and one or more entity tags as cache validators, MUST NOT return a locally cached response to the client unless that cached response is consistent with all of the conditional header fields in the request." I''ll get it changed today. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
José Valim
2008-Oct-21 12:04 UTC
Re: request.fresh? implementation does not follow HTTP specification
DHH, Considering this, we also have to change automatic etagging. We should not automatically set etag (with body digest) when Last- Modified field is set, because to check this etag, we would have to render the action and this is what we want to avoid in first place. And the current implementation of conditional get (handler_conditional_get!) automatically replies 304 whenever the etag is matching (even if it''s not body digest). I believe this might be the right implementation: def handle_conditional_get! if etag? || last_modified? set_conditional_cache_control! elsif nonempty_ok_response? self.etag = body if request && request.etag_matches?(etag) self.status = ''304 Not Modified'' self.body = '''' end set_conditional_cache_control! end end I can create a patch if needed. Thanks, --- José Valim http://josevalim.blogspot.com/ http://www.pagestacker.com/ --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---
José Valim
2008-Oct-21 15:51 UTC
Re: request.fresh? implementation does not follow HTTP specification
I''m glad to help! =) I was checking those things because I''m writing some code that relies on such features. It uses classes methods to do HTTP Cache: class ListsController < ApplicationController http_cache :index, :show, :last_modified => :list, :etag => :current_user protected def list @list ||= List.find(params[:id]) end def current_user @current_user ||= User.find(params[:user_id]) end end It''s very lightweight (100 lines) and accepts also Procs, Methods and Symbols as parameters. If there is some interest in merging into Rails Core, I will be glad to help again. README and source code are here: http://github.com/josevalim/easy-http-cache/ Regards, -- José Valim http://josevalim.blogspot.com/ http://www.pagestacker.com/ On Oct 21, 12:59 pm, DHH <david.heineme...@gmail.com> wrote:> > Considering this, we also have to change automatic etagging. > > We should not automatically set etag (with body digest) when Last- > > Modified field is set, because to check this etag, we would have to > > render the action and this is what we want to avoid in first place. > > You''re right. I''ve added your implementation and tests for it. Thanks > for helping to sanity check all this.--~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group. To post to this group, send email to rubyonrails-core@googlegroups.com To unsubscribe from this group, send email to rubyonrails-core+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en -~----------~----~----~----~------~----~------~--~---