diff options
author | James Tucker <jftucker@gmail.com> | 2012-06-18 16:49:03 -0700 |
---|---|---|
committer | James Tucker <jftucker@gmail.com> | 2012-06-18 16:49:03 -0700 |
commit | 0b4e8c8d84fe4f66e85cf7cfeebe604e0f2849db (patch) | |
tree | 87bec311722d7dbdb436c5b15d152e6385e275c8 /actionpack | |
parent | 7fe0f27e2b97a2f175b31b97d14f92473688f66a (diff) | |
download | rails-0b4e8c8d84fe4f66e85cf7cfeebe604e0f2849db.tar.gz rails-0b4e8c8d84fe4f66e85cf7cfeebe604e0f2849db.tar.bz2 rails-0b4e8c8d84fe4f66e85cf7cfeebe604e0f2849db.zip |
Ensure that cache-control headers are merged
There are several aspects to this commit, that don't well fit into broken down
commits, so they are detailed here:
* When a user uses response.headers['Cache-Control'] = some_value, then the
documented convention in ConditionalGet is not adhered to, in this case,
response.cache_control is ignored due to `return if
self[CACHE_CONTROL].present?`
* When a middleware sets cache-control headers that would clobber, they're
converted to symbols directly, without underscores. This would lead to bugs.
* Items that would live in :extras if set through expires_in, are placed
directly in the @cache_control hash, and not respected in many cases
(somewhat adhering to the aforementioned documentation).
* Although quite useless, any directive named 'extras' would be ignored.
The general convention applied is that expires_* take precedence, but no longer
overwrite everything and expires_* are ALWAYS applied, even if the header is
set.
I am still unhappy about the contents of this commit, and the code in general.
Ideally it should be refactored to no longer use :extras. I'd likely recommend
expanding @cache_control into a class, and giving it the power to handle the
merge in a more efficient fashion. Such a commit would be a larger change that
could have additional semantic changes for other libraries unless they utilize
expires_in in very standard ways.
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/lib/action_dispatch/http/cache.rb | 42 | ||||
-rw-r--r-- | actionpack/test/controller/render_test.rb | 12 |
2 files changed, 44 insertions, 10 deletions
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index 5ee4c044ea..0b895e7860 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -84,17 +84,29 @@ module ActionDispatch LAST_MODIFIED = "Last-Modified".freeze ETAG = "ETag".freeze CACHE_CONTROL = "Cache-Control".freeze + SPESHUL_KEYS = %w[extras no-cache max-age public must-revalidate] + + def cache_control_headers + cache_control = {} + if cc = self[CACHE_CONTROL] + cc.delete(' ').split(',').each do |segment| + directive, argument = segment.split('=', 2) + case directive + when *SPESHUL_KEYS + key = directive.tr('-', '_') + cache_control[key.to_sym] = argument || true + else + cache_control[:extras] ||= [] + cache_control[:extras] << segment + end + end + end + cache_control + end def prepare_cache_control! - @cache_control = {} + @cache_control = cache_control_headers @etag = self[ETAG] - - if cache_control = self[CACHE_CONTROL] - cache_control.split(/,\s*/).each do |segment| - first, last = segment.split("=") - @cache_control[first.to_sym] = last || true - end - end end def handle_conditional_get! @@ -110,14 +122,24 @@ module ActionDispatch MUST_REVALIDATE = "must-revalidate".freeze def set_conditional_cache_control! - return if self[CACHE_CONTROL].present? + control = {} + cc_headers = cache_control_headers + if extras = cc_headers.delete(:extras) + @cache_control[:extras] ||= [] + @cache_control[:extras] += extras + @cache_control[:extras].uniq! + end - control = @cache_control + control.merge! cc_headers + control.merge! @cache_control if control.empty? headers[CACHE_CONTROL] = DEFAULT_CACHE_CONTROL elsif control[:no_cache] headers[CACHE_CONTROL] = NO_CACHE + if control[:extras] + headers[CACHE_CONTROL] += ", #{control[:extras].join(', ')}" + end else extras = control[:extras] max_age = control[:max_age] diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 3d58c02338..e9cfda45a9 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -116,6 +116,12 @@ class TestController < ActionController::Base render :action => 'hello_world' end + def conditional_hello_with_cache_control_headers + response.headers['Cache-Control'] = 'no-transform' + expires_now + render :action => 'hello_world' + end + def conditional_hello_with_bangs render :action => 'hello_world' end @@ -1512,6 +1518,12 @@ class ExpiresInRenderTest < ActionController::TestCase assert_equal "no-cache", @response.headers["Cache-Control"] end + def test_expires_now + get :conditional_hello_with_cache_control_headers + assert_match /no-cache/, @response.headers["Cache-Control"] + assert_match /no-transform/, @response.headers["Cache-Control"] + end + def test_date_header_when_expires_in time = Time.mktime(2011,10,30) Time.stubs(:now).returns(time) |