Graham Leggett wrote:
Issac Goldstand wrote:

- Cache-Control and Pragma headers must be stripped from requests into
the cache.

Why?  Just because you are in offline mode doesn't mean other proxies
between you and the client (or the client itself) are.

Other proxies don't matter in this case - the end goal is to protect the origin server from being hit unnecessarily by the cache, in response to headers added by some client.

If strict RFC compliant behaviour was in effect, a Cache-Control: no-cache from a client would invalidate a cached URL, forcing a conditional refresh from the origin server, which may either be missing or broken. The result is that a cached URL gets replaced by a page with a 5xx error in it.
As Brian correctly noticed, I misread you initial comment - I thought you meant stripping the upstream headers, not downstream. In my implementation (see below) we just waste a bit more diskspace to deal with this by requiring an exact header match, rather than URL match, to deal with this. It's not final, but it's what we had time to initially do.

I think I can get permission to send a patch of what we did so far, if
there's interest; it's crude and still not fully tested (and we haven't
merged in the recent mod_disk_cache changes), but it's simple and may be
a helpful starting point.

Let's take a look.
Enclosed. Currently, I've attached it as a "massive" patch to revision 443423 (just before the massive changes to mod_disk_cache, which I haven't found time to review and merge yet), which contains the offline code, code to select a specific cache entry by ID using a special request header, and some other minor changes which don't affect functionality overall (whitespace, debug logging, etc). We plan on eventually making it better (and prettier); frankly I never expected to see any interest in offline functionality being bundled with the standard mod_cache, so aimed for quick'n'dirty rather than robust. If there's interest in using bits of this, I'd be happy to clean it up, merge it with trunk and break up the patch into multiple functionality changes-based patches.

 Issac
Index: mod_cache.c
===================================================================
--- mod_cache.c (revision 443423)
+++ mod_cache.c (working copy)
@@ -17,6 +17,7 @@
 #define CORE_PRIVATE
 
 #include "mod_cache.h"
+#include "util_md5.h" /* digesttobase64 */
 
 module AP_MODULE_DECLARE_DATA cache_module;
 APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
