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 : "<none>") << "'");
 
     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) */


