]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/HttpHdrCc.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / HttpHdrCc.cc
index 98de14a6acd54e8c00caca7e3b80b97be61471c4..b95b133e615c330e5c1ab6783d286763df17c4c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -9,52 +9,50 @@
 /* DEBUG: section 65    HTTP Cache Control Header */
 
 #include "squid.h"
+#include "base/LookupTable.h"
 #include "HttpHdrCc.h"
 #include "HttpHeader.h"
 #include "HttpHeaderFieldStat.h"
 #include "HttpHeaderStat.h"
 #include "HttpHeaderTools.h"
-#include "SBuf.h"
+#include "sbuf/SBuf.h"
 #include "StatHist.h"
 #include "Store.h"
 #include "StrList.h"
+#include "util.h"
 
 #include <map>
-
-/* a row in the table used for parsing cache control header and statistics */
-typedef struct {
-    const char *name;
-    http_hdr_cc_type id;
-    HttpHeaderFieldStat stat;
-} HttpHeaderCcFields;
-
-/* order must match that of enum http_hdr_cc_type. The constraint is verified at initialization time */
-static HttpHeaderCcFields CcAttrs[CC_ENUM_END] = {
-    {"public", CC_PUBLIC},
-    {"private", CC_PRIVATE},
-    {"no-cache", CC_NO_CACHE},
-    {"no-store", CC_NO_STORE},
-    {"no-transform", CC_NO_TRANSFORM},
-    {"must-revalidate", CC_MUST_REVALIDATE},
-    {"proxy-revalidate", CC_PROXY_REVALIDATE},
-    {"max-age", CC_MAX_AGE},
-    {"s-maxage", CC_S_MAXAGE},
-    {"max-stale", CC_MAX_STALE},
-    {"min-fresh", CC_MIN_FRESH},
-    {"only-if-cached", CC_ONLY_IF_CACHED},
-    {"stale-if-error", CC_STALE_IF_ERROR},
-    {"Other,", CC_OTHER} /* ',' will protect from matches */
+#include <vector>
+#include <ostream>
+
+// invariant: row[j].id == j
+static LookupTable<HttpHdrCcType>::Record CcAttrs[] = {
+    {"public", HttpHdrCcType::CC_PUBLIC},
+    {"private", HttpHdrCcType::CC_PRIVATE},
+    {"no-cache", HttpHdrCcType::CC_NO_CACHE},
+    {"no-store", HttpHdrCcType::CC_NO_STORE},
+    {"no-transform", HttpHdrCcType::CC_NO_TRANSFORM},
+    {"must-revalidate", HttpHdrCcType::CC_MUST_REVALIDATE},
+    {"proxy-revalidate", HttpHdrCcType::CC_PROXY_REVALIDATE},
+    {"max-age", HttpHdrCcType::CC_MAX_AGE},
+    {"s-maxage", HttpHdrCcType::CC_S_MAXAGE},
+    {"max-stale", HttpHdrCcType::CC_MAX_STALE},
+    {"min-fresh", HttpHdrCcType::CC_MIN_FRESH},
+    {"only-if-cached", HttpHdrCcType::CC_ONLY_IF_CACHED},
+    {"stale-if-error", HttpHdrCcType::CC_STALE_IF_ERROR},
+    {"immutable", HttpHdrCcType::CC_IMMUTABLE},
+    {"Other,", HttpHdrCcType::CC_OTHER}, /* ',' will protect from matches */
+    {nullptr, HttpHdrCcType::CC_ENUM_END}
 };
-
-/// Map an header name to its type, to expedite parsing
-typedef std::map<const SBuf,http_hdr_cc_type> CcNameToIdMap_t;
-static CcNameToIdMap_t CcNameToIdMap;
+LookupTable<HttpHdrCcType> ccLookupTable(HttpHdrCcType::CC_OTHER,CcAttrs);
+std::vector<HttpHeaderFieldStat> ccHeaderStats(HttpHdrCcType::CC_ENUM_END);
 
 /// used to walk a table of http_header_cc_type structs
-http_hdr_cc_type &operator++ (http_hdr_cc_type &aHeader)
+static HttpHdrCcType &
+operator++ (HttpHdrCcType &aHeader)
 {
     int tmp = (int)aHeader;
-    aHeader = (http_hdr_cc_type)(++tmp);
+    aHeader = (HttpHdrCcType)(++tmp);
     return aHeader;
 }
 
@@ -62,35 +60,43 @@ http_hdr_cc_type &operator++ (http_hdr_cc_type &aHeader)
 void
 httpHdrCcInitModule(void)
 {
-    /* build lookup and accounting structures */
-    for (int32_t i = 0; i < CC_ENUM_END; ++i) {
-        const HttpHeaderCcFields &f=CcAttrs[i];
-        assert(i == f.id); /* verify assumption: the id is the key into the array */
-        const SBuf k(f.name);
-        CcNameToIdMap[k]=f.id;
+    // check invariant on initialization table
+    for (unsigned int j = 0; CcAttrs[j].name != nullptr; ++j) {
+        assert(static_cast<decltype(j)>(CcAttrs[j].id) == j);
     }
 }
 
-/// Module cleanup hook.
 void
-httpHdrCcCleanModule(void)
+HttpHdrCc::clear()
 {
-    // HdrCcNameToIdMap is self-cleaning
+    *this=HttpHdrCc();
 }
 
+/// set a data member to a new value, and set the corresponding mask-bit.
+/// if setting is false, then the mask-bit is cleared.
 void
-HttpHdrCc::clear()
+HttpHdrCc::setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting)
 {
-    *this=HttpHdrCc();
+    if (setting) {
+        if (new_value < 0) {
+            debugs(65, 3, "rejecting negative-value Cache-Control directive " << hdr
+                   << " value " << new_value);
+            return;
+        }
+    } else {
+        new_value = -1; //rely on the convention that "unknown" is -1
+    }
+
+    value = new_value;
+    setMask(hdr,setting);
 }
 
 bool
 HttpHdrCc::parse(const String & str)
 {
     const char *item;
-    const char *p;             /* '=' parameter */
-    const char *pos = NULL;
-    http_hdr_cc_type type;
+    const char *p;      /* '=' parameter */
+    const char *pos = nullptr;
     int ilen;
     int nlen;
 
@@ -107,17 +113,13 @@ HttpHdrCc::parse(const String & str)
         }
 
         /* find type */
-        const CcNameToIdMap_t::const_iterator i=CcNameToIdMap.find(SBuf(item,nlen));
-        if (i==CcNameToIdMap.end())
-            type=CC_OTHER;
-        else
-            type=i->second;
+        const HttpHdrCcType type = ccLookupTable.lookup(SBuf(item,nlen));
 
         // ignore known duplicate directives
         if (isSet(type)) {
-            if (type != CC_OTHER) {
+            if (type != HttpHdrCcType::CC_OTHER) {
                 debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
-                ++CcAttrs[type].stat.repCount;
+                ++ ccHeaderStats[type].repCount;
                 continue;
             }
         }
@@ -125,7 +127,7 @@ HttpHdrCc::parse(const String & str)
         /* special-case-parsing and attribute-setting */
         switch (type) {
 
-        case CC_MAX_AGE:
+        case HttpHdrCcType::CC_MAX_AGE:
             if (!p || !httpHeaderParseInt(p, &max_age) || max_age < 0) {
                 debugs(65, 2, "cc: invalid max-age specs near '" << item << "'");
                 clearMaxAge();
@@ -134,7 +136,7 @@ HttpHdrCc::parse(const String & str)
             }
             break;
 
-        case CC_S_MAXAGE:
+        case HttpHdrCcType::CC_S_MAXAGE:
             if (!p || !httpHeaderParseInt(p, &s_maxage) || s_maxage < 0) {
                 debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'");
                 clearSMaxAge();
@@ -143,7 +145,7 @@ HttpHdrCc::parse(const String & str)
             }
             break;
 
-        case CC_MAX_STALE:
+        case HttpHdrCcType::CC_MAX_STALE:
             if (!p || !httpHeaderParseInt(p, &max_stale) || max_stale < 0) {
                 debugs(65, 2, "cc: max-stale directive is valid without value");
                 maxStale(MAX_STALE_ANY);
@@ -152,7 +154,7 @@ HttpHdrCc::parse(const String & str)
             }
             break;
 
-        case CC_MIN_FRESH:
+        case HttpHdrCcType::CC_MIN_FRESH:
             if (!p || !httpHeaderParseInt(p, &min_fresh) || min_fresh < 0) {
                 debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'");
                 clearMinFresh();
@@ -161,7 +163,7 @@ HttpHdrCc::parse(const String & str)
             }
             break;
 
-        case CC_STALE_IF_ERROR:
+        case HttpHdrCcType::CC_STALE_IF_ERROR:
             if (!p || !httpHeaderParseInt(p, &stale_if_error) || stale_if_error < 0) {
                 debugs(65, 2, "cc: invalid stale-if-error specs near '" << item << "'");
                 clearStaleIfError();
@@ -170,7 +172,7 @@ HttpHdrCc::parse(const String & str)
             }
             break;
 
-        case CC_PRIVATE: {
+        case HttpHdrCcType::CC_PRIVATE: {
             String temp;
             if (!p)  {
                 // Value parameter is optional.
@@ -185,7 +187,7 @@ HttpHdrCc::parse(const String & str)
         }
         break;
 
-        case CC_NO_CACHE: {
+        case HttpHdrCcType::CC_NO_CACHE: {
             String temp;
             if (!p) {
                 // On Requests, missing value parameter is expected syntax.
@@ -203,26 +205,29 @@ HttpHdrCc::parse(const String & str)
         }
         break;
 
-        case CC_PUBLIC:
+        case HttpHdrCcType::CC_PUBLIC:
             Public(true);
             break;
-        case CC_NO_STORE:
+        case HttpHdrCcType::CC_NO_STORE:
             noStore(true);
             break;
-        case CC_NO_TRANSFORM:
+        case HttpHdrCcType::CC_NO_TRANSFORM:
             noTransform(true);
             break;
-        case CC_MUST_REVALIDATE:
+        case HttpHdrCcType::CC_MUST_REVALIDATE:
             mustRevalidate(true);
             break;
-        case CC_PROXY_REVALIDATE:
+        case HttpHdrCcType::CC_PROXY_REVALIDATE:
             proxyRevalidate(true);
             break;
-        case CC_ONLY_IF_CACHED:
+        case HttpHdrCcType::CC_ONLY_IF_CACHED:
             onlyIfCached(true);
             break;
+        case HttpHdrCcType::CC_IMMUTABLE:
+            Immutable(true);
+            break;
 
-        case CC_OTHER:
+        case HttpHdrCcType::CC_OTHER:
             if (other.size())
                 other.append(", ");
 
@@ -239,41 +244,68 @@ HttpHdrCc::parse(const String & str)
 }
 
 void
-HttpHdrCc::packInto(Packer * p) const
+HttpHdrCc::packInto(Packable * p) const
 {
     // optimization: if the mask is empty do nothing
     if (mask==0)
         return;
 
-    http_hdr_cc_type flag;
+    HttpHdrCcType flag;
     int pcount = 0;
     assert(p);
 
-    for (flag = CC_PUBLIC; flag < CC_ENUM_END; ++flag) {
-        if (isSet(flag) && flag != CC_OTHER) {
+    for (flag = HttpHdrCcType::CC_PUBLIC; flag < HttpHdrCcType::CC_ENUM_END; ++flag) {
+        if (isSet(flag) && flag != HttpHdrCcType::CC_OTHER) {
 
             /* print option name for all options */
-            packerPrintf(p, (pcount ? ", %s": "%s") , CcAttrs[flag].name);
+            p->appendf((pcount ? ", %s": "%s"), CcAttrs[flag].name);
 
             /* for all options having values, "=value" after the name */
             switch (flag) {
-            case CC_MAX_AGE:
-                packerPrintf(p, "=%d", (int) maxAge());
+            case HttpHdrCcType::CC_PUBLIC:
+                break;
+            case HttpHdrCcType::CC_PRIVATE:
+                if (private_.size())
+                    p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(private_));
+                break;
+
+            case HttpHdrCcType::CC_NO_CACHE:
+                if (no_cache.size())
+                    p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(no_cache));
+                break;
+            case HttpHdrCcType::CC_NO_STORE:
+                break;
+            case HttpHdrCcType::CC_NO_TRANSFORM:
+                break;
+            case HttpHdrCcType::CC_MUST_REVALIDATE:
+                break;
+            case HttpHdrCcType::CC_PROXY_REVALIDATE:
+                break;
+            case HttpHdrCcType::CC_MAX_AGE:
+                p->appendf("=%d", max_age);
                 break;
-            case CC_S_MAXAGE:
-                packerPrintf(p, "=%d", (int) sMaxAge());
+            case HttpHdrCcType::CC_S_MAXAGE:
+                p->appendf("=%d", s_maxage);
                 break;
-            case CC_MAX_STALE:
+            case HttpHdrCcType::CC_MAX_STALE:
                 /* max-stale's value is optional.
                   If we didn't receive it, don't send it */
-                if (maxStale()!=MAX_STALE_ANY)
-                    packerPrintf(p, "=%d", (int) maxStale());
+                if (max_stale != MAX_STALE_ANY)
+                    p->appendf("=%d", max_stale);
                 break;
-            case CC_MIN_FRESH:
-                packerPrintf(p, "=%d", (int) minFresh());
+            case HttpHdrCcType::CC_MIN_FRESH:
+                p->appendf("=%d", min_fresh);
                 break;
-            default:
-                /* do nothing, directive was already printed */
+            case HttpHdrCcType::CC_ONLY_IF_CACHED:
+                break;
+            case HttpHdrCcType::CC_STALE_IF_ERROR:
+                p->appendf("=%d", stale_if_error);
+                break;
+            case HttpHdrCcType::CC_IMMUTABLE:
+                break;
+            case HttpHdrCcType::CC_OTHER:
+            case HttpHdrCcType::CC_ENUM_END:
+                // done below after the loop
                 break;
             }
 
@@ -282,27 +314,25 @@ HttpHdrCc::packInto(Packer * p) const
     }
 
     if (other.size() != 0)
-        packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH),
-                     SQUIDSTRINGPRINT(other));
+        p->appendf((pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(other));
 }
 
 void
 httpHdrCcUpdateStats(const HttpHdrCc * cc, StatHist * hist)
 {
-    http_hdr_cc_type c;
     assert(cc);
 
-    for (c = CC_PUBLIC; c < CC_ENUM_END; ++c)
+    for (HttpHdrCcType c = HttpHdrCcType::CC_PUBLIC; c < HttpHdrCcType::CC_ENUM_END; ++c)
         if (cc->isSet(c))
             hist->count(c);
 }
 
 void
-httpHdrCcStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
+httpHdrCcStatDumper(StoreEntry * sentry, int, double val, double, int count)
 {
-    extern const HttpHeaderStat *dump_stat;    /* argh! */
-    const int id = (int) val;
-    const int valid_id = id >= 0 && id < CC_ENUM_END;
+    extern const HttpHeaderStat *dump_stat; /* argh! */
+    const int id = static_cast<int>(val);
+    const bool valid_id = id >= 0 && id < static_cast<int>(HttpHdrCcType::CC_ENUM_END);
     const char *name = valid_id ? CcAttrs[id].name : "INVALID";
 
     if (count || valid_id)
@@ -310,6 +340,14 @@ httpHdrCcStatDumper(StoreEntry * sentry, int idx, double val, double size, int c
                           id, name, count, xdiv(count, dump_stat->ccParsedCount));
 }
 
-#if !_USE_INLINE_
-#include "HttpHdrCc.cci"
-#endif
+std::ostream &
+operator<< (std::ostream &s, HttpHdrCcType c)
+{
+    const unsigned char ic = static_cast<int>(c);
+    if (c < HttpHdrCcType::CC_ENUM_END)
+        s << CcAttrs[ic].name << '[' << ic << ']' ;
+    else
+        s << "*invalid hdrcc* [" << ic << ']';
+    return s;
+}
+