@@ -51,23 +52,26 @@
 static int cache_url_handler(request_rec *r, int lookup)
 {
     apr_status_t rv;
-    const char *auth;
     cache_provider_list *providers;
     cache_request_rec *cache;
     cache_server_conf *conf;
     apr_bucket_brigade *out;
-    ap_filter_t *next;
+    apr_bucket *b;
     ap_filter_rec_t *cache_out_handle;
 
-    /* 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);
 
     /*
+     * Are we allowed to serve cached info at all?
+     */
+
+    /* Unless we're in offline browsing mode, we can only process GET requests 
*/
+    if (!conf->offlinemode && r->method_number != M_GET) {
+        return DECLINED;
+    }
+
+    /*
      * Which cache module (if any) should handle this request?
      */
     if (!(providers = ap_cache_get_providers(r, conf, r->parsed_uri))) {
@@ -86,20 +90,6 @@
     cache->providers = providers;
 
     /*
-     * Are we allowed to serve cached info at all?
-     */
-
-    /* find certain cache controlling headers */
-    auth = apr_table_get(r->headers_in, "Authorization");
-
-    /* First things first - does the request allow us to return
-     * cached information at all? If not, just decline the request.
-     */
-    if (auth) {
-        return DECLINED;
-    }
-
-    /*
      * Try to serve this request from the cache.
      *
      * If no existing cache file (DECLINED)
@@ -109,7 +99,8 @@
      *   add cache_out filter
      *   return OK
      */
-    rv = cache_select(r);
+
+    rv = cache_select(r);
     if (rv != OK) {
         if (rv == DECLINED) {
             if (!lookup) {
@@ -145,9 +136,39 @@
                  * is available later during running the filter maybe
                  * different due to an internal redirect.
                  */
+
                 cache->remove_url_filter =
                     ap_add_output_filter_handle(cache_remove_url_filter_handle,
                                                 cache, r, r->connection);
+
+                /**
+                 * If we have a stale handle, and are running in offline mode,
+                 * add the output filter anyway, immediately following the SAVE
+                 * and REMOVAL filters.  This should be safe in all cases:
+                 * 1) If we get no content (502/504), we'll use stale content
+                 *    (and removed the removal filter)
+                 * 2) If we get fresh content, we'll have replaced the content,
+                 *    so it's still safe to serve the cached entity (albeit a
+                 *    small performance hit to re-read the re-written rentry)
+                 **/
+                if (conf->offlinemode && cache->stale_handle) {
+                    if (r->main) {
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                 r->server, "Adding CACHE_OUT_SUBREQ filter 
for %s",
+                                 r->uri);
+                        cache_out_handle = cache_out_subreq_filter_handle;
+                    } else {
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                 r->server, "Adding CACHE_OUT filter for %s",
+                                 r->uri);
+                        cache_out_handle = cache_out_filter_handle;
+                    }
+                    /** Don't touch the rest of the filters - we're still 
running
+                     * the request, and we're exactly where we want to be */
+                    ap_add_output_filter_handle(cache_out_handle, NULL,
+                                                r, r->connection);
+                    cache->reopen = 1; /** Flag CACHE_OUT to re-open data 
filehandle */
+                }
             }
             else {
                 if (cache->stale_headers) {
@@ -215,9 +236,15 @@
      * or not.
      */
     if (r->main) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                 r->server, "Adding CACHE_OUT_SUBREQ filter for %s",
+                 r->uri);
         cache_out_handle = cache_out_subreq_filter_handle;
     }
     else {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                 r->server, "Adding CACHE_OUT filter for %s",
+                 r->uri);
         cache_out_handle = cache_out_filter_handle;
     }
     ap_add_output_filter_handle(cache_out_handle, NULL, r, r->connection);
@@ -234,14 +261,22 @@
      * 2. We call the insert_filter hook. This causes filters e.g. like
      *    the ones set with SetOutputFilter to be added.
      */
-    next = r->output_filters;
+    
+    /** This needn't be done.  Instead, we'll simply send an EOS down the stack
+     * downstream filters will be nice enough not to put anything after the EOS
+     * and the cache_out filter will destroy everything before it
+     */
+/*    next = r->output_filters;
     while (next && (next->frec != cache_out_handle)) {
         ap_remove_output_filter(next);
         next = next->next;
-    }
+    }*/
 
     /* kick off the filter stack */
     out = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+    b = apr_bucket_eos_create(r->connection->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(out, b);
+    
     rv = ap_pass_brigade(r->output_filters, out);
     if (rv != APR_SUCCESS) {
         ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,
@@ -254,6 +289,71 @@
     return OK;
 }
 
+/**
+ * TODO: This function is resource-expensive.  Avoid using it if:
+ * 1) We detected a specific key request
+ * 2) We can fallback to an easier keygen routine (eg, GET url can possibly
+ * use mod_cache's default keygen routine)
+ **/
+
+static int add_header_to_digest (void *rec, const char* key, const char* val) {
+    apr_md5_update((apr_md5_ctx_t *)rec, key, strlen(key));
+    apr_md5_update((apr_md5_ctx_t *)rec, val, strlen(val));
+    return 1;
+}
+
+apr_status_t cache_off_generate_key_default(request_rec *r, apr_pool_t* p,
+                                            char** key)
+{
+    apr_md5_ctx_t *md5ctx;
+    apr_bucket_brigade *bb;
+    apr_bucket *b;
+    apr_status_t rv;
+    const char* data;
+    apr_size_t len;
+
+    md5ctx = apr_pcalloc(p, sizeof(*md5ctx));
+    apr_md5_init(md5ctx);
+
+    /** Add request line */
+    apr_md5_update(md5ctx, r->the_request, strlen(r->the_request));
+    /** Add headers - use headers_in rather than capturing the headers for
+     * subrequest compatibility */
+    /** Ensure that duplicate entries are removed (maybe the table was
+     * modified since ap_get_mime_headers did this) */
+    apr_table_compress(r->headers_in, APR_OVERLAP_TABLES_MERGE);
+    apr_table_do(add_header_to_digest, md5ctx, r->headers_in, NULL);
+
+    /** Finally, try to slurp a bit of body data, so we
+     * don't rely exclusively on the headers to produce the digest */
+    bb = apr_brigade_create(p, r->connection->bucket_alloc);
+    /** FIXME: Use a nice (preferably existing) const instead of 1024 */
+    ap_get_brigade(r->proto_input_filters, bb, AP_MODE_SPECULATIVE,
+                                           APR_NONBLOCK_READ, 1024);
+
+    for (b = APR_BRIGADE_FIRST(bb);
+         b != APR_BRIGADE_SENTINEL(bb);
+         b = APR_BUCKET_NEXT(b))
+    {
+        if (APR_BUCKET_IS_EOS(b)) {
+            break;
+        }
+        rv = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
+        if (rv != APR_SUCCESS) {
+            break;
+        }
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
+                      "[CACHE_GENERATE_KEY] Got data %s", 
+                      (const char *)apr_pstrndup(p, data ,len));
+        apr_md5_update(md5ctx, data, len);
+    }
+
+    *key = ap_md5contextTo64(p, md5ctx);
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
+                 "Got key %s for uri %s", *key, r->uri);
+    return APR_SUCCESS;
+}
+
 /*
  * CACHE_OUT filter
  * ----------------
@@ -264,6 +364,8 @@
 {
     request_rec *r = f->r;
     cache_request_rec *cache;
+    apr_bucket *b;
+    int *seen_eos;
 
     cache = (cache_request_rec *) ap_get_module_config(r->request_config,
                                                        &cache_module);
@@ -277,22 +379,53 @@
         return ap_pass_brigade(f->next, bb);
     }
 
+    seen_eos = f->ctx;
+    if (seen_eos == NULL) {
+        f->ctx = seen_eos = apr_pcalloc(r->pool, sizeof(*seen_eos));
+    }
+        
+    if (!(*seen_eos)) {
+        /** Go through buckets until we get an EOS */
+        for (b = APR_BRIGADE_FIRST(bb);
+            b != APR_BRIGADE_SENTINEL(bb);
+            b = APR_BUCKET_NEXT(b))
+        {
+            if (APR_BUCKET_IS_EOS(b)) {
+                *seen_eos = 1;
+                break;
+            }
+        }
+    }
+
+    /** If we haven't gotten EOS yet, just return success */
+    if (!(*seen_eos)) 
+        return APR_SUCCESS;
+
+    /** Fallthough - we got EOS, cache file should be ready by now */
     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
                  "cache: running CACHE_OUT filter");
 
