On Feb 10, 2008, at 2:12 PM, Dirk-Willem van Gulik wrote:

Right now (only) mod_disk_cache is doing the 'right(tm)' thing w.r.t. to Vary - the other caches (mod_memcache as part of the distribution and a handful of memcached, distcache and commercial cache modules) are just acting on the URI (key).

Bringing them in line involves a bit of cut-and-paste from disk-cache.

So I am wondering if I should do the following -- but want to have some feedback of the folks who have been spending the last years on this -- as I may have missed something fundamental:

- Move sundry like array_alphasort, tokens_to_array up into cache_utils or similar.

- Perhaps add a extra function vector called 'make_key' -- which can be NULL to the cache_provider; which understands most of rfc2616; including case (insensitivity). In short - we'll propably end up with a struct which details the relevant headers, if they are int, date, case-insensitive or sensitive. Which allows us then to always
        do the right thing.

- When we call store_* already pre-fillout cache_info (or add a param) which does the right things around checking for Vary. And perhaps even a precooked version of
        headers_out/in.

Before I embark on an experiment (without much design/planning) -- any thoughts ? Or has someone already done most of this and/or designed it properly ?

Thanks,

Dw

See below for some aloud thinking -- note that this is not yet well thought out (all I did was try to minimize the overlap between disk_cache, memcached, distcache and some commercial thing -- and tried to move as much RFC2616 knowledge into mod_cache.c*).

-       internal ap_cache_cacheable_hdr to hold all the RFC 2616
        non cachable headers info as before.

-       ap_cache_cacheable_hdrs_out introduced as per earlier discussion
        which knows about error headers and mandatory content setting.

-       ap_cache_cacheable_hdrs_in introduced - which has an optional
        argument to further curtail the headers returned by just those
        in the 'Vary'.

-       ap_normalize_vary_to_array() introduced. Which can help
        caching storage modules to create a key across the Vary
        relevant fields, if any.

        => given that they all have to create this -- wondering if
        we should make this part of the cache_info struct already.

-       Likewise a ap_generate_vary_key() which understands
        vary if/when needed.

Below more or less cleans mod_disk_cache completly of having to understand http (or any protocol aspect) - but for ap_generate_vary_key() -- but it is not yet HTTP neutral (i.e. a IMAP protocol module would have to overwrite things it has not yet XS to).

Thoughts,

Dw.


*: not yet nearly enough - i.e. lacking the whole case (in)sensitive, date, etc stuff.

Index: cache_util.c
===================================================================
--- cache_util.c        (revision 620288)
+++ cache_util.c        (working copy)
@@ -571,10 +573,10 @@
     return apr_pstrdup(p, hashfile);
 }

-/* Create a new table consisting of those elements from an input
+/* Create a new table consisting of those elements from an
  * headers table that are allowed to be stored in a cache.
  */
-CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs(apr_pool_t *pool,
apr_table_t *t,
                                                         server_rec *s)
 {
@@ -582,12 +584,17 @@
     char **header;
     int i;

+    if (t == NULL) {
+       return NULL;
+    };
+
     /* Make a copy of the headers, and remove from
      * the copy any hop-by-hop headers, as defined in Section
      * 13.5.1 of RFC 2616
      */
     apr_table_t *headers_out;
     headers_out = apr_table_copy(pool, t);
+
     apr_table_unset(headers_out, "Connection");
     apr_table_unset(headers_out, "Keep-Alive");
     apr_table_unset(headers_out, "Proxy-Authenticate");
@@ -599,6 +606,7 @@

     conf = (cache_server_conf *)ap_get_module_config(s->module_config,
                                                      &cache_module);
+
     /* Remove the user defined headers set with CacheIgnoreHeaders.
* This may break RFC 2616 compliance on behalf of the administrator.
      */
@@ -608,3 +616,170 @@
     }
     return headers_out;
 }
