diff options
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r-- | actionpack/lib/action_controller/base.rb | 70 | ||||
-rwxr-xr-x | actionpack/lib/action_controller/request.rb | 14 |
2 files changed, 65 insertions, 19 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 3ede681253..4c5c5ac597 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -965,22 +965,6 @@ module ActionController #:nodoc: render :nothing => true, :status => status end - # Sets the Last-Modified response header. Returns 304 Not Modified if the - # If-Modified-Since request header is <= last modified. - def last_modified!(utc_time) - response.last_modified= utc_time - if request.if_modified_since && request.if_modified_since <= utc_time - head(:not_modified) - end - end - - # Sets the ETag response header. Returns 304 Not Modified if the - # If-None-Match request header matches. - def etag!(etag) - response.etag = etag - head(:not_modified) if response.etag == request.if_none_match - end - # Clears the rendered results, allowing for another render to be performed. def erase_render_results #:nodoc: response.body = nil @@ -1090,6 +1074,54 @@ module ActionController #:nodoc: @performed_redirect = true end + # Sets the etag and/or last_modified on the response and checks it against + # the client request. If the request doesn't match the options provided, the + # request is considered stale and should be generated from scratch. Otherwise, + # it's fresh and we don't need to generate anything and can rely on the default + # reply of "304 Not Modified". + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # + # if stale?(:etag => @article, :last_modified => @article.created_at.utc) + # @statistics = @article.really_expensive_call + # respond_to do |format| + # # all the supported formats + # end + # end + # end + def stale?(options) + fresh_when(options) + !request.fresh?(response) + end + + # The opposite of stale? provided for parity when that feels more natural. + def fresh?(options) + !stale?(options) + end + + # Sets the etag, last_modified, or both such that the request can be short-circuited + # with a "304 Not Modified" response instead of rendering a template when the request + # is already fresh. + # + # Example: + # + # def show + # @article = Article.find(params[:id]) + # fresh_when(:etag => @article, :last_modified => @article.created_at.utc) + # end + # + # This will render the show template if the request isn't sending a matching etag or + # If-Modified-Since header and just a "304 Not Modified" response if there's a match. + def fresh_when(options) + options.assert_valid_keys(:etag, :last_modified) + + response.etag = options[:etag] if options[:etag] + response.last_modified = options[:last_modified] if options[:last_modified] + end + # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that # intermediate caches shouldn't cache the response. # @@ -1176,7 +1208,11 @@ module ActionController #:nodoc: end def default_render #:nodoc: - render + if request.fresh?(response) + head :not_modified + else + render + end end def perform_action diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 5e492e3ee1..9f33cbc55f 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -120,9 +120,19 @@ module ActionController end # Check response freshness (Last-Modified and ETag) against request - # If-Modified-Since and If-None-Match conditions. + # If-Modified-Since and If-None-Match conditions. If both headers are + # supplied, both must match, or the request is not considered fresh. def fresh?(response) - not_modified?(response.last_modified) || etag_matches?(response.etag) + case + when if_modified_since && if_none_match + not_modified?(response.last_modified) && etag_matches?(response.etag) + when if_modified_since + not_modified?(response.last_modified) + when if_none_match + etag_matches?(response.etag) + else + false + end end # Returns the Mime type for the \format used in the request. |