+    /** If we have stale content and no updated fresh content, use the stale */
+    if (!cache->handle && cache->stale_handle)
+        cache->handle = cache->stale_handle;
+
     /* restore status of cached response */
     /* XXX: This exposes a bug in mem_cache, since it does not
      * restore the status into it's handle. */
     r->status = cache->handle->cache_obj->info.status;
 
+    /** Reopen data file if we closed it after writing */
+    cache->handle->cache_obj->reopen = cache->reopen; 
+
     /* recall_headers() was called in cache_select() */
+
     cache->provider->recall_body(cache->handle, r->pool, bb);
 
     /* This filter is done once it has served up its content */
     ap_remove_output_filter(f);
 
     ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r->server,
-                 "cache: serving %s", r->uri);
+                 "cache: served %s", r->uri);
     return ap_pass_brigade(f->next, bb);
 }
 
@@ -429,23 +562,44 @@
      * is appropriate _NOT_ to cache the data from the server. There are
      * a whole lot of conditions that prevent us from caching this data.
      * They are tested here one by one to be clear and unambiguous.
+     *
+     * TODO: With mod_proxy, we don't get a 502/4 here, and end up running
+     * REMOVE_URL filter (which is bad).  The client seems to get a 404
      */
-    if (r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
+    if (conf->offlinemode && cache->handle == NULL
+            && (r->status == HTTP_BAD_GATEWAY
+                || r->status == HTTP_GATEWAY_TIME_OUT)) {
+        /** No fresh handle, and no upstream response */
+        if (cache->stale_handle != NULL) {
+            /** Remove the delete filter */
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "cache: Removing CACHE_REMOVE_URL filter for "
+                         "unverifiable stale entry.");
+            ap_remove_output_filter(cache->remove_url_filter);
+        }
+        reason = apr_psprintf(p, "Response status %d", r->status);
+    }
+
+    if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE
         && r->status != HTTP_MULTIPLE_CHOICES
         && r->status != HTTP_MOVED_PERMANENTLY
