On 10/17/2010 05:57 PM, Graham Leggett wrote:
> On 17 Oct 2010, at 2:29 PM, Ruediger Pluem wrote:
>
>> Are you sure? If the client said Cache-Control: no-cache, we IMHO treat
>> the cached entry as stale and revalidate:
>>
>> From cache_util.c:
>>
>> /* This value comes from the client's initial request. */
>> cc_req = apr_table_get(r->headers_in, "Cache-Control");
>> pragma = apr_table_get(r->headers_in, "Pragma");
>>
>>
>> if (ap_cache_liststr(NULL, pragma, "no-cache", NULL)
>> || ap_cache_liststr(NULL, cc_req, "no-cache", NULL)) {
>>
>> if (!conf->ignorecachecontrol) {
>> /* Treat as stale, causing revalidation */
>> return 0;
>> }
>>
>> ap_log_error(APLOG_MARK, APLOG_INFO, 0, r->server,
>> "Incoming request is asking for a uncached version
>> of "
>> "%s, but we have been configured to ignore it and "
>> "serve a cached response anyway",
>> r->unparsed_uri);
>> }
>
> 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:
/* Is our cached response fresh enough? */
fresh = cache_check_freshness(h, cache, r);
if (!fresh) {
const char *etag, *lastmod;
/* Cache-Control: only-if-cached and revalidation required, try
* the next provider
*/
if (cache->control_in.only_if_cached) {
/* try again with next cache type */
list = list->next;
continue;
}
/* set aside the stale entry for accessing later */
cache->stale_headers = apr_table_copy(r->pool,
r->headers_in);
cache->stale_handle = h;
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_SUCCESS, r,
"Cached response for %s isn't fresh. Adding/replacing "
"conditional request headers.", r->uri);
/* We can only revalidate with our own conditionals: remove the
* conditions from the original request.
*/
apr_table_unset(r->headers_in, "If-Match");
apr_table_unset(r->headers_in, "If-Modified-Since");
apr_table_unset(r->headers_in, "If-None-Match");
apr_table_unset(r->headers_in, "If-Range");
apr_table_unset(r->headers_in, "If-Unmodified-Since");
etag = apr_table_get(h->resp_hdrs, "ETag");
lastmod = apr_table_get(h->resp_hdrs, "Last-Modified");
>
> What we do differently, is that instead of invalidating the existing
> content with our 5xx response to the no-cache request, we leave it alone
> completely, so that the next request, where the client hasn't asked for
> no-cache, can be served the stale content, instead of an error because
> the stale content had been blown away by the previous no-cache request.
I understand this purpose and I do not question it. It makes a lot of sense.
>
>>> existing file at all, and cache->stale_handle would always be NULL.
>>
>> Even then remains the question: Why doing it differently in both
>> case (ap_die, mod_proxy)?
>
> 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:
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.
Regards
Rüdiger