+
+/* Create a new table consisting of those elements from an input
+ * headers table that are allowed to be stored in a cache. Optionally
+ * filtered to just the fields passed in the vary_filter array.
+ *
+ * @return a table with the valid input headers or NULL if none are relevant.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_in(request_rec * r, apr_array_header_t * vary_filter)
+{
+       apr_table_t *headers_in, * vary_filtered;
+        int i;
+
+       if (r->headers_in == NULL)
+               return NULL;
+
+ headers_in = ap_cache_cacheable_hdrs(r->pool, r->headers_in, r->server);
+       
+       if (!vary_filter)
+               return apr_is_empty_table(headers_in) ? NULL: headers_in;
+
+        /* The vary array is most propably the smallest of the two - su
+        * only copy just those fields across.
+        */
+       vary_filtered = apr_table_make(r->pool, vary_filter->nelts);
+        for (i = 0; i < vary_filter->nelts; i++) {
+                const char *key = ((const char**)vary_filter->elts)[i];
+ const char * val = apr_table_get(headers_in, key); /* XXX: Not case insensitive */
+                if (val)
+                        apr_table_add(vary_filtered, key, val);
+        };
+
+       return apr_is_empty_table(vary_filtered) ? NULL : vary_filtered;
+}
+
+/* Create a new table consisting of those elements from an output
+ * headers table that are allowed to be stored in a cache;
+ * ensure there is a content type and capture any errors.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(request_rec * r)
+{
+       apr_table_t *headers_out;
+
+       headers_out = ap_cache_cacheable_hdrs(r->pool, r->headers_out,
+                                                  r->server);
+
+        if (!apr_table_get(headers_out, "Content-Type")
+            && r->content_type) {
+            apr_table_setn(headers_out, "Content-Type",
+                           ap_make_content_type(r, r->content_type));
+        }
+
+        headers_out = apr_table_overlay(r->pool, headers_out,
+                                        r->err_headers_out);
+
+       return headers_out;
+}
+
+static int array_alphasort(const void *fn1, const void *fn2)
+{
+    return strcmp(*(char**)fn1, *(char**)fn2);
+}
+
+static void vary_tokens_to_array(apr_pool_t *p, const char *data,
+                            apr_array_header_t *arr)
+{
+    char *token;
+
+    while ((token = ap_get_list_item(p, &data)) != NULL) {
+        *((const char **) apr_array_push(arr)) = token;
+    }
+
+    qsort((void *) arr->elts, arr->nelts,
+         sizeof(char *), array_alphasort);
+}
+
+/* Given a header table; extract any Vary present and return
+ * the values in a predictable way (i.e. Sort it so that
+ * "Vary: A, B" and "Vary: B, A" are stored the same; and take
+ * care of any case sensitive issues. Onb exit the array will
+ * either be NULL (no VARYance) or a sorted array.
+ *
+ * @return vary-array or NULL, if no variance
+ */
+CACHE_DECLARE(apr_array_header_t*) ap_normalize_vary_to_array(apr_pool_t *p, apr_table_t * headers)
+{
+       apr_array_header_t* varray = NULL;
+       const char *vary;
+       
+       if ((headers) && ((vary= apr_table_get(headers, "Vary")) != NULL))
+       {
+           varray = apr_array_make(p, 6, sizeof(char*));
+            vary_tokens_to_array(p, vary, varray);
+       }
+       
+       return varray;
+}
+
+/* Generate a new key based on the normal key (URI) and normalized values from + * the Vary array. The vary array is assumed to be in a stable order and format.
+ */
+CACHE_DECLARE(const char*) ap_generate_vary_key(apr_pool_t *p, apr_table_t * input_headers, + apr_array_header_t *varray, const char *oldkey)
+{
+    struct iovec *iov;
+    int i, k;
+    int nvec;
+    const char *header;
+    const char **elts;
+
+    if (varray == NULL);
+       return oldkey;
+
+    nvec = (varray->nelts * 2) + 1;
+    iov = apr_palloc(p, sizeof(struct iovec) * nvec);
+    elts = (const char **) varray->elts;
+
+    /* TODO:
+     *    - Handle multiple-value headers better. (sort them?)
+     *    - Handle Case in-sensitive Values better.
+ * This isn't the end of the world, since it just lowers the cache
+     *        hit rate, but it would be nice to fix.
+     *
+ * The majority are case insenstive if they are values (encoding etc).
+     * Most of rfc2616 is case insensitive on header contents.
+     *
+ * So the better solution may be to identify headers which should be
+     * treated case-sensitive?
+     *  HTTP URI's (3.2.3) [host and scheme are insensitive]
+     *  HTTP method (5.1.1)
+     *  HTTP-date values (3.3.1)
+     *  3.7 Media Types [exerpt]
+     *     The type, subtype, and parameter attribute names are case-
+ * insensitive. Parameter values might or might not be case- sensitive,
+     *     depending on the semantics of the parameter name.
+     *  4.20 Except [exerpt]
+ * Comparison of expectation values is case-insensitive for unquoted + * tokens (including the 100-continue token), and is case- sensitive for
+     *     quoted-string expectation-extensions.
+        *
+ * XX we are currently concatenating the values. With some puzzling one could + * set a header value which looks like the 'next/previous' value - and hence + * cause a value which is not the 'real' one. This may be a security risk. + * Ideally we should use a special value which cannot occur in a header + * legally (or is escaped/wacked). Given that (at least) a Header cannot
+        *     contain a space or ':' - may be an idea to insert those.
+     */
+    for(i=0, k=0; i < varray->nelts; i++) {
+               /* Note that we're assuming that the case if the Vary fields 
matches
+                * the case of the actual headers.
+                */
+        header = apr_table_get(input_headers, elts[i]);
+        if (!header) {
+            header = "";
+        }
+        iov[k].iov_base = (char*) elts[i];
+        iov[k].iov_len = strlen(elts[i]);
+        k++;
+        iov[k].iov_base = (char*) header;
+        iov[k].iov_len = strlen(header);
+        k++;
+    }
+    iov[k].iov_base = (char*) oldkey;
+    iov[k].iov_len = strlen(oldkey);
+    k++;
+
+    return apr_pstrcatv(p, iov, k, NULL);
+}
Index: mod_mem_cache.c
===================================================================
--- mod_mem_cache.c     (revision 620288)
+++ mod_mem_cache.c     (working copy)
@@ -605,17 +605,8 @@
     mobj->req_hdrs = deep_table_copy(mobj->pool, r->headers_in);

     /* Precompute how much storage we need to hold the headers */
