HTTP Compliance: support requests with Cache-Control: min-fresh. Added min-fresh directive support for Cache-Control header. The directive is handled in refreshCheck() by incrementing age and check_time by min-fresh value. Co-Advisor test case: test_case/rfc2616/ccReqDirMsg-min-fresh-obey === modified file 'src/HttpHdrCc.cc' --- src/HttpHdrCc.cc 2010-07-30 20:41:26 +0000 +++ src/HttpHdrCc.cc 2010-09-28 03:18:01 +0000 @@ -34,79 +34,80 @@ */ #include "squid.h" #include "Store.h" #include "HttpHeader.h" /* this table is used for parsing cache control header */ static const HttpHeaderFieldAttrs CcAttrs[CC_ENUM_END] = { {"public", (http_hdr_type)CC_PUBLIC}, {"private", (http_hdr_type)CC_PRIVATE}, {"no-cache", (http_hdr_type)CC_NO_CACHE}, {"no-store", (http_hdr_type)CC_NO_STORE}, {"no-transform", (http_hdr_type)CC_NO_TRANSFORM}, {"must-revalidate", (http_hdr_type)CC_MUST_REVALIDATE}, {"proxy-revalidate", (http_hdr_type)CC_PROXY_REVALIDATE}, {"only-if-cached", (http_hdr_type)CC_ONLY_IF_CACHED}, {"max-age", (http_hdr_type)CC_MAX_AGE}, {"s-maxage", (http_hdr_type)CC_S_MAXAGE}, {"max-stale", (http_hdr_type)CC_MAX_STALE}, + {"min-fresh", (http_hdr_type)CC_MIN_FRESH}, {"Other,", (http_hdr_type)CC_OTHER} /* ',' will protect from matches */ }; HttpHeaderFieldInfo *CcFieldsInfo = NULL; http_hdr_cc_type &operator++ (http_hdr_cc_type &aHeader) { int tmp = (int)aHeader; aHeader = (http_hdr_cc_type)(++tmp); return aHeader; } /* local prototypes */ static int httpHdrCcParseInit(HttpHdrCc * cc, const String * str); /* module initialization */ void httpHdrCcInitModule(void) { CcFieldsInfo = httpHeaderBuildFieldsInfo(CcAttrs, CC_ENUM_END); } void httpHdrCcCleanModule(void) { httpHeaderDestroyFieldsInfo(CcFieldsInfo, CC_ENUM_END); CcFieldsInfo = NULL; } /* implementation */ HttpHdrCc * httpHdrCcCreate(void) { HttpHdrCc *cc = (HttpHdrCc *)memAllocate(MEM_HTTP_HDR_CC); - cc->max_age = cc->s_maxage = cc->max_stale = -1; + cc->max_age = cc->s_maxage = cc->max_stale = cc->min_fresh = -1; return cc; } /* creates an cc object from a 0-terminating string */ HttpHdrCc * httpHdrCcParseCreate(const String * str) { HttpHdrCc *cc = httpHdrCcCreate(); if (!httpHdrCcParseInit(cc, str)) { httpHdrCcDestroy(cc); cc = NULL; } return cc; } /* parses a 0-terminating string and inits cc */ static int httpHdrCcParseInit(HttpHdrCc * cc, const String * str) @@ -164,107 +165,121 @@ httpHdrCcParseInit(HttpHdrCc * cc, const case CC_S_MAXAGE: if (!p || !httpHeaderParseInt(p, &cc->s_maxage)) { debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'"); cc->s_maxage = -1; EBIT_CLR(cc->mask, type); } break; case CC_MAX_STALE: if (!p || !httpHeaderParseInt(p, &cc->max_stale)) { debugs(65, 2, "cc: max-stale directive is valid without value"); cc->max_stale = -1; } break; + case CC_MIN_FRESH: + + if (!p || !httpHeaderParseInt(p, &cc->min_fresh)) { + debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'"); + cc->min_fresh = -1; + EBIT_CLR(cc->mask, type); + } + + break; + case CC_OTHER: if (cc->other.size()) cc->other.append(", "); cc->other.append(item, ilen); break; default: /* note that we ignore most of '=' specs (RFCVIOLATION) */ break; } } return cc->mask != 0; } void httpHdrCcDestroy(HttpHdrCc * cc) { assert(cc); if (cc->other.defined()) cc->other.clean(); memFree(cc, MEM_HTTP_HDR_CC); } HttpHdrCc * httpHdrCcDup(const HttpHdrCc * cc) { HttpHdrCc *dup; assert(cc); dup = httpHdrCcCreate(); dup->mask = cc->mask; dup->max_age = cc->max_age; dup->s_maxage = cc->s_maxage; dup->max_stale = cc->max_stale; + dup->min_fresh = cc->min_fresh; return dup; } void httpHdrCcPackInto(const HttpHdrCc * cc, Packer * p) { http_hdr_cc_type flag; int pcount = 0; assert(cc && p); for (flag = CC_PUBLIC; flag < CC_ENUM_END; ++flag) { if (EBIT_TEST(cc->mask, flag) && flag != CC_OTHER) { /* print option name */ packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(CcFieldsInfo[flag].name)); /* handle options with values */ if (flag == CC_MAX_AGE) packerPrintf(p, "=%d", (int) cc->max_age); if (flag == CC_S_MAXAGE) packerPrintf(p, "=%d", (int) cc->s_maxage); if (flag == CC_MAX_STALE && cc->max_stale >= 0) packerPrintf(p, "=%d", (int) cc->max_stale); + if (flag == CC_MIN_FRESH) + packerPrintf(p, "=%d", (int) cc->min_fresh); + pcount++; } } if (cc->other.size() != 0) packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(cc->other)); } /* negative max_age will clean old max_Age setting */ void httpHdrCcSetMaxAge(HttpHdrCc * cc, int max_age) { assert(cc); cc->max_age = max_age; if (max_age >= 0) EBIT_SET(cc->mask, CC_MAX_AGE); else EBIT_CLR(cc->mask, CC_MAX_AGE); === modified file 'src/enums.h' --- src/enums.h 2010-09-01 08:18:47 +0000 +++ src/enums.h 2010-09-25 13:07:08 +0000 @@ -90,40 +90,41 @@ enum { typedef enum { PEER_NONE, PEER_SIBLING, PEER_PARENT, PEER_MULTICAST } peer_t; typedef enum { CC_BADHDR = -1, CC_PUBLIC = 0, CC_PRIVATE, CC_NO_CACHE, CC_NO_STORE, CC_NO_TRANSFORM, CC_MUST_REVALIDATE, CC_PROXY_REVALIDATE, CC_MAX_AGE, CC_S_MAXAGE, CC_MAX_STALE, + CC_MIN_FRESH, CC_ONLY_IF_CACHED, CC_OTHER, CC_ENUM_END } http_hdr_cc_type; typedef enum { SC_NO_STORE, SC_NO_STORE_REMOTE, SC_MAX_AGE, SC_CONTENT, SC_OTHER, SC_ENUM_END } http_hdr_sc_type; typedef enum _mem_status_t { NOT_IN_MEMORY, IN_MEMORY } mem_status_t; === modified file 'src/refresh.cc' --- src/refresh.cc 2010-05-14 09:47:03 +0000 +++ src/refresh.cc 2010-09-25 14:12:50 +0000 @@ -237,57 +237,69 @@ refreshCheck(const StoreEntry * entry, H stale_flags sf; if (entry->mem_obj) uri = entry->mem_obj->url; else if (request) uri = urlCanonical(request); debugs(22, 3, "refreshCheck: '" << (uri ? uri : "") << "'"); if (check_time > entry->timestamp) age = check_time - entry->timestamp; // FIXME: what to do when age < 0 or counter overflow? assert(age >= 0); R = uri ? refreshLimits(uri) : refreshUncompiledPattern("."); if (NULL == R) R = &DefaultRefresh; - memset(&sf, '\0', sizeof(sf)); - - staleness = refreshStaleness(entry, check_time, age, R, &sf); - - debugs(22, 3, "Staleness = " << staleness); - debugs(22, 3, "refreshCheck: Matched '" << R->pattern << " " << (int) R->min << " " << (int) (100.0 * R->pct) << "%% " << (int) R->max << "'"); - - debugs(22, 3, "refreshCheck: age = " << age); + debugs(22, 3, "\tage:\t" << age); debugs(22, 3, "\tcheck_time:\t" << mkrfc1123(check_time)); debugs(22, 3, "\tentry->timestamp:\t" << mkrfc1123(entry->timestamp)); + if (request && !request->flags.ignore_cc) { + const HttpHdrCc *const cc = request->cache_control; + if (cc && cc->min_fresh > 0) { + debugs(22, 3, "\tage + min-fresh:\t" << age << " + " << + cc->min_fresh << " = " << age + cc->min_fresh); + debugs(22, 3, "\tcheck_time + min-fresh:\t" << check_time << " + " + << cc->min_fresh << " = " << + mkrfc1123(check_time + cc->min_fresh)); + age += cc->min_fresh; + check_time += cc->min_fresh; + } + } + + memset(&sf, '\0', sizeof(sf)); + + staleness = refreshStaleness(entry, check_time, age, R, &sf); + + debugs(22, 3, "Staleness = " << staleness); + if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1 #if USE_HTTP_VIOLATIONS && !R->flags.ignore_must_revalidate #endif ) { debugs(22, 3, "refreshCheck: YES: Must revalidate stale response"); return STALE_MUST_REVALIDATE; } /* request-specific checks */ if (request && !request->flags.ignore_cc) { HttpHdrCc *cc = request->cache_control; if (request->flags.ims && (R->flags.refresh_ims || Config.onoff.refresh_all_ims)) { /* The clients no-cache header is changed into a IMS query */ debugs(22, 3, "refreshCheck: YES: refresh-ims"); return STALE_FORCED_RELOAD; } #if USE_HTTP_VIOLATIONS === modified file 'src/structs.h' --- src/structs.h 2010-09-14 07:37:38 +0000 +++ src/structs.h 2010-09-25 13:10:00 +0000 @@ -700,40 +700,41 @@ struct _HttpBody { #include "SquidString.h" /* http header extention field */ class HttpHdrExtField { String name; /* field-name from HTTP/1.1 (no column after name) */ String value; /* field-value from HTTP/1.1 */ }; /* http cache control header field */ class HttpHdrCc { public: int mask; int max_age; int s_maxage; int max_stale; + int min_fresh; String other; }; /* some fields can hold either time or etag specs (e.g. If-Range) */ struct _TimeOrTag { ETag tag; /* entity tag */ time_t time; int valid; /* true if struct is usable */ }; /* per field statistics */ class HttpHeaderFieldStat { public: HttpHeaderFieldStat() : aliveCount(0), seenCount(0), parsCount(0), errCount(0), repCount(0) {} int aliveCount; /* created but not destroyed (count) */