]> granicus.if.org Git - apache/commitdiff
mod_cache: Invalidate cached entities in response to RFC2616 Section
authorGraham Leggett <minfrin@apache.org>
Tue, 28 May 2013 20:30:04 +0000 (20:30 +0000)
committerGraham Leggett <minfrin@apache.org>
Tue, 28 May 2013 20:30:04 +0000 (20:30 +0000)
13.10 Invalidation After Updates or Deletions. PR 15868

trunk patch: http://svn.apache.org/r1070179
             http://svn.apache.org/r1478140
             http://svn.apache.org/r1478173
             http://svn.apache.org/r1478798
2.4.x patch: http://people.apache.org/~minfrin/httpd-mod_cache-invalidate7.patch

Submitted by: minfrin
Reviewed by: jim, wrowe

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1487102 13f79535-47bb-0310-9956-ffa450edef68

CHANGES
STATUS
include/ap_mmn.h
modules/cache/cache_common.h
modules/cache/cache_storage.c
modules/cache/cache_storage.h
modules/cache/cache_util.c
modules/cache/mod_cache.c
modules/cache/mod_cache_disk.c
modules/cache/mod_cache_socache.c

diff --git a/CHANGES b/CHANGES
index a9ed1ec32d149a2eafa7582426424e20989eefdc..8fd76ed71e593f76cb6744646145cb11c305aca7 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,10 @@
 
 Changes with Apache 2.4.5
 
+  *) mod_cache: Invalidate cached entities in response to RFC2616 Section
+     13.10 Invalidation After Updates or Deletions. PR 15868 [Graham
+     Leggett]
+
   *) mod_dav: Improve error handling in dav_method_put(), add new
      dav_join_error() function.  PR 54145.  [Ben Reser <ben reser.org>]
 
diff --git a/STATUS b/STATUS
index 88c67b6198026d2eeab7cbdfbc9f99d6addc934a..8048fdffe537e11e5c3659999c72a40a50b9b251 100644 (file)
--- a/STATUS
+++ b/STATUS
@@ -90,15 +90,6 @@ RELEASE SHOWSTOPPERS:
 PATCHES ACCEPTED TO BACKPORT FROM TRUNK:
   [ start all new proposals below, under PATCHES PROPOSED. ]
  
-    * mod_cache: Invalidate cached entities in response to RFC2616 Section
-      13.10 Invalidation After Updates or Deletions. PR 15868
-      trunk patch: http://svn.apache.org/r1070179
-                   http://svn.apache.org/r1478140
-                   http://svn.apache.org/r1478173
-                   http://svn.apache.org/r1478798
-      2.4.x patch: http://people.apache.org/~minfrin/httpd-mod_cache-invalidate7.patch
-      +1: minfrin, jim, wrowe
-
     * mod_cache: Ignore response headers specified by no-cache=header and
       private=header as specified by RFC2616 14.9.1 What is Cacheable. Ensure
       that these headers are still processed when multiple Cache-Control
index 7087fba9a2c84ee9469818ba9c1da27cb5c01d0d..6d3ce534694648b8ea0b8044b753c9466ab3f86a 100644 (file)
  * 20120211.13 (2.4.5-dev) Add ap_get_exec_line
  * 20120211.14 (2.4.5-dev) Add ppinherit and inherit to proxy_server_conf
  * 20120211.15 (2.4.5-dev) Add dav_join_error()
+ * 20120211.16 (2.4.5-dev) Add cache_control_t.invalidated
  */
 
 #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
 #ifndef MODULE_MAGIC_NUMBER_MAJOR
 #define MODULE_MAGIC_NUMBER_MAJOR 20120211
 #endif
-#define MODULE_MAGIC_NUMBER_MINOR 15                   /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 16                   /* 0...n */
 
 /**
  * Determine if the server's current MODULE_MAGIC_NUMBER is at least a
index cedce0767d2a6ec743a3ddcc5f85dcce00ed3331..9d56d28b66b24224a02fe9779eb62aecc9deb502 100644 (file)
@@ -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 */
index dc51ca5b4d76ec76a27b5966fe70b2b04259af2c..1267c5da0ea335e56cdb50a0a2af0788130d34c3 100644 (file)
@@ -390,14 +390,15 @@ int cache_select(cache_request_rec *cache, request_rec *r)
     return DECLINED;
 }
 
-apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
-        const char **key)
+static 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) {
         /*
@@ -411,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
@@ -437,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;
@@ -464,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;
@@ -489,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)) {
@@ -525,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;
@@ -542,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;
             }
@@ -557,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 {
@@ -579,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 {
                         /*
@@ -606,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);
     }
 
     /*
@@ -622,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(02468) "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(02469) "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(02470) "cache: Attempted to invalidate cached entity with key: %s", content_location_key);
+        }
+
+        list = list->next;
+    }
+
+    return status;
+}
index 2b67970e1ef5fd4f89dbc013925f02a451ea3be1..90445f06551020d0477578cc47d6a255d5eae03b 100644 (file)
@@ -40,6 +40,20 @@ int cache_remove_url(cache_request_rec *cache, request_rec *r);
 int cache_create_entity(cache_request_rec *cache, request_rec *r,
                         apr_off_t size, apr_bucket_brigade *in);
 int cache_select(cache_request_rec *cache, request_rec *r);
+
+/**
+ * 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.
+ * @param h cache_handle_t
+ * @param r request_rec
+ */
+int cache_invalidate(cache_request_rec *cache, request_rec *r);
+
 apr_status_t cache_generate_key_default(request_rec *r, apr_pool_t* p,
         const char **key);
 
index 1e5098d5374292b2c111a4c2281cfb26b487d529..cdabe8ff820c91b671bc7fba242cec59d73ac986 100644 (file)
@@ -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;
     }
