diff --git a/lib/rack/cache/context.rb b/lib/rack/cache/context.rb index 583f4bf..bf81950 100644 --- a/lib/rack/cache/context.rb +++ b/lib/rack/cache/context.rb @@ -286,7 +286,7 @@ def fetch # Write the response to the cache. def store(response) strip_ignore_headers(response) - metastore.store(@request, response, entitystore) + metastore.store(@request, response, entitystore, non_cached_headers) response.headers['age'] = response.age.to_s rescue => e log_error(e) diff --git a/lib/rack/cache/meta_store.rb b/lib/rack/cache/meta_store.rb index 7fc63a9..6637c65 100644 --- a/lib/rack/cache/meta_store.rb +++ b/lib/rack/cache/meta_store.rb @@ -58,7 +58,7 @@ def lookup(request, entity_store) # Write a cache entry to the store under the given key. Existing # entries are read and any that match the response are removed. # This method calls #write with the new list of cache entries. - def store(request, response, entity_store) + def store(request, response, entity_store, non_cached_headers = []) key = cache_key(request) stored_env = persist_request(request) @@ -97,6 +97,7 @@ def store(request, response, entity_store) headers = persist_response(response) headers.delete('age') + non_cached_headers.each{ |h| headers.delete(h) } entries.unshift [stored_env, headers] if request.env['rack-cache.use_native_ttl'] && response.fresh? diff --git a/lib/rack/cache/options.rb b/lib/rack/cache/options.rb index d2d5112..b6680a8 100644 --- a/lib/rack/cache/options.rb +++ b/lib/rack/cache/options.rb @@ -93,6 +93,12 @@ def option_name(key) # Default: [] option_accessor :transfer_headers + # Set of response headers that will be passed to the client when present + # in any responses but will not be stored in cached entries. + # + # Default: [] + option_accessor :non_cached_headers + # Set of request headers that trigger "private" cache-control behavior # on responses that don't explicitly state whether the response is # public or private via a cache-control directive. Applications that use @@ -149,19 +155,20 @@ def set(option, value=self, &block) private def initialize_options(options={}) @default_options = { - 'rack-cache.cache_key' => Key, - 'rack-cache.verbose' => true, - 'rack-cache.storage' => Rack::Cache::Storage.instance, - 'rack-cache.metastore' => 'heap:/', - 'rack-cache.entitystore' => 'heap:/', - 'rack-cache.default_ttl' => 0, - 'rack-cache.ignore_headers' => ['set-cookie'], - 'rack-cache.transfer_headers' => [], - 'rack-cache.private_headers' => ['Authorization', 'Cookie'], - 'rack-cache.allow_reload' => false, - 'rack-cache.allow_revalidate' => false, - 'rack-cache.use_native_ttl' => false, - 'rack-cache.fault_tolerant' => false, + 'rack-cache.cache_key' => Key, + 'rack-cache.verbose' => true, + 'rack-cache.storage' => Rack::Cache::Storage.instance, + 'rack-cache.metastore' => 'heap:/', + 'rack-cache.entitystore' => 'heap:/', + 'rack-cache.default_ttl' => 0, + 'rack-cache.ignore_headers' => ['set-cookie'], + 'rack-cache.transfer_headers' => [], + 'rack-cache.non_cached_headers' => [], + 'rack-cache.private_headers' => ['Authorization', 'Cookie'], + 'rack-cache.allow_reload' => false, + 'rack-cache.allow_revalidate' => false, + 'rack-cache.use_native_ttl' => false, + 'rack-cache.fault_tolerant' => false, } self.options = options end diff --git a/test/context_test.rb b/test/context_test.rb index 15470d9..1444055 100644 --- a/test/context_test.rb +++ b/test/context_test.rb @@ -800,6 +800,34 @@ cache.trace.must_include :valid end + it 'excludes specified headers from being cached in responses' do + count = 0 + respond_with do |req,res| + count += 1 + res['ETAG'] = '"12345"' + if count == 1 + res['not-cached'] = '"1"' + end + res.status = (count == 1) ? 200 : 304 + end + + get '/', 'rack-cache.non_cached_headers' => ['not-cached'] + assert app.called? + assert response.ok? + response.headers.must_include 'not-cached' + cache.trace.must_include :miss + cache.trace.must_include :store + cache.trace.wont_include :ignore + + get '/', 'rack-cache.non_cached_headers' => ['not-cached'] + assert app.called? + assert response.ok? + response.headers.wont_include 'not-cached' + cache.trace.must_include :stale + cache.trace.must_include :valid + cache.trace.must_include :store + end + it 'replaces cached responses when validation results in non-304 response' do timestamp = Time.now.httpdate count = 0