From 6e77e5b20f77caf50cf2ddf6a69fea583c4a03f5 Mon Sep 17 00:00:00 2001 From: Graham Leggett Date: Wed, 1 May 2013 18:49:04 +0000 Subject: mod_cache: Invalidate cached entities in response to RFC2616 Section 13.10 Invalidation After Updates or Deletions. PR 15868 Resolves outstanding issue with r1070179 as per http://www.gossamer-threads.com/lists/apache/dev/395830?do=post_view_threaded#395830 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1478140 13f79535-47bb-0310-9956-ffa450edef68 --- modules/cache/mod_cache.c | 145 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 122 insertions(+), 23 deletions(-) (limited to 'modules/cache/mod_cache.c') diff --git a/modules/cache/mod_cache.c b/modules/cache/mod_cache.c index 173602fb56..3e29340d4f 100644 --- a/modules/cache/mod_cache.c +++ b/modules/cache/mod_cache.c @@ -34,6 +34,7 @@ static ap_filter_rec_t *cache_save_subreq_filter_handle; static ap_filter_rec_t *cache_out_filter_handle; static ap_filter_rec_t *cache_out_subreq_filter_handle; static ap_filter_rec_t *cache_remove_url_filter_handle; +static ap_filter_rec_t *cache_invalidate_filter_handle; /* * CACHE handler @@ -112,24 +113,39 @@ static int cache_quick_handler(request_rec *r, int lookup) return DECLINED; } - /* Are we something other than GET or HEAD? If so, invalidate - * the cached entities. + /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities. */ - if (r->method_number != M_GET) { + switch (r->method_number) { + case M_PUT: + case M_POST: + case M_DELETE: + { + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO() + "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s", + r->uri); - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00748) - "Invalidating all cached entities in response to '%s' request for %s", - r->method, r->uri); + /* Add cache_invalidate filter to this request to force a + * cache entry to be invalidated if the response is + * ultimately successful (2xx). + */ + ap_add_output_filter_handle( + cache_invalidate_filter_handle, cache, r, + r->connection); - cache_invalidate(cache, r); + return DECLINED; + } + case M_GET: { + break; + } + default : { - /* we've got a cache invalidate! tell everyone who cares */ - cache_run_cache_status(cache->handle, r, r->headers_out, - AP_CACHE_INVALIDATE, apr_psprintf(r->pool, - "cache invalidated by %s", r->method)); + ap_log_rerror( + APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO() "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri); return DECLINED; } + } /* * Try to serve this request from the cache. @@ -385,24 +401,38 @@ static int cache_handler(request_rec *r) /* save away the possible providers */ cache->providers = providers; - /* Are we something other than GET or HEAD? If so, invalidate - * the cached entities. + /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities. */ - if (r->method_number != M_GET) { - - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00755) - "Invalidating all cached entities in response to '%s' request for %s", - r->method, r->uri); + switch (r->method_number) { + case M_PUT: + case M_POST: + case M_DELETE: + { - cache_invalidate(cache, r); + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO() + "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s", + r->uri); - /* we've got a cache invalidate! tell everyone who cares */ - cache_run_cache_status(cache->handle, r, r->headers_out, - AP_CACHE_INVALIDATE, apr_psprintf(r->pool, - "cache invalidated by %s", r->method)); + /* Add cache_invalidate filter to this request to force a + * cache entry to be invalidated if the response is + * ultimately successful (2xx). + */ + ap_add_output_filter_handle( + cache_invalidate_filter_handle, cache, r, + r->connection); return DECLINED; + } + case M_GET: { + break; + } + default : { + ap_log_rerror( + APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO() "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri); + + return DECLINED; + } } /* @@ -1513,6 +1543,70 @@ static apr_status_t cache_remove_url_filter(ap_filter_t *f, return ap_pass_brigade(f->next, in); } +/* + * CACHE_INVALIDATE filter + * ----------------------- + * + * This filter gets added in the quick handler should a PUT, POST or DELETE + * method be detected. If the response is successful, we must invalidate any + * cached entity as per RFC2616 section 13.10. + * + * CACHE_INVALIDATE has to be a protocol filter to ensure that is run even if + * the response is a canned error message, which removes the content filters + * from the chain. + * + * CACHE_INVALIDATE expects cache request rec within its context because the + * request this filter runs on can be different from the one whose cache entry + * should be removed, due to internal redirects. + */ +static apr_status_t cache_invalidate_filter(ap_filter_t *f, + apr_bucket_brigade *in) +{ + request_rec *r = f->r; + cache_request_rec *cache; + + /* Setup cache_request_rec */ + cache = (cache_request_rec *) f->ctx; + + if (!cache) { + /* user likely configured CACHE_INVALIDATE manually; they should really + * use mod_cache configuration to do that. So: + * 1. Remove ourselves + * 2. Do nothing and bail out + */ + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() + "cache: CACHE_INVALIDATE enabled unexpectedly: %s", r->uri); + } + else { + + if (r->status > 299) { + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() + "cache: response status to '%s' method is %d (>299), not invalidating cached entity: %s", r->method, r->status, r->uri); + + } + else { + + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO() + "cache: Invalidating all cached entities in response to '%s' request for %s", + r->method, r->uri); + + cache_invalidate(cache, r); + + /* we've got a cache invalidate! tell everyone who cares */ + cache_run_cache_status(cache->handle, r, r->headers_out, + AP_CACHE_INVALIDATE, apr_psprintf(r->pool, + "cache invalidated by %s", r->method)); + + } + + } + + /* remove ourselves */ + ap_remove_output_filter(f); + return ap_pass_brigade(f->next, in); +} + /* * CACHE filter * ------------ @@ -2481,6 +2575,11 @@ static void register_hooks(apr_pool_t *p) cache_remove_url_filter, NULL, AP_FTYPE_PROTOCOL); + cache_invalidate_filter_handle = + ap_register_output_filter("CACHE_INVALIDATE", + cache_invalidate_filter, + NULL, + AP_FTYPE_PROTOCOL); ap_hook_post_config(cache_post_config, NULL, NULL, APR_HOOK_REALLY_FIRST); } -- cgit v1.2.3