I've attached a proof-of-concept patch against httpd 2.4.10 that allows mod_cache to be bypassed under conditions specified in the conf files. It adds an optional fourth argument to the CacheEnable directive:

CacheEnable cache_type [url-string] [expr=expression]

If the expression is present, data will only be served from the cache for requests for which the expression evaluates to true. This permits things such as:

# Only serve cached data if no (login or other) cookies are present in the request:
CacheEnable disk / "expr=-z %{req:Cookie}"

# Do not serve cached pages to our testing network:
<Location /some/path>
CacheEnable disk "expr=! ( %{REMOTE_ADDR} -ipmatch 192.168.0.0/16 )"
</Location>

Is there interest in such an enhancement? If so, I'll make any requested changes to the implementation, port the patch forward to trunk, put in real APLOGNOs, make sure it passes the test suite, create a documentation patch, and create a bugzilla for all this.

--
  Mark Montague
  [email protected]

diff -urd httpd-2.4.10.orig/modules/cache/cache_util.c 
httpd-2.4.10/modules/cache/cache_util.c
--- httpd-2.4.10.orig/modules/cache/cache_util.c        2014-05-30 
13:50:37.000000000 +0000
+++ httpd-2.4.10/modules/cache/cache_util.c     2014-08-23 01:34:25.521689874 
+0000
@@ -27,6 +27,10 @@
 
 extern module AP_MODULE_DECLARE_DATA cache_module;
 
+extern int cache_run_cache_status(cache_handle_t *h, request_rec *r,
+                apr_table_t *headers, ap_cache_status_e status,
+                const char *reason);
+
 /* Determine if "url" matches the hostname, scheme and port and path
  * in "filter". All but the path comparisons are case-insensitive.
  */
@@ -129,10 +133,30 @@
 }
 
 static cache_provider_list *get_provider(request_rec *r, struct cache_enable 
*ent,
-        cache_provider_list *providers)
+        cache_provider_list *providers, int *bypass)
 {
-    /* Fetch from global config and add to the list. */
     cache_provider *provider;
+
+    /* If an expression is present, evaluate it and make sure it is true */
+    if (ent->expr != NULL) {
+        const char *err = NULL;
+        int eval = ap_expr_exec(r, ent->expr, &err);
+        if (err) {
+            ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+                APLOGNO(06668) "Failed to evaluate expression (%s) - skipping 
CacheEnable for uri %s", err, r->uri);
+            return providers;
+        }
+        if (eval <= 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+                          APLOGNO(06667) "cache: CacheEnable expr at %s(%u) is 
FALSE for uri %s", ent->expr->filename, ent->expr->line_number, r->uri);
+            (*bypass)++;
+            return providers;
+        }
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
+                      APLOGNO(06666) "cache: CacheEnable expr at %s(%u) is 
TRUE for uri %s", ent->expr->filename, ent->expr->line_number, r->uri);
+    }
+
+    /* Fetch from global config and add to the list. */
     provider = ap_lookup_provider(CACHE_PROVIDER_GROUP, ent->type,
                                   "0");
     if (!provider) {
@@ -172,6 +196,7 @@
 {
     cache_dir_conf *dconf = ap_get_module_config(r->per_dir_config, 
&cache_module);
     cache_provider_list *providers = NULL;
+    int bypass = 0;
     int i;
 
     /* per directory cache disable */
@@ -193,7 +218,7 @@
     for (i = 0; i < dconf->cacheenable->nelts; i++) {
         struct cache_enable *ent =
                                 (struct cache_enable 
*)dconf->cacheenable->elts;
-        providers = get_provider(r, &ent[i], providers);
+        providers = get_provider(r, &ent[i], providers, &bypass);
     }
 
     /* loop through all the global cacheenable entries */
@@ -201,10 +226,16 @@
         struct cache_enable *ent =
                                 (struct cache_enable *)conf->cacheenable->elts;
         if (uri_meets_conditions(&ent[i].url, ent[i].pathlen, &uri)) {
-            providers = get_provider(r, &ent[i], providers);
+            providers = get_provider(r, &ent[i], providers, &bypass);
         }
     }
 
+    if (providers == NULL && bypass > 0) {
+        /* we're bypassing the cache. tell everyone who cares */
+        cache_run_cache_status(NULL, r, r->headers_out, AP_CACHE_BYPASS,
+                               apr_psprintf(r->pool, "cache bypass: %d 
conditions not satisfied", bypass));
+    }
+
     return providers;
 }
 
diff -urd httpd-2.4.10.orig/modules/cache/cache_util.h 
httpd-2.4.10/modules/cache/cache_util.h
--- httpd-2.4.10.orig/modules/cache/cache_util.h        2014-08-20 
14:50:12.251792173 +0000
+++ httpd-2.4.10/modules/cache/cache_util.h     2014-08-22 00:20:24.946556676 
+0000
@@ -109,6 +109,7 @@
     apr_uri_t url;
     const char *type;
     apr_size_t pathlen;
+    ap_expr_info_t *expr;
 };
 
 struct cache_disable {
diff -urd httpd-2.4.10.orig/modules/cache/mod_cache.c 
httpd-2.4.10/modules/cache/mod_cache.c
--- httpd-2.4.10.orig/modules/cache/mod_cache.c 2014-08-20 14:50:12.253792121 
+0000
+++ httpd-2.4.10/modules/cache/mod_cache.c      2014-08-23 01:26:23.185166576 
+0000
@@ -1753,7 +1753,7 @@
  * 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,
+int cache_status(cache_handle_t *h, request_rec *r,
         apr_table_t *headers, ap_cache_status_e status, const char *reason)
 {
     cache_server_conf
@@ -1781,6 +1781,10 @@
         apr_table_setn(r->subprocess_env, AP_CACHE_INVALIDATE_ENV, reason);
         break;
     }
+    case AP_CACHE_BYPASS: {
+        apr_table_setn(r->subprocess_env, AP_CACHE_BYPASS_ENV, reason);
+        break;
+    }
     }
 
     apr_table_setn(r->subprocess_env, AP_CACHE_STATUS_ENV, reason);
@@ -1795,7 +1799,8 @@
         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",
+                                == AP_CACHE_INVALIDATE ? "INVALIDATE" : status
+                                == AP_CACHE_BYPASS ? "BYPASS" : "MISS",
                 r->server->server_hostname));
     }
 
