From 5059403b5661cac424afaf63335121126e25330a Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Mon, 27 Sep 2010 19:28:40 +0000 Subject: [PATCH] mod_cache: Make sure that we never allow a 304 Not Modified response that we asked for to leak to the client should the 304 response be uncacheable. PR45341 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1001884 13f79535-47bb-0310-9956-ffa450edef68 --- CHANGES | 4 +++ modules/cache/mod_cache.c | 56 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/CHANGES b/CHANGES index e19ba00d5d..9591b97d2a 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,10 @@ Changes with Apache 2.3.9 + *) mod_cache: Make sure that we never allow a 304 Not Modified response + that we asked for to leak to the client should the 304 response be + uncacheable. PR45341 [Graham Leggett] + *) mod_cache: Add the cache_status hook to register the final cache decision hit/miss/revalidate. Add optional support for an X-Cache and/or an X-Cache-Detail header to add the cache status to the diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c index 704968eced..1313306d06 100644 --- a/modules/cache/mod_cache.c +++ b/modules/cache/mod_cache.c @@ -904,6 +904,62 @@ static int cache_save_filter(ap_filter_t *f, apr_bucket_brigade *in) reason = "r->no_cache present"; } + /* Hold the phone. Some servers might allow us to cache a 2xx, but + * then make their 304 responses non cacheable. This leaves us in a + * sticky position. If the 304 is in answer to our own conditional + * request, we cannot send this 304 back to the client because the + * client isn't expecting it. Instead, our only option is to respect + * the answer to the question we asked (has it changed, answer was + * no) and return the cached item to the client, and then respect + * the uncacheable nature of this 304 by allowing the remove_url + * filter to kick in and remove the cached entity. + */ + if (reason && r->status == HTTP_NOT_MODIFIED && + cache->stale_handle) { + apr_bucket_brigade *bb; + apr_bucket *bkt; + int status; + + cache->handle = cache->stale_handle; + info = &cache->handle->cache_obj->info; + + /* Load in the saved status and clear the status line. */ + r->status = info->status; + r->status_line = NULL; + + bb = apr_brigade_create(r->pool, r->connection->bucket_alloc); + + r->headers_in = cache->stale_headers; + status = ap_meets_conditions(r); + if (status != OK) { + r->status = status; + + bkt = apr_bucket_flush_create(bb->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(bb, bkt); + } + else { + cache->provider->recall_body(cache->handle, r->pool, bb); + } + + cache->block_response = 1; + + /* we've got a cache conditional hit! tell anyone who cares */ + cache_run_cache_status( + cache->handle, + r, + AP_CACHE_REVALIDATE, + apr_psprintf( + r->pool, + "conditional cache hit: 304 was uncacheable though (%s); entity removed", + reason)); + + /* let someone else attempt to cache */ + cache_remove_lock(conf, cache, r, cache->handle ? + (char *)cache->handle->cache_obj->key : NULL, NULL); + + return ap_pass_brigade(f->next, bb); + } + if (reason) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "cache: %s not cached. Reason: %s", r->unparsed_uri, -- 2.40.0