-        && r->status != HTTP_NOT_MODIFIED) {
+        && r->status != HTTP_NOT_MODIFIED
+        && !conf->offlinemode)) {
         /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
          * We don't cache 206, because we don't (yet) cache partial responses.
          * We include 304 Not Modified here too as this is the origin server
          * telling us to serve the cached copy.
          */
+
         reason = apr_psprintf(p, "Response status %d", r->status);
     }
-    else if (exps != NULL && exp == APR_DATE_BAD) {
+    else if (exps != NULL && exp == APR_DATE_BAD
+             && !conf->offlinemode) {
         /* if a broken Expires header is present, don't cache it */
         reason = apr_pstrcat(p, "Broken expires header: ", exps, NULL);
     }
-    else if (r->args && exps == NULL) {
+    else if (r->args && exps == NULL
+             && !conf->offlinemode) {
         /* if query string present but no expiration time, don't cache it
          * (RFC 2616/13.9)
          */
@@ -455,11 +609,13 @@
              !cache->handle && !cache->stale_handle) {
         /* if the server said 304 Not Modified but we have no cache
          * file - pass this untouched to the user agent, it's not for us.
+         * Even if we're in offline mode, this makes sense
          */
         reason = "HTTP Status 304 Not Modified";
     }
     else if (r->status == HTTP_OK && lastmods == NULL && etag == NULL
-             && (exps == NULL) && (conf->no_last_mod_ignore ==0)) {
+             && (exps == NULL) && (conf->no_last_mod_ignore ==0)
+             && !conf->offlinemode) {
         /* 200 OK response from HTTP/1.0 and up without Last-Modified,
          * Etag, or Expires headers.
          */
@@ -468,12 +624,13 @@
          */
         reason = "No Last-Modified, Etag, or Expires headers";
     }
-    else if (r->header_only) {
+    else if (r->header_only && !conf->offlinemode) {
         /* HEAD requests */
         reason = "HTTP HEAD request";
     }
-    else if (!conf->store_nostore &&
-             ap_cache_liststr(NULL, cc_out, "no-store", NULL)) {
+    else if (!conf->store_nostore
+             && ap_cache_liststr(NULL, cc_out, "no-store", NULL)
+             && !conf->offlinemode) {
         /* RFC2616 14.9.2 Cache-Control: no-store response
          * indicating do not cache, or stop now if you are
          * trying to cache it.
@@ -485,8 +642,9 @@
          */
         reason = "Cache-Control: no-store present";
     }
-    else if (!conf->store_private &&
-             ap_cache_liststr(NULL, cc_out, "private", NULL)) {
+    else if (!conf->store_private
+             && ap_cache_liststr(NULL, cc_out, "private", NULL)
+             && !conf->offlinemode) {
         /* RFC2616 14.9.1 Cache-Control: private response
          * this object is marked for this user's eyes only. Behave
          * as a tunnel.
@@ -497,22 +655,26 @@
     else if (apr_table_get(r->headers_in, "Authorization") != NULL
              && !(ap_cache_liststr(NULL, cc_out, "s-maxage", NULL)
                   || ap_cache_liststr(NULL, cc_out, "must-revalidate", NULL)
-                  || ap_cache_liststr(NULL, cc_out, "public", NULL))) {
+                  || ap_cache_liststr(NULL, cc_out, "public", NULL))
+             && !conf->offlinemode) {
         /* RFC2616 14.8 Authorisation:
          * if authorisation is included in the request, we don't cache,
          * but we can cache if the following exceptions are true:
          * 1) If Cache-Control: s-maxage is included
          * 2) If Cache-Control: must-revalidate is included
          * 3) If Cache-Control: public is included
+         * XXX: Using offline keygen, this may be OK, since authorization
+         * headers match (although beware of MD5 compramise)
          */
         reason = "Authorization required";
     }
     else if (ap_cache_liststr(NULL,
                               apr_table_get(r->headers_out, "Vary"),
-                              "*", NULL)) {
+                              "*", NULL)
+             && !conf->offlinemode) {
         reason = "Vary header contains '*'";
     }
-    else if (r->no_cache) {
+    else if (r->no_cache && !conf->offlinemode) {
         /* or we've been asked not to cache it above */
         reason = "r->no_cache present";
     }
@@ -726,6 +888,9 @@
     }
     info->expire = exp;
 
+    /** Add cache entity ID for cobra application */
+    apr_table_set(r->headers_out, "X-Cache-Entity", 
cache->handle->cache_obj->key);
+
     /* We found a stale entry which wasn't really stale. */
     if (cache->stale_handle) {
         /* Load in the saved status and clear the status line. */
@@ -872,6 +1037,8 @@
         ap_remove_output_filter(f);
         return ap_pass_brigade(f->next, in);
     }
+    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+             "cache: running CACHE_REMOVE_URL filter");
     /* Now remove this cache entry from the cache */
     cache_remove_url(cache, r->pool);
 
@@ -910,6 +1077,8 @@
     ps->store_private_set = 0;
     ps->store_nostore = 0;
     ps->store_nostore_set = 0;
+    ps->offlinemode = 0;
+    ps->offlinemode_set = 0;
     /* array of headers that should not be stored in cache */
     ps->ignore_headers = apr_array_make(p, 10, sizeof(char *));
     ps->ignore_headers_set = CACHE_IGNORE_HEADERS_UNSET;
@@ -955,6 +1124,10 @@
         (overrides->store_nostore_set == 0)
         ? base->store_nostore
         : overrides->store_nostore;
+    ps->offlinemode  =
+        (overrides->offlinemode_set == 0)
+        ? base->offlinemode
+        : overrides->offlinemode;
     ps->ignore_headers =
         (overrides->ignore_headers_set == CACHE_IGNORE_HEADERS_UNSET)
         ? base->ignore_headers
@@ -1014,6 +1187,19 @@
     return NULL;
 }
 
+static const char *set_cache_offline_mode(cmd_parms *parms, void *dummy,
+                                           int flag)
+{
+    cache_server_conf *conf;
+
+    conf =
+        (cache_server_conf *)ap_get_module_config(parms->server->module_config,
+                                                  &cache_module);
+    conf->offlinemode = flag;
+    conf->offlinemode_set = 1;
+    return NULL;
+}
+
 static const char *add_ignore_header(cmd_parms *parms, void *dummy,
                                      const char *header)
 {
@@ -1153,12 +1339,18 @@
 static int cache_post_config(apr_pool_t *p, apr_pool_t *plog,
                              apr_pool_t *ptemp, server_rec *s)
 {
+    cache_server_conf *conf;
     /* This is the means by which unusual (non-unix) os's may find alternate
      * means to run a given command (e.g. shebang/registry parsing on Win32)
      */
     cache_generate_key = APR_RETRIEVE_OPTIONAL_FN(ap_cache_generate_key);
+    conf = (cache_server_conf *) ap_get_module_config(s->module_config,
+                                                      &cache_module);
+
     if (!cache_generate_key) {
-        cache_generate_key = cache_generate_key_default;
+        /** If offline enabled, use filter, else fallback to mod_cache default 
*/
+        cache_generate_key = (conf->offlinemode ? 
cache_off_generate_key_default :
+                                                  cache_generate_key_default);
     }
     return OK;
 }
@@ -1198,6 +1390,9 @@
     AP_INIT_FLAG("CacheStoreNoStore", set_cache_store_nostore,
                  NULL, RSRC_CONF,
                  "Ignore 'Cache-Control: no-store' and store sensitive 
content"),
+    AP_INIT_FLAG("CacheOfflineBrowsingEnabled", set_cache_offline_mode,
+                 NULL, RSRC_CONF,
+                 "Enable offline browsing mode (cache all data possible)"),
     AP_INIT_ITERATE("CacheIgnoreHeaders", add_ignore_header, NULL, RSRC_CONF,
                     "A space separated list of headers that should not be "
                     "stored by the cache"),
Index: mod_cache.h
===================================================================
--- mod_cache.h (revision 443423)
+++ mod_cache.h (working copy)
@@ -24,7 +24,7 @@
  */
 
 #ifndef MOD_CACHE_H
-#define MOD_CACHE_H 
+#define MOD_CACHE_H
 
 #define CORE_PRIVATE
 
@@ -88,7 +88,7 @@
 #define DEFAULT_CACHE_EXPIRE    MSEC_ONE_HR
 #define DEFAULT_CACHE_LMFACTOR  (0.1)
 
-/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and 
+/* Create a set of PROXY_DECLARE(type), PROXY_DECLARE_NONSTD(type) and
  * PROXY_DECLARE_DATA with appropriate export and import tags for the platform
  */
 #if !defined(WIN32)
@@ -135,7 +135,7 @@
     int factor_set;
     /** ignore the last-modified header when deciding to cache this request */
     int no_last_mod_ignore_set;
-    int no_last_mod_ignore; 
+    int no_last_mod_ignore;
     /** ignore client's requests for uncached responses */
     int ignorecachecontrol;
     int ignorecachecontrol_set;
@@ -145,6 +145,9 @@
     /** ignore Cache-Control: no-store header from client or server */
     int store_nostore;
     int store_nostore_set;
+    /** Offline browsing mode */
+    int offlinemode;
+    int offlinemode_set;
     /** store the headers that should not be stored in the cache */
     apr_array_header_t *ignore_headers;
     /* flag if CacheIgnoreHeader has been set */
@@ -159,16 +162,16 @@
 /* cache info information */
 typedef struct cache_info cache_info;
 struct cache_info {
-    /** 
+    /**
      * HTTP status code of the cached entity. Though not neccessarily the
-     * status code finally issued to the request. 
+     * status code finally issued to the request.
      */
-    int status; 
-    /** 
-     * the original time corresponding to the 'Date:' header of the request 
-     * served 
+    int status;
+    /**
+     * the original time corresponding to the 'Date:' header of the request
+     * served
      */
-    apr_time_t date; 
+    apr_time_t date;
     /** a time when the cached entity is due to expire */
     apr_time_t expire;
     /** r->request_time from the same request */
@@ -179,11 +182,11 @@
 
 /* cache handle information */
 
-/* XXX TODO On the next structure change/MMN bump, 
+/* XXX TODO On the next structure change/MMN bump,
  * count must become an apr_off_t, representing
  * the potential size of disk cached objects.
  * Then dig for
- * "XXX Bad Temporary Cast - see cache_object_t notes" 
+ * "XXX Bad Temporary Cast - see cache_object_t notes"
  */
 typedef struct cache_object cache_object_t;
 struct cache_object {
@@ -196,6 +199,8 @@
     apr_size_t count;   /* Number of body bytes written to the cache so far */
     int complete;
     apr_uint32_t refcount;  /* refcount and bit flag to cleanup object */
+    int reopen;                            /* CACHE_OUT enabled after 
CACHE_SAVE
+                                           So reopen filehandles */
 };
 
 typedef struct cache_handle cache_handle_t;
@@ -212,7 +217,7 @@
     apr_status_t (*store_headers)(cache_handle_t *h, request_rec *r, 
cache_info *i);
     apr_status_t (*store_body)(cache_handle_t *h, request_rec *r, 
apr_bucket_brigade *b);
     apr_status_t (*recall_headers) (cache_handle_t *h, request_rec *r);
-    apr_status_t (*recall_body) (cache_handle_t *h, apr_pool_t *p, 
apr_bucket_brigade *bb); 
+    apr_status_t (*recall_body) (cache_handle_t *h, apr_pool_t *p, 
apr_bucket_brigade *bb);
     int (*create_entity) (cache_handle_t *h, request_rec *r,
                            const char *urlkey, apr_off_t len);
     int (*open_entity) (cache_handle_t *h, request_rec *r,
@@ -245,6 +250,8 @@
     apr_time_t exp;                     /* expiration */
     apr_time_t lastmod;                 /* last-modified time */
     cache_info *info;                   /* current cache info */
+    int reopen;                          /* CACHE_OUT enabled after CACHE_SAVE
+                                            So reopen filehandles */
     ap_filter_t *remove_url_filter;     /* Enable us to remove the filter */
 } cache_request_rec;
 
@@ -274,8 +281,8 @@
 
 CACHE_DECLARE(apr_time_t) ap_cache_hex2usec(const char *x);
 CACHE_DECLARE(void) ap_cache_usec2hex(apr_time_t j, char *y);
-CACHE_DECLARE(char *) ap_cache_generate_name(apr_pool_t *p, int dirlevels, 
-                                             int dirlength, 
+CACHE_DECLARE(char *) ap_cache_generate_name(apr_pool_t *p, int dirlevels,
+                                             int dirlength,
                                              const char *name);
 CACHE_DECLARE(cache_provider_list *)ap_cache_get_providers(request_rec *r, 
cache_server_conf *conf, apr_uri_t uri);
 CACHE_DECLARE(int) ap_cache_liststr(apr_pool_t *p, const char *list,
@@ -311,8 +318,7 @@
 */
 
 /* hooks */
-
-/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and 
+/* Create a set of CACHE_DECLARE(type), CACHE_DECLARE_NONSTD(type) and
  * CACHE_DECLARE_DATA with appropriate export and import tags for the platform
  */
 #if !defined(WIN32)
@@ -333,10 +339,9 @@
 #define CACHE_DECLARE_DATA             __declspec(dllimport)
 #endif
 
-APR_DECLARE_OPTIONAL_FN(apr_status_t, 
-                        ap_cache_generate_key, 
+APR_DECLARE_OPTIONAL_FN(apr_status_t,
+                        ap_cache_generate_key,
                         (request_rec *r, apr_pool_t*p, char**key ));
 
-
 #endif /*MOD_CACHE_H*/
 /** @} */
Index: mod_disk_cache.c
===================================================================
--- mod_disk_cache.c    (revision 443423)
+++ mod_disk_cache.c    (working copy)
@@ -408,6 +408,9 @@
     flags = APR_READ|APR_BINARY|APR_BUFFERED;
     rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
     if (rc != APR_SUCCESS) {
+                    ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "disk_cache: Error opening %s.", dobj->hdrsfile);
+
         return DECLINED;
     }
 
@@ -422,9 +425,14 @@
         len = sizeof(expire);
         apr_file_read_full(dobj->hfd, &expire, len, &len);
 
-        if (expire < r->request_time) {
+    /** don't expire files for me, thanks */
+  /**      if (expire < r->request_time) {
+                        ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "disk_cache: Header file %s expired at %d [%d].",
+                         dobj->hdrsfile, expire, r->request_time);
+
             return DECLINED;
-        }
+        }*/
 
         varray = apr_array_make(r->pool, 5, sizeof(char*));
         rc = read_array(r, varray, dobj->hfd);
@@ -437,6 +445,9 @@
         apr_file_close(dobj->hfd);
 
         nkey = regen_key(r->pool, r->headers_in, varray, key);
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
+                         "disk_cache: Changed %s to %s.",
+                         key, nkey);
 
         dobj->hashfile = NULL;
         dobj->prefix = dobj->hdrsfile;
@@ -445,6 +456,9 @@
         flags = APR_READ|APR_BINARY|APR_BUFFERED;
         rc = apr_file_open(&dobj->hfd, dobj->hdrsfile, flags, 0, r->pool);
         if (rc != APR_SUCCESS) {
+                        ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,
+                         "disk_cache(2): Cannot open data file %s.", 
dobj->hdrsfile);
+
             return DECLINED;
         }
     }
@@ -476,7 +490,8 @@
 #endif
     rc = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, r->pool);
     if (rc != APR_SUCCESS) {
-        /* XXX: Log message */
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+             "disk_cache: Unable to open data file %s.", dobj->datafile);
         return DECLINED;
     }
 
@@ -488,7 +503,8 @@
     /* Read the bytes to setup the cache_info fields */
     rc = file_cache_recall_mydata(dobj->hfd, info, dobj, r);
     if (rc != APR_SUCCESS) {
-        /* XXX log message */
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+             "disk_cache: Unable to extract data file %s.", dobj->datafile);
         return DECLINED;
     }
 
@@ -773,8 +789,23 @@
 
 static apr_status_t recall_body(cache_handle_t *h, apr_pool_t *p, 
apr_bucket_brigade *bb)
 {
+    apr_status_t rv;
+    int flags;
     apr_bucket *e;
     disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj->vobj;
+    
+    if (h->cache_obj->reopen) {
+        /* Re-open the data file */
+        flags = APR_READ|APR_BINARY;
+#ifdef APR_SENDFILE_ENABLED
+        flags |= APR_SENDFILE_ENABLED;
+#endif
+        rv = apr_file_open(&dobj->fd, dobj->datafile, flags, 0, p);
+        if (rv != APR_SUCCESS) {
+            /* Handle error */
+            return rv;
+        }
+    }
 
     apr_brigade_insert_file(bb, dobj->fd, 0, dobj->file_size, p);
 
Index: cache_storage.c
===================================================================
--- cache_storage.c     (revision 443423)
+++ cache_storage.c     (working copy)
@@ -17,7 +17,12 @@
 #define CORE_PRIVATE
 
 #include "mod_cache.h"
+#include "apreq2/apreq.h"
+#include "apreq2/apreq_module_apache2.h"
+#include "apreq2/apreq_cookie.h"
+#define CACHE_COOKIE "Get-Cache-Entity"
 
+
 extern APR_OPTIONAL_FN_TYPE(ap_cache_generate_key) *cache_generate_key;
 
 extern module AP_MODULE_DECLARE_DATA cache_module;
@@ -186,9 +191,66 @@
     apr_status_t rv;
     cache_handle_t *h;
     char *key;
+    apreq_handle_t *req;
+    apreq_cookie_t *cookie;
     cache_request_rec *cache = (cache_request_rec *)
                          ap_get_module_config(r->request_config, 
&cache_module);
 
+
+    /** Test for cookie */
+    req = apreq_handle_apache2(r);
+    cookie = apreq_cookie(req, CACHE_COOKIE);
+    if (cookie) {
+        /** Use requested cookie */
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                r->server,
+                                "cache_getentity(): Requesting entity %s.",
+                                cookie->v.data);
+
+        h = apr_palloc(r->pool, sizeof(cache_handle_t));
+
+        list = cache->providers;
+
+        while (list) {
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                    r->server,
+                                    "cache_getentity(): Querying provider %s.",
+                                    list->provider_name);
+            switch ((rv = list->provider->open_entity(h, r, cookie->v.data))) {
+            case OK: {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                r->server,
+                                "cache_getentity(): Possible cache hit for 
%s.",
+                                cookie->v.data);
+
+                if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
+                    /* TODO: Handle this error */
+                    return DECLINED;
+                }
+
+                /** Set up cache information */
+                cache->provider = list->provider;
+                cache->provider_name = list->provider_name;
+                ap_cache_accept_headers(h, r, 0);
+                cache->handle = h;
+                return OK;
+            }
+            case DECLINED: {
+                /* try again with next cache type */
+                list = list->next;
+                continue;
+                }
+            default: {
+                /* oo-er! an error */
+                return rv;
+                }
+            }
+        }
+        /** We couldn't find the requested entity - TODO: abort return a 404 */
+        return DECLINED;
+    }
+    /** Normal processing */
+
     rv = cache_generate_key(r, r->pool, &key);
     if (rv != APR_SUCCESS) {
         return rv;
@@ -199,11 +261,20 @@
     list = cache->providers;
 
     while (list) {
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                r->server,
+                                "cache_select_url(): Querying provider %s.",
+                                list->provider_name);
         switch ((rv = list->provider->open_entity(h, r, key))) {
         case OK: {
             char *vary = NULL;
             int fresh;
 
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS,
+                                r->server,
+                                "cache_select_url(): Possible cache hit for 
%s.",
+                                key);
+
             if (list->provider->recall_headers(h, r) != APR_SUCCESS) {
                 /* TODO: Handle this error */
                 return DECLINED;

Reply via email to