-    headers_out = ap_cache_cacheable_hdrs_out(r->pool, r->headers_out,
-                                              r->server);
+    headers_out = ap_cache_cacheable_hdrs_out(r);

-    /* If not set in headers_out, set Content-Type */
-    if (!apr_table_get(headers_out, "Content-Type")
-        && r->content_type) {
-        apr_table_setn(headers_out, "Content-Type",
-                       ap_make_content_type(r, r->content_type));
-    }
-
- headers_out = apr_table_overlay(r->pool, headers_out, r- >err_headers_out);
     mobj->header_out = deep_table_copy(mobj->pool, headers_out);

     /* Init the info struct */
Index: mod_cache.c
===================================================================
--- mod_cache.c (revision 620288)
+++ mod_cache.c (working copy)
@@ -752,10 +752,12 @@
          * However, before doing that, we need to first merge in
          * err_headers_out and we also need to strip any hop-by-hop
          * headers that might have snuck in.
          */
         r->headers_out = apr_table_overlay(r->pool, r->headers_out,
                                            r->err_headers_out);
- r->headers_out = ap_cache_cacheable_hdrs_out(r->pool, r- >headers_out, + r->headers_out = ap_cache_cacheable_hdrs(r->pool, r- >headers_out,
                                                      r->server);
         apr_table_clear(r->err_headers_out);

Index: mod_cache.h
===================================================================
--- mod_cache.h (revision 620288)
+++ mod_cache.h (working copy)
@@ -288,13 +288,25 @@
                                     const char *key, char **val);
CACHE_DECLARE(const char *)ap_cache_tokstr(apr_pool_t *p, const char *list, const char **str);

-/* Create a new table consisting of those elements from a request_rec's
- * headers_out that are allowed to be stored in a cache
+/* Create a new table consisting of those elements from an
+ * headers table that are allowed to be stored in a cache.
  */
-CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool,
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs(apr_pool_t *pool,
apr_table_t *t, server_rec *s);

