HTTP Compliance: add appropriate Warnings if serving a stale hit. Per RFC 2616, we MUST add "110 Response is stale" Warning if serving a stale reply for any reason, including configured overrides. We MUST add "111 Revalidation failed" Warning if serving a stale reply because an attempt to revalidate the response failed, due to an inability to reach the server. The patch adds a new stale_if_hit request flag, which is set in refreshCheckHTTP() when entry freshness is calculated. refreshCheckHTTP() is now called in offline mode, to set stale_if_hit properly. We check for the offline mode before returning from refreshCheckHTTP() to preserve the original logic. refreshCheckHTTP() is no longer called for internal requests, to avoid setting of stale_if_hit flag. It did not do anything important for internal requests anyway. Co-Advisor test cases: test_case/rfc2616/noSrv-hit-stale-max-age-req test_case/rfc2616/ccReqDirMsg-max-stale-warning === modified file 'src/HttpHeader.cc' --- src/HttpHeader.cc 2010-09-10 21:12:54 +0000 +++ src/HttpHeader.cc 2010-09-21 16:17:28 +0000 @@ -1195,6 +1195,14 @@ mb.clean(); } +void +HttpHeader::putWarning(const int code, const char *const text) +{ + char buf[512]; + snprintf(buf, sizeof(buf), "%i %s \"%s\"", code, visible_appname_string, text); + putStr(HDR_WARNING, buf); +} + /* add extension header (these fields are not parsed/analyzed/joined, etc.) */ void HttpHeader::putExt(const char *name, const char *value) === modified file 'src/HttpHeader.h' --- src/HttpHeader.h 2010-08-25 00:14:25 +0000 +++ src/HttpHeader.h 2010-09-19 04:37:31 +0000 @@ -238,6 +238,7 @@ void putContRange(const HttpHdrContRange * cr); void putRange(const HttpHdrRange * range); void putSc(HttpHdrSc *sc); + void putWarning(const int code, const char *const text); ///< add a Warning header void putExt(const char *name, const char *value); int getInt(http_hdr_type id) const; int64_t getInt64(http_hdr_type id) const; === modified file 'src/client_side_reply.cc' --- src/client_side_reply.cc 2010-09-19 04:32:46 +0000 +++ src/client_side_reply.cc 2010-09-21 16:21:01 +0000 @@ -518,7 +518,7 @@ ) { http->logType = LOG_TCP_NEGATIVE_HIT; sendMoreData(result); - } else if (!Config.onoff.offline && refreshCheckHTTP(e, r) && !http->flags.internal) { + } else if (!http->flags.internal && refreshCheckHTTP(e, r)) { debugs(88, 5, "clientCacheHit: in refreshCheck() block"); /* * We hold a stale copy; it needs to be validated @@ -1309,6 +1309,13 @@ } } + // add Warnings required by RFC 2616 if serving a stale hit + if (http->request->flags.stale_if_hit && logTypeIsATcpHit(http->logType)) { + hdr->putWarning(110, "Response is stale"); + if (http->request->flags.need_validation) + hdr->putWarning(111, "Revalidation failed"); + } + /* Filter unproxyable authentication types */ if (http->logType != LOG_TCP_DENIED && === modified file 'src/refresh.cc' --- src/refresh.cc 2010-09-19 04:33:46 +0000 +++ src/refresh.cc 2010-09-21 16:16:31 +0000 @@ -434,6 +434,20 @@ return 1; } +/// whether reply is stale if it is a hit +static bool +refreshIsStaleIfHit(const int reason) +{ + switch (reason) { + case FRESH_MIN_RULE: + case FRESH_LMFACTOR_RULE: + case FRESH_EXPIRES: + return false; + default: + return true; + } +} + /* refreshCheck... functions below are protocol-specific wrappers around * refreshCheck() function above */ @@ -443,7 +457,8 @@ int reason = refreshCheck(entry, request, 0); refreshCounts[rcHTTP].total++; refreshCounts[rcHTTP].status[reason]++; - return (reason < 200) ? 0 : 1; + request->flags.stale_if_hit = refreshIsStaleIfHit(reason); + return (Config.onoff.offline || reason < 200) ? 0 : 1; } int === modified file 'src/structs.h' --- src/structs.h 2010-09-19 04:32:46 +0000 +++ src/structs.h 2010-09-19 04:37:31 +0000 @@ -1008,7 +1008,7 @@ struct request_flags { - request_flags(): range(0),nocache(0),ims(0),auth(0),cachable(0),hierarchical(0),loopdetect(0),proxy_keepalive(0),proxying(0),refresh(0),redirected(0),need_validation(0),fail_on_validation_err(0),accelerated(0),ignore_cc(0),intercepted(0),spoof_client_ip(0),internal(0),internalclient(0),must_keepalive(0),chunked_reply(0),stream_error(0),destinationIPLookedUp_(0) { + request_flags(): range(0),nocache(0),ims(0),auth(0),cachable(0),hierarchical(0),loopdetect(0),proxy_keepalive(0),proxying(0),refresh(0),redirected(0),need_validation(0),fail_on_validation_err(0),stale_if_hit(0),accelerated(0),ignore_cc(0),intercepted(0),spoof_client_ip(0),internal(0),internalclient(0),must_keepalive(0),chunked_reply(0),stream_error(0),destinationIPLookedUp_(0) { #if USE_HTTP_VIOLATIONS nocache_hack = 0; #endif @@ -1031,6 +1031,7 @@ unsigned int redirected:1; unsigned int need_validation:1; unsigned int fail_on_validation_err:1; ///< whether we should fail if validation fails + unsigned int stale_if_hit:1; ///< reply is stale if it is a hit #if USE_HTTP_VIOLATIONS unsigned int nocache_hack:1; /* for changing/ignoring no-cache requests */ #endif