aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/base.rb70
-rwxr-xr-xactionpack/lib/action_controller/request.rb14
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.