+/* Create a new table consisting of those elements from an input
+ * headers table that are allowed to be stored in a cache. Optionally
+ * filtered to just the fields in the vary_filter.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_in(request_rec * r, apr_array_header_t * vary_filter);
+
+/* Create a new table consisting of those elements from an output
+ * headers table that are allowed to be stored in a cache;
+ * ensure there is a content type and capture any errors.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(request_rec * r);
+
 /**
  * cache_storage.c
  */
@@ -316,6 +328,21 @@
apr_status_t cache_recall_entity_body(cache_handle_t *h, apr_pool_t *p, apr_bucket_brigade *bb);
 */

+/* Given a header table; extract any Vary present and return
+ * the values in a predictable way (i.e. Sort it so that
+ * "Vary: A, B" and "Vary: B, A" are stored the same; and take
+ * care of any case sensitive issues. Onb exit the array will
+ * either be NULL (no VARYance) or a sorted array.
+ *
+ * @return vary-array or NULL, if no variance
+ */
+CACHE_DECLARE(apr_array_header_t*) ap_normalize_vary_to_array(apr_pool_t *p, apr_table_t * headers);
+
+/* Generate a new key based on the normal key (URI) and normalized values from + * the Vary array. The vary array is assumed to be in a stable order and format.
+ */
+CACHE_DECLARE(const char*) ap_generate_vary_key(apr_pool_t *p, apr_table_t * input_headers, + apr_array_header_t *varray, const char *oldkey);
 /* hooks */

 APR_DECLARE_OPTIONAL_FN(apr_status_t,
Index: mod_disk_cache.c
===================================================================
--- mod_disk_cache.c    (revision 620288)
+++ mod_disk_cache.c    (working copy)
@@ -102,7 +102,7 @@
      }
 }

-static void mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool) +static apr_status_t mkdir_structure(disk_cache_conf *conf, const char *file, apr_pool_t *pool)
 {
     apr_status_t rv;
     char *p;
@@ -116,11 +116,12 @@
         rv = apr_dir_make(file,
                           APR_UREAD|APR_UWRITE|APR_UEXECUTE, pool);
         if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
-            /* XXX */
+            return rv;
         }
         *p = '/';
         ++p;
     }
+    return APR_SUCCESS;
 }

 /* htcacheclean may remove directories underneath us.
@@ -141,7 +142,9 @@
             /* 1000 micro-seconds aka 0.001 seconds. */
             apr_sleep(1000);

-            mkdir_structure(conf, dest, pool);
+            rv = mkdir_structure(conf, dest, pool);
+           if (rv != APR_SUCCESS)
+               continue;

             rv = apr_file_rename(src, dest, pool);
         }
@@ -241,81 +244,6 @@
     return APR_SUCCESS;
 }

