You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by mi...@apache.org on 2010/09/27 11:20:41 UTC
svn commit: r1001639 - in /httpd/httpd/trunk: CHANGES docs/manual/env.xml
docs/manual/mod/mod_cache.xml include/ap_mmn.h modules/cache/cache_util.h
modules/cache/mod_cache.c modules/cache/mod_cache.h
Author: minfrin
Date: Mon Sep 27 09:20:40 2010
New Revision: 1001639
URL: http://svn.apache.org/viewvc?rev=1001639&view=rev
Log:
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
response. PR48241
Modified:
httpd/httpd/trunk/CHANGES
httpd/httpd/trunk/docs/manual/env.xml
httpd/httpd/trunk/docs/manual/mod/mod_cache.xml
httpd/httpd/trunk/include/ap_mmn.h
httpd/httpd/trunk/modules/cache/cache_util.h
httpd/httpd/trunk/modules/cache/mod_cache.c
httpd/httpd/trunk/modules/cache/mod_cache.h
Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Mon Sep 27 09:20:40 2010
@@ -2,6 +2,11 @@
Changes with Apache 2.3.9
+ *) 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
+ response. PR48241 [Graham Leggett]
+
*) mod_authz_host: Add 'local' provider that matches connections originating
on the local host. PR 19938. [Stefan Fritsch]
Modified: httpd/httpd/trunk/docs/manual/env.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/env.xml?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/env.xml (original)
+++ httpd/httpd/trunk/docs/manual/env.xml Mon Sep 27 09:20:40 2010
@@ -59,6 +59,7 @@
<title>Setting Environment Variables</title>
<related>
<modulelist>
+ <module>mod_cache</module>
<module>mod_env</module>
<module>mod_rewrite</module>
<module>mod_setenvif</module>
Modified: httpd/httpd/trunk/docs/manual/mod/mod_cache.xml
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/docs/manual/mod/mod_cache.xml?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/docs/manual/mod/mod_cache.xml (original)
+++ httpd/httpd/trunk/docs/manual/mod/mod_cache.xml Mon Sep 27 09:20:40 2010
@@ -226,6 +226,39 @@
</section>
+<section id="status"><title>Cache Status and Logging</title>
+ <p>Once <module>mod_cache</module> has made a decision as to whether or not
+ an entity is to be served from cache, the detailed reason for the decision
+ is written to the subprocess environment within the request under the
+ <strong>cache-status</strong> key. This reason can be logged by the
+ <directive module="mod_log_config">LogFormat</directive> directive as
+ follows:</p>
+
+ <example>
+ LogFormat "%{cache-status}e ..."
+ </example>
+
+ <p>Based on the caching decision made, the reason is also written to the
+ subprocess environment under one the following three keys, as appropriate:</p>
+
+ <dl>
+ <dt>cache-hit</dt><dd>The response was served from cache.</dd>
+ <dt>cache-revalidate</dt><dd>The response was stale and was successfully
+ revalidated, then served from cache.</dd>
+ <dt>cache-miss</dt><dd>The response was served from the upstream server.</dd>
+ </dl>
+
+ <p>This makes it possible to support conditional logging of cached requests
+ as per the following example:</p>
+
+ <example>
+ CustomLog cached-requests.log common env=cache-hit<br />
+ CustomLog uncached-requests.log common env=cache-miss<br />
+ CustomLog revalidated-requests.log common env=cache-revalidate<br />
+ </example>
+
+</section>
+
<directivesynopsis>
<name>CacheEnable</name>
<description>Enable caching of specified URLs using a specified storage
@@ -777,5 +810,87 @@ LastModified date.</description>
</usage>
</directivesynopsis>
+
+<directivesynopsis>
+<name>CacheHeader</name>
+<description>Add an X-Cache header to the response.</description>
+<syntax>CacheHeader <var>on|off</var></syntax>
+<default>CacheHeader off</default>
+<contextlist><context>server config</context>
+ <context>virtual host</context>
+ <context>directory</context>
+ <context>.htaccess</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.9 and later</compatibility>
+
+<usage>
+ <p>When the <directive module="mod_cache">CacheHeader</directive> directive
+ is switched on, an <strong>X-Cache</strong> header will be added to the response
+ with the cache status of this response. If the normal handler is used, this
+ directive may appear within a <directive module="core"><Directory></directive>
+ or <directive module="core"><Location></directive> directive. If the quick
+ handler is used, this directive must appear within a server or virtual host
+ context, otherwise the setting will be ignored.</p>
+
+ <dl>
+ <dt><strong>HIT</strong></dt><dd>The entity was fresh, and was served from
+ cache.</dd>
+ <dt><strong>REVALIDATE</strong></dt><dd>The entity was stale, was successfully
+ revalidated and was served from cache.</dd>
+ <dt><strong>MISS</strong></dt><dd>The entity was fetched from the upstream
+ server and was not served from cache.</dd>
+ </dl>
+
+ <example>
+ # Enable the X-Cache header<br />
+ CacheHeader on<br />
+ </example>
+
+ <example>
+ X-Cache: HIT from localhost<br />
+ </example>
+
+</usage>
+</directivesynopsis>
+
+<directivesynopsis>
+<name>CacheDetailHeader</name>
+<description>Add an X-Cache-Detail header to the response.</description>
+<syntax>CacheDetailHeader <var>on|off</var></syntax>
+<default>CacheDetailHeader off</default>
+<contextlist><context>server config</context>
+ <context>virtual host</context>
+ <context>directory</context>
+ <context>.htaccess</context>
+</contextlist>
+<compatibility>Available in Apache 2.3.9 and later</compatibility>
+
+<usage>
+ <p>When the <directive module="mod_cache">CacheDetailHeader</directive> directive
+ is switched on, an <strong>X-Cache-Detail</strong> header will be added to the response
+ containing the detailed reason for a particular caching decision.</p>
+
+ <p>It can be useful during development of cached RESTful services to have additional
+ information about the caching decision written to the response headers, so as to
+ confirm whether <code>Cache-Control</code> and other headers have been correctly
+ used by the service and client.</p>
+ <p>If the normal handler is used, this directive may appear within a
+ <directive module="core"><Directory></directive> or
+ <directive module="core"><Location></directive> directive. If the quick handler
+ is used, this directive must appear within a server or virtual host context, otherwise
+ the setting will be ignored.</p>
+
+ <example>
+ # Enable the X-Cache-Detail header<br />
+ CacheDetailHeader on<br />
+ </example>
+
+ <example>
+ X-Cache-Detail: "conditional cache hit: entity refreshed" from localhost<br />
+ </example>
+
+</usage>
+</directivesynopsis>
+
</modulesynopsis>
Modified: httpd/httpd/trunk/include/ap_mmn.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/include/ap_mmn.h?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/include/ap_mmn.h (original)
+++ httpd/httpd/trunk/include/ap_mmn.h Mon Sep 27 09:20:40 2010
@@ -269,6 +269,7 @@
* ap_cache_try_lock, ap_cache_check_freshness,
* cache_server_conf, cache_enable, cache_disable,
* cache_request_rec and cache_provider_list private.
+ * 20100923.1 (2.3.9-dev) Add cache_status hook.
*/
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
@@ -276,7 +277,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20100923
#endif
-#define MODULE_MAGIC_NUMBER_MINOR 0 /* 0...n */
+#define MODULE_MAGIC_NUMBER_MINOR 1 /* 0...n */
/**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a
Modified: httpd/httpd/trunk/modules/cache/cache_util.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/cache_util.h?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/cache_util.h (original)
+++ httpd/httpd/trunk/modules/cache/cache_util.h Mon Sep 27 09:20:40 2010
@@ -92,11 +92,12 @@ extern "C" {
#define DEFAULT_CACHE_EXPIRE MSEC_ONE_HR
#define DEFAULT_CACHE_LMFACTOR (0.1)
#define DEFAULT_CACHE_MAXAGE 5
+#define DEFAULT_X_CACHE 0
+#define DEFAULT_X_CACHE_DETAIL 0
#define DEFAULT_CACHE_LOCKPATH "/mod_cache-lock"
#define CACHE_LOCKNAME_KEY "mod_cache-lockname"
#define CACHE_LOCKFILE_KEY "mod_cache-lockfile"
-
/**
* cache_util.c
*/
@@ -168,8 +169,19 @@ typedef struct {
/** run within the quick handler */
int quick;
int quick_set;
+ int x_cache;
+ int x_cache_set;
+ int x_cache_detail;
+ int x_cache_detail_set;
} cache_server_conf;
+typedef struct {
+ int x_cache;
+ int x_cache_set;
+ int x_cache_detail;
+ int x_cache_detail_set;
+} cache_dir_conf;
+
/* A linked-list of authn providers. */
typedef struct cache_provider_list cache_provider_list;
Modified: httpd/httpd/trunk/modules/cache/mod_cache.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_cache.c?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/mod_cache.c (original)
+++ httpd/httpd/trunk/modules/cache/mod_cache.c Mon Sep 27 09:20:40 2010
@@ -201,6 +201,9 @@ static int cache_quick_handler(request_r
return DECLINED;
}
+ /* we've got a cache hit! tell everyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_HIT, "cache hit");
+
/* if we are a lookup, we are exiting soon one way or another; Restore
* the headers. */
if (lookup) {
@@ -453,6 +456,9 @@ static int cache_handler(request_rec *r)
return DECLINED;
}
+ /* we've got a cache hit! tell everyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_HIT, "cache hit");
+
rv = ap_meets_conditions(r);
if (rv != OK) {
return rv;
@@ -816,7 +822,7 @@ static int cache_save_filter(ap_filter_t
&& exp < r->request_time)
{
/* if a Expires header is in the past, don't cache it */
- reason = "Expires header already expired, not cacheable";
+ reason = "Expires header already expired; not cacheable";
}
else if (!conf->ignorequerystring && r->parsed_uri.query && exps == NULL &&
!ap_cache_liststr(NULL, cc_out, "max-age", NULL) &&
@@ -844,7 +850,7 @@ static int cache_save_filter(ap_filter_t
/* Note: mod-include clears last_modified/expires/etags - this
* is why we have an optional function for a key-gen ;-)
*/
- reason = "No Last-Modified, Etag, Expires, Cache-Control:max-age or Cache-Control:s-maxage headers";
+ reason = "No Last-Modified; Etag; Expires; Cache-Control:max-age or Cache-Control:s-maxage headers";
}
else if (r->header_only && !cache->stale_handle) {
/* Forbid HEAD requests unless we have it cached already */
@@ -903,6 +909,9 @@ static int cache_save_filter(ap_filter_t
"cache: %s not cached. Reason: %s", r->unparsed_uri,
reason);
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_MISS, reason);
+
/* remove this filter from the chain */
ap_remove_output_filter(f);
@@ -1009,6 +1018,10 @@ static int cache_save_filter(ap_filter_t
}
if (rv != OK) {
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_MISS,
+ "cache miss: create_entity failed");
+
/* Caching layer declined the opportunity to cache the response */
ap_remove_output_filter(f);
cache_remove_lock(conf, cache, r, cache->handle ?
@@ -1211,6 +1224,17 @@ static int cache_save_filter(ap_filter_t
"cache: attempt to remove url from cache unsuccessful.");
}
+ /* we've got a cache conditional hit! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_REVALIDATE,
+ "conditional cache hit: entity refresh failed");
+
+ }
+ else {
+
+ /* we've got a cache conditional hit! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_REVALIDATE,
+ "conditional cache hit: entity refreshed");
+
}
/* let someone else attempt to cache */
@@ -1224,12 +1248,20 @@ static int cache_save_filter(ap_filter_t
ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, r->server,
"cache: store_headers failed");
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_MISS,
+ "cache miss: store_headers failed");
+
ap_remove_output_filter(f);
cache_remove_lock(conf, cache, r, cache->handle ?
(char *)cache->handle->cache_obj->key : NULL, NULL);
return ap_pass_brigade(f->next, in);
}
+ /* we've got a cache miss! tell anyone who cares */
+ cache_run_cache_status(cache->handle, r, AP_CACHE_MISS,
+ "cache miss: attempting entity save");
+
return cache_save_store(f, in, conf, cache);
}
@@ -1309,9 +1341,102 @@ static int cache_filter(ap_filter_t *f,
return ap_pass_brigade(f->next, in);
}
+/**
+ * If configured, add the status of the caching attempt to the subprocess
+ * environment, and if configured, to headers in the response.
+ *
+ * The status is saved below the broad category of the status (hit, miss,
+ * revalidate), as well as a single cache-status key. This can be used for
+ * conditional logging.
+ *
+ * The status is optionally saved to an X-Cache header, and the detail of
+ * why a particular cache entry was cached (or not cached) is optionally
+ * saved to an X-Cache-Detail header. This extra detail is useful for
+ * service developers who may need to know whether their Cache-Control headers
+ * are working correctly.
+ */
+static int cache_status(cache_handle_t *h, request_rec *r, ap_cache_status_e status,
+ const char *reason)
+{
+ cache_server_conf
+ *conf =
+ (cache_server_conf *) ap_get_module_config(r->server->module_config,
+ &cache_module);
+
+ cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, &cache_module);
+ int x_cache = 0, x_cache_detail = 0;
+
+ switch (status) {
+ case AP_CACHE_HIT: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_HIT_ENV, reason);
+ break;
+ }
+ case AP_CACHE_REVALIDATE: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_REVALIDATE_ENV, reason);
+ break;
+ }
+ case AP_CACHE_MISS: {
+ apr_table_setn(r->subprocess_env, AP_CACHE_MISS_ENV, reason);
+ break;
+ }
+ }
+
+ apr_table_setn(r->subprocess_env, AP_CACHE_STATUS_ENV, reason);
+
+ if (dconf && dconf->x_cache_set) {
+ x_cache = dconf->x_cache;
+ }
+ else {
+ x_cache = conf->x_cache;
+ }
+ if (x_cache) {
+ apr_table_setn(r->headers_out, "X-Cache", apr_psprintf(r->pool, "%s from %s",
+ status == AP_CACHE_HIT ? "HIT" : status == AP_CACHE_REVALIDATE ?
+ "REVALIDATE" : "MISS", r->server->server_hostname));
+ }
+
+ if (dconf && dconf->x_cache_detail_set) {
+ x_cache_detail = dconf->x_cache_detail;
+ }
+ else {
+ x_cache_detail = conf->x_cache_detail;
+ }
+ if (x_cache_detail) {
+ apr_table_setn(r->headers_out, "X-Cache-Detail", apr_psprintf(r->pool,
+ "\"%s\" from %s", reason, r->server->server_hostname));
+ }
+
+ return OK;
+}
+
/* -------------------------------------------------------------- */
/* Setup configurable data */
+static void *create_dir_config(apr_pool_t *p, char *dummy)
+{
+ cache_dir_conf *dconf = apr_pcalloc(p, sizeof(cache_dir_conf));
+
+ dconf->x_cache = DEFAULT_X_CACHE;
+ dconf->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
+
+ return dconf;
+}
+
+static void *merge_dir_config(apr_pool_t *p, void *basev, void *addv) {
+ cache_dir_conf *new = (cache_dir_conf *) apr_pcalloc(p, sizeof(cache_dir_conf));
+ cache_dir_conf *add = (cache_dir_conf *) addv;
+ cache_dir_conf *base = (cache_dir_conf *) basev;
+
+ new->x_cache = (add->x_cache_set == 0) ? base->x_cache : add->x_cache;
+ new->x_cache_set = add->x_cache_set || base->x_cache_set;
+ new->x_cache_detail = (add->x_cache_detail_set == 0) ? base->x_cache_detail
+ : add->x_cache_detail;
+ new->x_cache_detail_set = add->x_cache_detail_set
+ || base->x_cache_detail_set;
+
+ return new;
+}
+
static void * create_cache_config(apr_pool_t *p, server_rec *s)
{
const char *tmppath;
@@ -1361,6 +1486,8 @@ static void * create_cache_config(apr_po
ps->lockpath = apr_pstrcat(p, tmppath, DEFAULT_CACHE_LOCKPATH, NULL);
}
ps->lockmaxage = apr_time_from_sec(DEFAULT_CACHE_MAXAGE);
+ ps->x_cache = DEFAULT_X_CACHE;
+ ps->x_cache_detail = DEFAULT_X_CACHE_DETAIL;
return ps;
}
@@ -1435,6 +1562,14 @@ static void * merge_cache_config(apr_poo
(overrides->quick_set == 0)
? base->quick
: overrides->quick;
+ ps->x_cache =
+ (overrides->x_cache_set == 0)
+ ? base->x_cache
+ : overrides->x_cache;
+ ps->x_cache_detail =
+ (overrides->x_cache_detail_set == 0)
+ ? base->x_cache_detail
+ : overrides->x_cache_detail;
return ps;
}
@@ -1782,6 +1917,52 @@ static const char *set_cache_lock_maxage
return NULL;
}
+static const char *set_cache_x_cache(cmd_parms *parms, void *dummy, int flag)
+{
+
+ if (parms->path) {
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->x_cache = flag;
+ dconf->x_cache_set = 1;
+
+ }
+ else {
+ cache_server_conf *conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+
+ conf->x_cache = flag;
+ conf->x_cache_set = 1;
+
+ }
+
+ return NULL;
+}
+
+static const char *set_cache_x_cache_detail(cmd_parms *parms, void *dummy, int flag)
+{
+
+ if (parms->path) {
+ cache_dir_conf *dconf = (cache_dir_conf *)dummy;
+
+ dconf->x_cache_detail = flag;
+ dconf->x_cache_detail_set = 1;
+
+ }
+ else {
+ cache_server_conf *conf =
+ (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+ &cache_module);
+
+ conf->x_cache_detail = flag;
+ conf->x_cache_detail_set = 1;
+
+ }
+
+ return NULL;
+}
+
static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
@@ -1859,6 +2040,11 @@ static const command_rec cache_cmds[] =
"temp directory."),
AP_INIT_TAKE1("CacheLockMaxAge", set_cache_lock_maxage, NULL, RSRC_CONF,
"Maximum age of any thundering herd lock."),
+ AP_INIT_FLAG("CacheHeader", set_cache_x_cache, NULL, RSRC_CONF | ACCESS_CONF,
+ "Add a X-Cache header to responses. Default is off."),
+ AP_INIT_FLAG("CacheDetailHeader", set_cache_x_cache_detail, NULL,
+ RSRC_CONF | ACCESS_CONF,
+ "Add a X-Cache-Detail header to responses. Default is off."),
{NULL}
};
@@ -1869,6 +2055,8 @@ static void register_hooks(apr_pool_t *p
ap_hook_quick_handler(cache_quick_handler, NULL, NULL, APR_HOOK_FIRST);
/* cache handler */
ap_hook_handler(cache_handler, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ /* cache status */
+ cache_hook_cache_status(cache_status, NULL, NULL, APR_HOOK_MIDDLE);
/* cache filters
* XXX The cache filters need to run right after the handlers and before
* any other filters. Consider creating AP_FTYPE_CACHE for this purpose.
@@ -1949,10 +2137,20 @@ static void register_hooks(apr_pool_t *p
AP_DECLARE_MODULE(cache) =
{
STANDARD20_MODULE_STUFF,
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
+ create_dir_config, /* create per-directory config structure */
+ merge_dir_config, /* merge per-directory config structures */
create_cache_config, /* create per-server config structure */
merge_cache_config, /* merge per-server config structures */
cache_cmds, /* command apr_table_t */
register_hooks
};
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(cache_status)
+)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_ALL(cache, CACHE, int, cache_status,
+ (cache_handle_t *h, request_rec *r,
+ ap_cache_status_e status,
+ const char *reason), (h, r, status, reason),
+ OK, DECLINED)
Modified: httpd/httpd/trunk/modules/cache/mod_cache.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_cache.h?rev=1001639&r1=1001638&r2=1001639&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/cache/mod_cache.h (original)
+++ httpd/httpd/trunk/modules/cache/mod_cache.h Mon Sep 27 09:20:40 2010
@@ -29,6 +29,7 @@
#include "httpd.h"
#include "apr_date.h"
#include "apr_optional.h"
+#include "apr_hooks.h"
/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and
* CACHE_DECLARE_DATA with appropriate export and import tags for the platform
@@ -106,6 +107,17 @@ typedef struct {
apr_status_t (*commit_entity)(cache_handle_t *h, request_rec *r);
} cache_provider;
+typedef enum {
+ AP_CACHE_HIT,
+ AP_CACHE_REVALIDATE,
+ AP_CACHE_MISS
+} ap_cache_status_e;
+
+#define AP_CACHE_HIT_ENV "cache-hit"
+#define AP_CACHE_REVALIDATE_ENV "cache-revalidate"
+#define AP_CACHE_MISS_ENV "cache-miss"
+#define AP_CACHE_STATUS_ENV "cache-status"
+
/* cache_util.c */
/* do a HTTP/1.1 age calculation */
@@ -150,6 +162,22 @@ CACHE_DECLARE(apr_table_t *)ap_cache_cac
/* hooks */
+
+/**
+ * Cache status hook.
+ * This hook is called as soon as the cache has made a decision as to whether
+ * an entity should be served from cache (hit), should be served from cache
+ * after a successful validation (revalidate), or served from the backend
+ * and potentially cached (miss).
+ *
+ * A basic implementation of this hook exists in mod_cache which writes this
+ * information to the subprocess environment, and optionally to request
+ * headers. Further implementations may add hooks as appropriate to perform
+ * more advanced processing, or to store statistics about the cache behaviour.
+ */
+APR_DECLARE_EXTERNAL_HOOK(cache, CACHE, int, cache_status, (cache_handle_t *h,
+ request_rec *r, ap_cache_status_e status, const char *reason));
+
APR_DECLARE_OPTIONAL_FN(apr_status_t,
ap_cache_generate_key,
(request_rec *r, apr_pool_t*p, const char **key));