Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom options for transferable headers and non-cached headers #16

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/rack/cache/context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ def validate(entry)

entry = entry.dup
entry.headers.delete('date')
%w[Date expires cache-control etag last-modified].each do |name|
%w[Date expires cache-control etag last-modified].concat(transfer_headers).each do |name|
next unless value = response.headers[name]
entry.headers[name] = value
end
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion lib/rack/cache/meta_store.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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?
Expand Down
39 changes: 27 additions & 12 deletions lib/rack/cache/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,19 @@ def option_name(key)
# Default: ['set-cookie']
option_accessor :ignore_headers

# Set of response headers that are transferred from the backend response
# onto the cache entry when validating a cached entity (after receiving a
# 304 response from the backend) before sending it to the client.
#
# 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
Expand Down Expand Up @@ -142,18 +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.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
Expand Down
52 changes: 52 additions & 0 deletions test/context_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -776,6 +776,58 @@
cache.trace.wont_include :miss
end

it 'includes specified additional headers from the backend in responses to the client' do
count = 0
respond_with do |req,res|
count += 1
res['ETAG'] = '"12345"'
res['unique-snowflake'] = '"so-special"'
res.status = (count == 1) ? 200 : 304
end

get '/', 'rack-cache.transfer_headers' => ['unique-snowflake']
assert app.called?
assert response.ok?
response.headers.must_include 'unique-snowflake'
response['unique-snowflake'].must_equal '"so-special"'
cache.trace.must_include :miss

get '/', 'rack-cache.transfer_headers' => ['unique-snowflake']
assert app.called?
assert response.ok?
response.headers.must_include 'unique-snowflake'
response['unique-snowflake'].must_equal '"so-special"'
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
Expand Down
Loading