-static const char* regen_key(apr_pool_t *p, apr_table_t *headers,
- apr_array_header_t *varray, const char *oldkey)
-{
-    struct iovec *iov;
-    int i, k;
-    int nvec;
-    const char *header;
-    const char **elts;
-
-    nvec = (varray->nelts * 2) + 1;
-    iov = apr_palloc(p, sizeof(struct iovec) * nvec);
-    elts = (const char **) varray->elts;
-
-    /* TODO:
-     *    - Handle multiple-value headers better. (sort them?)
-     *    - Handle Case in-sensitive Values better.
- * This isn't the end of the world, since it just lowers the cache
-     *        hit rate, but it would be nice to fix.
-     *
- * The majority are case insenstive if they are values (encoding etc).
-     * Most of rfc2616 is case insensitive on header contents.
-     *
- * So the better solution may be to identify headers which should be
-     * treated case-sensitive?
-     *  HTTP URI's (3.2.3) [host and scheme are insensitive]
-     *  HTTP method (5.1.1)
-     *  HTTP-date values (3.3.1)
-     *  3.7 Media Types [exerpt]
-     *     The type, subtype, and parameter attribute names are case-
- * insensitive. Parameter values might or might not be case- sensitive,
-     *     depending on the semantics of the parameter name.
-     *  4.20 Except [exerpt]
- * Comparison of expectation values is case-insensitive for unquoted - * tokens (including the 100-continue token), and is case- sensitive for
-     *     quoted-string expectation-extensions.
-     */
-
-    for(i=0, k=0; i < varray->nelts; i++) {
-        header = apr_table_get(headers, elts[i]);
-        if (!header) {
-            header = "";
-        }
-        iov[k].iov_base = (char*) elts[i];
-        iov[k].iov_len = strlen(elts[i]);
-        k++;
-        iov[k].iov_base = (char*) header;
-        iov[k].iov_len = strlen(header);
-        k++;
-    }
-    iov[k].iov_base = (char*) oldkey;
-    iov[k].iov_len = strlen(oldkey);
-    k++;
-
-    return apr_pstrcatv(p, iov, k, NULL);
-}
-
-static int array_alphasort(const void *fn1, const void *fn2)
-{
-    return strcmp(*(char**)fn1, *(char**)fn2);
-}
-
-static void tokens_to_array(apr_pool_t *p, const char *data,
-                            apr_array_header_t *arr)
-{
-    char *token;
-
-    while ((token = ap_get_list_item(p, &data)) != NULL) {
-        *((const char **) apr_array_push(arr)) = token;
-    }
-
- /* Sort it so that "Vary: A, B" and "Vary: B, A" are stored the same. */
-    qsort((void *) arr->elts, arr->nelts,
-         sizeof(char *), array_alphasort);
-}
-
 /*
  * Hook and mod_cache callback functions
  */
@@ -432,7 +360,7 @@
         }
         apr_file_close(dobj->hfd);

-        nkey = regen_key(r->pool, r->headers_in, varray, key);
+ nkey = ap_generate_vary_key(r->pool, r->headers_in, varray, key);

         dobj->hashfile = NULL;
         dobj->prefix = dobj->hdrsfile;
@@ -472,7 +400,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_ERR, rc, r->server,
+                 "disk_cache: Cannot open %f",  dobj->datafile);
         return DECLINED;
     }

@@ -484,7 +413,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_ERR, rc, r->server,
+ "disk_cache: Cannot read cache_info from %f", dobj- >hdrsfile);
         return DECLINED;
     }

@@ -821,6 +751,7 @@
     apr_status_t rv;
     apr_size_t amt;
disk_cache_object_t *dobj = (disk_cache_object_t*) h->cache_obj- >vobj;
+    apr_array_header_t* varray;

     disk_cache_info_t disk_info;
     struct iovec iov[2];
@@ -828,15 +759,18 @@
/* This is flaky... we need to manage the cache_info differently */
     h->cache_obj->info = *info;

