On 18 Oct 2010, at 8:36 AM, Ruediger Pluem wrote:

If the client specified no-cache, the cache steps down completely, and
the client is guaranteed fresh content from the source server (as per
the RFC). The cache will only revalidate if you say max-age=X (or other
valid caching tokens).

I cannot follow that. The above code means that cache_check_freshness returns
0 and thus we replace the client supplied conditionals with our own:

mod_cache used to do this, but not any more as it's an RFC violation. Now, if the client supplies no-cache, this is caught by a function called ap_cache_check_allowed(), which if not allowed, causes no attempt to be made to open or touch an existing cached file. Look further upwards in cache_select(), you'll see the call to ap_cache_check_allowed():

int cache_select(cache_request_rec *cache, request_rec *r)
{
    cache_provider_list *list;
    apr_status_t rv;
    cache_handle_t *h;

    if (!cache) {
        /* This should never happen */
        ap_log_rerror(APLOG_MARK, APLOG_ERR, APR_EGENERAL, r,
                "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;
        }
    }

    if (!ap_cache_check_allowed(cache, r)) {
        return DECLINED;
    }

    /* go through the cache types till we get a match */
    h = apr_palloc(r->pool, sizeof(cache_handle_t));

    list = cache->providers;

    while (list) {
        switch ((rv = list->provider->open_entity(h, r, cache->key))) {

We do it the same way in both cases, the remove_url filter is removed in
the ap_die() case on line  1630, and in the mod_proxy case inside
save_filter on line 791.

Sorry for being a PITA, but IMHO we do not:

If a bug exists, we must find it :)

URL: 
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_cache.c?rev=1021946&r1=1021945&r2=1021946&view=diff
= = = = = = = = ======================================================================
--- httpd/httpd/trunk/modules/cache/mod_cache.c (original)
+++ httpd/httpd/trunk/modules/cache/mod_cache.c Tue Oct 12 22:54:06 2010
@@ -779,6 +779,61 @@ static int cache_save_filter(ap_filter_t

    dconf = ap_get_module_config(r->per_dir_config, &cache_module);

+    /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
+ * If a cache receives a 5xx response while attempting to revalidate an + * entry, it MAY either forward this response to the requesting client, + * or act as if the server failed to respond. In the latter case, it MAY
+     * return a previously received response unless the cached entry
+ * includes the "must-revalidate" cache-control directive (see section
+     * 14.9).
+     *
+ * This covers the case where an error was generated behind us, for example
+     * by a backend server via mod_proxy.
+     */
+ if (dconf->stale_on_error && r->status >= HTTP_INTERNAL_SERVER_ERROR) {
+
+        ap_remove_output_filter(cache->remove_url_filter);
+
+        if (cache->stale_handle && !ap_cache_liststr(
+                NULL,
+ apr_table_get(cache->stale_handle->resp_hdrs, "Cache-Control"),
+                "must-revalidate", NULL)) {
+            const char *warn_head;


Modified: httpd/httpd/trunk/modules/cache/mod_cache.c
URL: 
http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/cache/mod_cache.c?rev=1021546&r1=1021545&r2=1021546&view=diff
= = = = = = = = ======================================================================
--- httpd/httpd/trunk/modules/cache/mod_cache.c (original)
+++ httpd/httpd/trunk/modules/cache/mod_cache.c Mon Oct 11 23:32:56 2010

+    /* RFC2616 13.8 Errors or Incomplete Response Cache Behavior:
+ * If a cache receives a 5xx response while attempting to revalidate an + * entry, it MAY either forward this response to the requesting client, + * or act as if the server failed to respond. In the latter case, it MAY
+     * return a previously received response unless the cached entry
+ * includes the "must-revalidate" cache-control directive (see section
+     * 14.9).
+     */
+    apr_pool_userdata_get(&dummy, CACHE_CTX_KEY, r->pool);
+    if (dummy) {
+        cache_request_rec *cache = (cache_request_rec *) dummy;
+
+ if (cache->stale_handle && cache->save_filter && ! ap_cache_liststr(
+                NULL, apr_table_get(cache->stale_handle->resp_hdrs,
+                        "Cache-Control"), "must-revalidate", NULL)) {
+            const char *warn_head;
+
+ /* morph the current save filter into the out filter, and serve from
+             * cache.
+             */
+            cache->handle = cache->stale_handle;
+            if (r->main) {
+ cache->save_filter->frec = cache_out_subreq_filter_handle;
+            }
+            else {
+                cache->save_filter->frec = cache_out_filter_handle;
+            }
+
+            r->output_filters = cache->save_filter;
+
+            r->err_headers_out = cache->stale_handle->resp_hdrs;
+
+            /* add a stale warning */
+            warn_head = apr_table_get(r->err_headers_out, "Warning");
+            if ((warn_head == NULL) || ((warn_head != NULL)
+                    && (ap_strstr_c(warn_head, "110") == NULL))) {
+                apr_table_mergen(r->err_headers_out, "Warning",
+                        "110 Response is stale");
+            }
+
+            ap_remove_output_filter(cache->remove_url_filter);
+
+

Please compare the code. In the mod_proxy case we call

ap_remove_output_filter(cache->remove_url_filter);

if (dconf->stale_on_error && r->status >= HTTP_INTERNAL_SERVER_ERROR)

In the ap_die case we require

+ if (cache->stale_handle && cache->save_filter && ! ap_cache_liststr(
+                NULL, apr_table_get(cache->stale_handle->resp_hdrs,
+                        "Cache-Control"), "must-revalidate", NULL)) {
+

These additional conditions are required in the second if in the mod_proxy case.

So if I'm understanding you correctly, the issue is with the different handling of the check for "must-revalidate"?

Hmmm.

If "must-revalidate" is present in the original cached response, in both cases, we don't serve the stale-cached-content, which is correct according to the definition of must-revalidate.

This makes the invalidation of the stale response academic from the client's point of view, as this stale response, which contains the "must-revalidate", will never be served to the client while the error persists.

This is however inconsistent as you've pointed out, and needs to be fixed.

From the server's point of view, invalidating the cached entry means that when the server comes back, it will need to serve a 200 response from scratch, and if our server is returning 5xx errors this is less than ideal. I think we should remove the remove_url filter in both cases, so that we're consistent, like below.

Does this make sense?

Index: modules/cache/mod_cache.c
===================================================================
--- modules/cache/mod_cache.c   (revision 1023462)
+++ modules/cache/mod_cache.c   (working copy)
@@ -1595,6 +1595,8 @@
     if (dummy) {
         cache_request_rec *cache = (cache_request_rec *) dummy;

+        ap_remove_output_filter(cache->remove_url_filter);
+
         if (cache->stale_handle && cache->save_filter
&& !cache->stale_handle->cache_obj- >info.control.must_revalidate && !cache->stale_handle->cache_obj- >info.control.proxy_revalidate) {
@@ -1627,8 +1629,6 @@
                         "110 Response is stale");
             }

-            ap_remove_output_filter(cache->remove_url_filter);
-
             cache_run_cache_status(
                     cache->handle,
                     r,

Regards,
Graham
--

Reply via email to