summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGES4
-rw-r--r--include/ap_mmn.h1
-rw-r--r--modules/cache/cache_common.h1
-rw-r--r--modules/cache/cache_storage.c216
-rw-r--r--modules/cache/cache_util.c6
-rw-r--r--modules/cache/mod_cache.c145
-rw-r--r--modules/cache/mod_cache_disk.c12
-rw-r--r--modules/cache/mod_cache_socache.c5
8 files changed, 287 insertions, 103 deletions
diff --git a/CHANGES b/CHANGES
index 3a09209b88..9cccdc3247 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,10 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) mod_cache: Invalidate cached entities in response to RFC2616 Section
+ 13.10 Invalidation After Updates or Deletions. PR 15868 [Graham
+ Leggett]
+
*) mod_dav: mod_dav overrides dav_fs response on PUT failure. PR 35981
[Basant Kumar Kukreja <basant.kukreja sun.com>, Alejandro Alvarez
<alejandro.alvarez.ayllon cern.ch>]
diff --git a/include/ap_mmn.h b/include/ap_mmn.h
index 56203997d6..091ac57511 100644
--- a/include/ap_mmn.h
+++ b/include/ap_mmn.h
@@ -421,6 +421,7 @@
* 20121222.7 (2.5.0-dev) Add ap_remove_input|output_filter_byhandle()
* 20121222.8 (2.5.0-dev) Add dav_join_error()
* 20121222.9 (2.5.0-dev) Add conn_sense_e
+ * 20121222.10 (2.5.0-dev) Add cache_control_t.invalidated
*/
#define MODULE_MAGIC_COOKIE 0x41503235UL /* "AP25" */
diff --git a/modules/cache/cache_common.h b/modules/cache/cache_common.h
index cedce0767d..9d56d28b66 100644
--- a/modules/cache/cache_common.h
+++ b/modules/cache/cache_common.h
@@ -45,6 +45,7 @@ typedef struct cache_control {
unsigned int must_revalidate:1;
unsigned int proxy_revalidate:1;
unsigned int s_maxage:1;
+ unsigned int invalidated:1; /* has this entity been invalidated? */
apr_int64_t max_age_value; /* if positive, then set */
apr_int64_t max_stale_value; /* if positive, then set */
apr_int64_t min_fresh_value; /* if positive, then set */
diff --git a/modules/cache/cache_storage.c b/modules/cache/cache_storage.c
index 179c79e69c..a68d860a61 100644
--- a/modules/cache/cache_storage.c
+++ b/modules/cache/cache_storage.c
@@ -390,61 +390,15 @@ int cache_select(cache_request_rec *cache, request_rec *r)
return DECLINED;
}
-/*
- * invalidate a specific URL entity in all caches
- *
- * All cached entities for this URL are removed, usually in
- * response to a POST/PUT or DELETE.
- *
- * This function returns OK if at least one entity was found and
- * removed, and DECLINED if no cached entities were removed.
- */
-int cache_invalidate(cache_request_rec *cache, request_rec *r)
-{
- cache_provider_list *list;
- apr_status_t rv, status = DECLINED;
- cache_handle_t *h;
-
- if (!cache) {
- /* This should never happen */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00697)
- "cache: No cache request information available for key"
- " generation");
- return DECLINED;
- }
-
- if (!cache->key) {
- rv = cache_generate_key(r, r->pool, &cache->key);
- if (rv != APR_SUCCESS) {
- return DECLINED;
- }
- }
-
- /* go through the cache types */
- h = apr_palloc(r->pool, sizeof(cache_handle_t));
-
- list = cache->providers;
-
- while (list) {
- rv = list->provider->open_entity(h, r, cache->key);
- if (OK == rv) {
- list->provider->remove_url(h, r);
- status = OK;
- }
- list = list->next;
- }
-
- return status;
-}
-
-apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
- const char **key)
+apr_status_t cache_canonicalise_key(request_rec *r, apr_pool_t* p,
+ const char *uri, apr_uri_t *parsed_uri, const char **key)
{
cache_server_conf *conf;
char *port_str, *hn, *lcs;
const char *hostname, *scheme;
int i;
- char *path, *querystring;
+ const char *path;
+ char *querystring;
if (*key) {
/*
@@ -458,7 +412,7 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* option below.
*/
conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
- &cache_module);
+ &cache_module);
/*
* Use the canonical name to improve cache hit rate, but only if this is
@@ -484,15 +438,15 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
}
else {
/* Use _default_ as the hostname if none present, as in mod_vhost */
- hostname = ap_get_server_name(r);
+ hostname = ap_get_server_name(r);
if (!hostname) {
hostname = "_default_";
}
}
}
- else if(r->parsed_uri.hostname) {
+ else if (parsed_uri->hostname) {
/* Copy the parsed uri hostname */
- hn = apr_pstrdup(p, r->parsed_uri.hostname);
+ hn = apr_pstrdup(p, parsed_uri->hostname);
ap_str_tolower(hn);
/* const work-around */
hostname = hn;
@@ -511,9 +465,9 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* "no proxy request" and "reverse proxy request" are handled in the same
* manner (see above why this is needed).
*/
- if (r->proxyreq && r->parsed_uri.scheme) {
+ if (r->proxyreq && parsed_uri->scheme) {
/* Copy the scheme and lower-case it */
- lcs = apr_pstrdup(p, r->parsed_uri.scheme);
+ lcs = apr_pstrdup(p, parsed_uri->scheme);
ap_str_tolower(lcs);
/* const work-around */
scheme = lcs;
@@ -536,11 +490,11 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* server.
*/
if (r->proxyreq && (r->proxyreq != PROXYREQ_REVERSE)) {
- if (r->parsed_uri.port_str) {
- port_str = apr_pcalloc(p, strlen(r->parsed_uri.port_str) + 2);
+ if (parsed_uri->port_str) {
+ port_str = apr_pcalloc(p, strlen(parsed_uri->port_str) + 2);
port_str[0] = ':';
- for (i = 0; r->parsed_uri.port_str[i]; i++) {
- port_str[i + 1] = apr_tolower(r->parsed_uri.port_str[i]);
+ for (i = 0; parsed_uri->port_str[i]; i++) {
+ port_str[i + 1] = apr_tolower(parsed_uri->port_str[i]);
}
}
else if (apr_uri_port_of_scheme(scheme)) {
@@ -572,13 +526,13 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* Check if we need to ignore session identifiers in the URL and do so
* if needed.
*/
- path = r->uri;
- querystring = r->parsed_uri.query;
+ path = uri;
+ querystring = parsed_uri->query;
if (conf->ignore_session_id->nelts) {
int i;
char **identifier;
- identifier = (char **)conf->ignore_session_id->elts;
+ identifier = (char **) conf->ignore_session_id->elts;
for (i = 0; i < conf->ignore_session_id->nelts; i++, identifier++) {
int len;
char *param;
@@ -589,9 +543,9 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* of the path and that the parameter matches our identifier
*/
if ((param = strrchr(path, ';'))
- && !strncmp(param + 1, *identifier, len)
- && (*(param + len + 1) == '=')
- && !strchr(param + len + 2, '/')) {
+ && !strncmp(param + 1, *identifier, len)
+ && (*(param + len + 1) == '=')
+ && !strchr(param + len + 2, '/')) {
path = apr_pstrndup(p, path, param - path);
continue;
}
@@ -604,7 +558,7 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* querystring and followed by a '='
*/
if (!strncmp(querystring, *identifier, len)
- && (*(querystring + len) == '=')) {
+ && (*(querystring + len) == '=')) {
param = querystring;
}
else {
@@ -626,14 +580,15 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
if (querystring != param) {
querystring = apr_pstrndup(p, querystring,
- param - querystring);
+ param - querystring);
}
else {
querystring = "";
}
if ((amp = strchr(param + len + 1, '&'))) {
- querystring = apr_pstrcat(p, querystring, amp + 1, NULL);
+ querystring = apr_pstrcat(p, querystring, amp + 1,
+ NULL);
}
else {
/*
@@ -653,12 +608,12 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
/* Key format is a URI, optionally without the query-string */
if (conf->ignorequerystring) {
- *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
- path, "?", NULL);
+ *key = apr_pstrcat(p, scheme, "://", hostname, port_str, path, "?",
+ NULL);
}
else {
- *key = apr_pstrcat(p, scheme, "://", hostname, port_str,
- path, "?", querystring, NULL);
+ *key = apr_pstrcat(p, scheme, "://", hostname, port_str, path, "?",
+ querystring, NULL);
}
/*
@@ -669,9 +624,118 @@ apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
* resource in the cache under a key where it is never found by the quick
* handler during following requests.
*/
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00698)
- "cache: Key for entity %s?%s is %s", r->uri,
- r->parsed_uri.query, *key);
+ ap_log_rerror(
+ APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(00698) "cache: Key for entity %s?%s is %s", uri, parsed_uri->query, *key);
return APR_SUCCESS;
}
+
+apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
+ const char **key)
+{
+ return cache_canonicalise_key(r, p, r->uri, &r->parsed_uri, key);
+}
+
+/*
+ * Invalidate a specific URL entity in all caches
+ *
+ * All cached entities for this URL are removed, usually in
+ * response to a POST/PUT or DELETE.
+ *
+ * This function returns OK if at least one entity was found and
+ * removed, and DECLINED if no cached entities were removed.
+ */
+int cache_invalidate(cache_request_rec *cache, request_rec *r)
+{
+ cache_provider_list *list;
+ apr_status_t rv, status = DECLINED;
+ cache_handle_t *h;
+ apr_uri_t location_uri;
+ apr_uri_t content_location_uri;
+
+ const char *location, *location_key = NULL;
+ const char *content_location, *content_location_key = NULL;
+
+ if (!cache) {
+ /* This should never happen */
+ ap_log_rerror(
+ APLOG_MARK, APLOG_ERR, APR_EGENERAL, r, APLOGNO(00697) "cache: No cache request information available for key"
+ " generation");
+ return DECLINED;
+ }
+
+ if (!cache->key) {
+ rv = cache_generate_key(r, r->pool, &cache->key);
+ if (rv != APR_SUCCESS) {
+ return DECLINED;
+ }
+ }
+
+ location = apr_table_get(r->headers_out, "Location");
+ if (location) {
+ if (APR_SUCCESS != apr_uri_parse(r->pool, location, &location_uri)
+ || APR_SUCCESS
+ != cache_canonicalise_key(r, r->pool, location,
+ &location_uri, &location_key)
+ || strcmp(r->parsed_uri.hostname, location_uri.hostname)) {
+ location_key = NULL;
+ }
+ }
+
+ content_location = apr_table_get(r->headers_out, "Content-Location");
+ if (content_location) {
+ if (APR_SUCCESS
+ != apr_uri_parse(r->pool, content_location,
+ &content_location_uri)
+ || APR_SUCCESS
+ != cache_canonicalise_key(r, r->pool, content_location,
+ &content_location_uri, &content_location_key)
+ || strcmp(r->parsed_uri.hostname,
+ content_location_uri.hostname)) {
+ content_location_key = NULL;
+ }
+ }
+
+ /* go through the cache types */
+ h = apr_palloc(r->pool, sizeof(cache_handle_t));
+
+ list = cache->providers;
+
+ while (list) {
+
+ /* invalidate the request uri */
+ rv = list->provider->open_entity(h, r, cache->key);
+ if (OK == rv) {
+ rv = list->provider->invalidate_entity(h, r);
+ status = OK;
+ }
+ ap_log_rerror(
+ APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO() "cache: Attempted to invalidate cached entity with key: %s", cache->key);
+
+ /* invalidate the Location */
+ if (location_key) {
+ rv = list->provider->open_entity(h, r, location_key);
+ if (OK == rv) {
+ rv = list->provider->invalidate_entity(h, r);
+ status = OK;
+ }
+ ap_log_rerror(
+ APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO() "cache: Attempted to invalidate cached entity with key: %s", location_key);
+ }
+
+ /* invalidate the Content-Location */
+ if (content_location_key) {
+ rv = list->provider->open_entity(h, r, content_location_key);
+ if (OK == rv) {
+ rv = list->provider->invalidate_entity(h, r);
+ status = OK;
+ }
+ ap_log_rerror(
+ APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO() "cache: Attempted to invalidate cached entity with key: %s", content_location_key);
+ }
+
+ list = list->next;
+ }
+
+ return status;
+}
diff --git a/modules/cache/cache_util.c b/modules/cache/cache_util.c
index 1e5098d537..cdabe8ff82 100644
--- a/modules/cache/cache_util.c
+++ b/modules/cache/cache_util.c
@@ -544,11 +544,13 @@ int cache_check_freshness(cache_handle_t *h, cache_request_rec *cache,
/* These come from the cached entity. */
if (h->cache_obj->info.control.no_cache
|| h->cache_obj->info.control.no_cache_header
- || h->cache_obj->info.control.private_header) {
+ || h->cache_obj->info.control.private_header
+ || h->cache_obj->info.control.invalidated) {
/*
* The cached entity contained Cache-Control: no-cache, or a
* no-cache with a header present, or a private with a header
- * present, so treat as stale causing revalidation.
+ * present, or the cached entity has been invalidated in the
+ * past, so treat as stale causing revalidation.
*/
return 0;
}
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;
+ }
}
/*
@@ -1514,6 +1544,70 @@ static apr_status_t cache_remove_url_filter(ap_filter_t *f,
}
/*
+ * 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);
}
diff --git a/modules/cache/mod_cache_disk.c b/modules/cache/mod_cache_disk.c
index 919613ca51..7f116b838a 100644
--- a/modules/cache/mod_cache_disk.c
+++ b/modules/cache/mod_cache_disk.c
@@ -1344,7 +1344,17 @@ static apr_status_t commit_entity(cache_handle_t *h, request_rec *r)
static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
{
- return APR_ENOTIMPL;
+ apr_status_t rv;
+
+ rv = recall_headers(h, r);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* mark the entity as invalidated */
+ h->cache_obj->info.control.invalidated = 1;
+
+ return commit_entity(h, r);
}
static void *create_dir_config(apr_pool_t *p, char *dummy)
diff --git a/modules/cache/mod_cache_socache.c b/modules/cache/mod_cache_socache.c
index 9632db123e..fb3fea948a 100644
--- a/modules/cache/mod_cache_socache.c
+++ b/modules/cache/mod_cache_socache.c
@@ -1182,7 +1182,10 @@ fail:
static apr_status_t invalidate_entity(cache_handle_t *h, request_rec *r)
{
- return APR_ENOTIMPL;
+ /* mark the entity as invalidated */
+ h->cache_obj->info.control.invalidated = 1;
+
+ return commit_entity(h, r);
}
static void *create_dir_config(apr_pool_t *p, char *dummy)