-    if (r->headers_out) {
-        const char *tmp;
-
-        tmp = apr_table_get(r->headers_out, "Vary");
-
-        if (tmp) {
-            apr_array_header_t* varray;
+ /* XXX it may make sense to make this part of cache_info -- as every
+     *     sane module will need to calculate this. Potentially
+     *     allong with a ap_generate_vary_key() value as well.
+     *
+     *     Or alternatively insist on the create_/open_ to already
+     *     prepare for this eventuality.
+     */
+    varray  = ap_normalize_vary_to_array(r->pool, r->headers_out);
+    if (varray) {
             apr_uint32_t format = VARY_FORMAT_VERSION;
-
+           const char * nkey;
+                       
             /* If we were initially opened as a vary format, rollback
* that internal state for the moment so we can recreate the
              * vary format hints in the appropriate directory.
@@ -846,7 +780,12 @@
                 dobj->prefix = NULL;
             }

-            mkdir_structure(conf, dobj->hdrsfile, r->pool);
+            rv = mkdir_structure(conf, dobj->hdrsfile, r->pool);
+            if (rv != APR_SUCCESS) {
+               ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
+ "disk_cache: cannot create path for %s", dobj- >hdrsfile);
+                return rv;
+            }

             rv = apr_file_mktemp(&dobj->tfd, dobj->tempfile,
APR_CREATE | APR_WRITE | APR_BINARY | APR_EXCL,
@@ -862,9 +801,6 @@
             amt = sizeof(info->expire);
             apr_file_write(dobj->tfd, &info->expire, &amt);

-            varray = apr_array_make(r->pool, 6, sizeof(char*));
-            tokens_to_array(r->pool, tmp, varray);
-
             store_array(dobj->tfd, varray);

             apr_file_close(dobj->tfd);
@@ -882,15 +818,13 @@
             }

dobj->tempfile = apr_pstrcat(r->pool, conf->cache_root, AP_TEMPFILE, NULL); - tmp = regen_key(r->pool, r->headers_in, varray, dobj- >name); + nkey = ap_generate_vary_key(r->pool, r->headers_in, varray, dobj->name);
             dobj->prefix = dobj->hdrsfile;
             dobj->hashfile = NULL;
-            dobj->datafile = data_file(r->pool, conf, dobj, tmp);
-            dobj->hdrsfile = header_file(r->pool, conf, dobj, tmp);
+            dobj->datafile = data_file(r->pool, conf, dobj, nkey);
+            dobj->hdrsfile = header_file(r->pool, conf, dobj, nkey);
         }
-    }

-
     rv = apr_file_mktemp(&dobj->hfd, dobj->tempfile,
                          APR_CREATE | APR_WRITE | APR_BINARY |
                          APR_BUFFERED | APR_EXCL, r->pool);
@@ -916,41 +850,40 @@

rv = apr_file_writev(dobj->hfd, (const struct iovec *) &iov, 2, &amt);
     if (rv != APR_SUCCESS) {
+        apr_file_close(dobj->hfd); /* flush and close */
+       apr_file_remove(dobj->tempfile, r->pool);
         return rv;
     }

     if (r->headers_out) {
         apr_table_t *headers_out;

- headers_out = ap_cache_cacheable_hdrs_out(r->pool, r- >headers_out,
-                                                  r->server);
+        headers_out = ap_cache_cacheable_hdrs_out(r);

-        if (!apr_table_get(headers_out, "Content-Type")
-            && r->content_type) {
-            apr_table_setn(headers_out, "Content-Type",
-                           ap_make_content_type(r, r->content_type));
-        }
-
-        headers_out = apr_table_overlay(r->pool, headers_out,
-                                        r->err_headers_out);
         rv = store_table(dobj->hfd, headers_out);
         if (rv != APR_SUCCESS) {
+           apr_file_close(dobj->hfd); /* flush and close */
+           apr_file_remove(dobj->tempfile, r->pool);
             return rv;
         }
     }

- /* Parse the vary header and dump those fields from the headers_in. */ - /* FIXME: Make call to the same thing cache_select calls to crack Vary. */
-    if (r->headers_in) {
+    /* Parse the vary header and dump those fields from the headers_in.
+     * But only do so if needed - i.e. there is a Vary - and then limit
+     * it to the headers needed to validate the Vary.
+     */
+    if (r->headers_in && varray) {
         apr_table_t *headers_in;
-
- headers_in = ap_cache_cacheable_hdrs_out(r->pool, r- >headers_in,
-                                                 r->server);
+        headers_in = ap_cache_cacheable_hdrs_in(r, varray);
+        if (headers_in) {
         rv = store_table(dobj->hfd, headers_in);
         if (rv != APR_SUCCESS) {
+                    apr_file_close(dobj->hfd); /* flush and close */
+                    apr_file_remove(dobj->tempfile, r->pool);
             return rv;
         }
     }
+    }

     apr_file_close(dobj->hfd); /* flush and close */

@@ -960,8 +893,14 @@
      */
     rv = apr_file_remove(dobj->hdrsfile, r->pool);
     if (rv != APR_SUCCESS) {
-        mkdir_structure(conf, dobj->hdrsfile, r->pool);
+        rv = mkdir_structure(conf, dobj->hdrsfile, r->pool);
+            if (rv != APR_SUCCESS) {
+               ap_log_error(APLOG_MARK, APLOG_WARNING, rv, r->server,
+ "disk_cache: cannot create path for %s", dobj- >hdrsfile);
+               apr_file_remove(dobj->tempfile, r->pool);
+                return rv;
     }
+    }

rv = safe_file_rename(conf, dobj->tempfile, dobj->hdrsfile, r- >pool);
     if (rv != APR_SUCCESS) {

Reply via email to