@@ -2212,8 +2217,8 @@
 }
 
 static const char *add_cache_enable(cmd_parms *parms, void *dummy,
-                                    const char *type,
-                                    const char *url)
+                                    const char *type, const char *url,
+                                    const char *exprclause)
 {
     cache_dir_conf *dconf = (cache_dir_conf *)dummy;
     cache_server_conf *conf;
@@ -2231,6 +2236,14 @@
           type);
     }
 
+    if (url && strncasecmp(url, "expr=", 5) == 0) {
+        if (exprclause) {
+            return "Too many arguments, or url and conditional expr are 
switched.";
+        }
+        exprclause = url;
+        url = NULL;
+    }
+
     if (!url) {
         url = parms->path;
     }
@@ -2265,6 +2278,16 @@
         new->pathlen = 1;
         new->url.path = "/";
     }
+    if (exprclause && strncasecmp(exprclause, "expr=", 5) == 0) {
+        new->expr = ap_expr_parse_cmd(parms, exprclause + 5,
+                                      AP_EXPR_FLAG_DONT_VARY, &err, NULL);
+        if (err) {
+            return apr_pstrcat(parms->pool,
+                               "Can't parse expression: ", err, NULL);
+        }
+    } else {
+        new->expr = NULL;
+    }
     return NULL;
 }
 
@@ -2528,9 +2551,9 @@
      * This is more intuitive that requiring a LoadModule directive.
      */
 
-    AP_INIT_TAKE12("CacheEnable", add_cache_enable, NULL, 
RSRC_CONF|ACCESS_CONF,
+    AP_INIT_TAKE123("CacheEnable", add_cache_enable, NULL, 
RSRC_CONF|ACCESS_CONF,
                    "A cache type and partial URL prefix below which "
-                   "caching is enabled"),
+                   "caching is enabled with an optional conditional 
expression"),
     AP_INIT_TAKE1("CacheDisable", add_cache_disable, NULL, 
RSRC_CONF|ACCESS_CONF,
                   "A partial URL prefix below which caching is disabled"),
     AP_INIT_TAKE12("CacheMaxExpire", set_cache_maxex, NULL, 
RSRC_CONF|ACCESS_CONF,
diff -urd httpd-2.4.10.orig/modules/cache/mod_cache.h 
httpd-2.4.10/modules/cache/mod_cache.h
--- httpd-2.4.10.orig/modules/cache/mod_cache.h 2011-12-03 18:02:24.000000000 
+0000
+++ httpd-2.4.10/modules/cache/mod_cache.h      2014-08-22 13:37:20.562717222 
+0000
@@ -116,13 +116,15 @@
     AP_CACHE_HIT,
     AP_CACHE_REVALIDATE,
     AP_CACHE_MISS,
-    AP_CACHE_INVALIDATE
+    AP_CACHE_INVALIDATE,
+    AP_CACHE_BYPASS
 } 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_INVALIDATE_ENV "cache-invalidate"
+#define AP_CACHE_BYPASS_ENV "cache-bypass"
 #define AP_CACHE_STATUS_ENV "cache-status"
 
 

Reply via email to