index b1bb65abd03be777bfa2111155c9c97fd6fc57ce..6a0ca007852e49730a91a6a64590f840924c7d16 100644 (file)
@@ -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
@@ -75,11 +76,6 @@ static int cache_quick_handler(request_rec *r, int lookup)
     ap_filter_rec_t *cache_out_handle;
     cache_server_conf *conf;
 
-    /* Delay initialization until we know we are handling a GET */
-    if (r->method_number != M_GET) {
-        return DECLINED;
-    }
-
     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
                                                       &cache_module);
 
@@ -117,6 +113,40 @@ static int cache_quick_handler(request_rec *r, int lookup)
         return DECLINED;
     }
 
+    /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities.
+     */
+    switch (r->method_number) {
+    case M_PUT:
+    case M_POST:
+    case M_DELETE:
+    {
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02461)
+                "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s",
+                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);
+
+        return DECLINED;
+    }
+    case M_GET: {
+        break;
+    }
+    default : {
+
+        ap_log_rerror(
+                APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02462) "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri);
+
+        return DECLINED;
+    }
+    }
+
     /*
      * Try to serve this request from the cache.
      *
@@ -176,9 +206,10 @@ static int cache_quick_handler(request_rec *r, int lookup)
                      * is available later during running the filter may be
                      * different due to an internal redirect.
                      */
-                    cache->remove_url_filter =
-                        ap_add_output_filter_handle(cache_remove_url_filter_handle,
-                                cache, r, r->connection);
+                    cache->remove_url_filter = ap_add_output_filter_handle(
+                            cache_remove_url_filter_handle, cache, r,
+                            r->connection);
+
                 }
                 else {
                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv,
@@ -347,11 +378,6 @@ static int cache_handler(request_rec *r)
     ap_filter_rec_t *cache_save_handle;
     cache_server_conf *conf;
 
-    /* Delay initialization until we know we are handling a GET */
-    if (r->method_number != M_GET) {
-        return DECLINED;
-    }
-
     conf = (cache_server_conf *) ap_get_module_config(r->server->module_config,
                                                       &cache_module);
 
@@ -375,6 +401,40 @@ static int cache_handler(request_rec *r)
     /* save away the possible providers */
     cache->providers = providers;
 
+    /* Are we PUT/POST/DELETE? If so, prepare to invalidate the cached entities.
+     */
+    switch (r->method_number) {
+    case M_PUT:
+    case M_POST:
+    case M_DELETE:
+    {
+
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02463)
+                "PUT/POST/DELETE: Adding CACHE_INVALIDATE filter for %s",
+                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);
+
+        return DECLINED;
+    }
+    case M_GET: {
+        break;
+    }
+    default : {
+
+        ap_log_rerror(
+                APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r, APLOGNO(02464) "cache: Method '%s' not cacheable by mod_cache, ignoring: %s", r->method, r->uri);
+
+        return DECLINED;
+    }
+    }
+
     /*
      * Try to serve this request from the cache.
      *
@@ -455,9 +515,10 @@ static int cache_handler(request_rec *r)
                  * is available later during running the filter may be
                  * different due to an internal redirect.
                  */
-                cache->remove_url_filter =
-                    ap_add_output_filter_handle(cache_remove_url_filter_handle,
-                            cache, r, r->connection);
+                cache->remove_url_filter
+                        = ap_add_output_filter_handle(
+                                cache_remove_url_filter_handle, cache, r,
+                                r->connection);
 
             }
             else {
@@ -1482,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(02465)
+                "cache: CACHE_INVALIDATE enabled unexpectedly: %s", r->uri);
+    }
+    else {
+
+        if (r->status > 299) {
+
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02466)
+                    "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(02467)
+                    "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
  * ------------
@@ -1579,11 +1704,11 @@ static int cache_status(cache_handle_t *h, request_rec *r,
         x_cache = conf->x_cache;
     }
     if (x_cache) {
-        apr_table_setn(headers, "X-Cache",
-                apr_psprintf(r->pool, "%s from %s",
-                        status == AP_CACHE_HIT ? "HIT" : status
-                                == AP_CACHE_REVALIDATE ? "REVALIDATE" : "MISS",
-                        r->server->server_hostname));
+        apr_table_setn(headers, "X-Cache", apr_psprintf(r->pool, "%s from %s",
+                status == AP_CACHE_HIT ? "HIT"
+                        : status == AP_CACHE_REVALIDATE ? "REVALIDATE" : status
+                                == AP_CACHE_INVALIDATE ? "INVALIDATE" : "MISS",
+                r->server->server_hostname));
     }
 
     if (dconf && dconf->x_cache_detail_set) {
@@ -2455,6 +2580,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);
 }
 
index 670e4b8d0ceb89315332ed8b6f9199f0c4cd4f09..b0d86fe1ffd2efde74fb6392f82cc5ed9662d8c3 100644 (file)
@@ -1342,7 +1342,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)
index 9632db123e45ffb9b0cffd4422f10ae79da364b2..fb3fea948aad073224ea52130d6e3a0ebfb9aad5 100644 (file)
@@ -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)