-
/*
- * $Id$
- *
- * DEBUG: section 90 HTTP Cache Control Header
- * AUTHOR: Alex Rousskov
- * Robert Collins (Surrogate-Control is derived from
- * Cache-Control).
- *
- * SQUID Web Proxy Cache http://www.squid-cache.org/
- * ----------------------------------------------------------
- *
- * Squid is the result of efforts by numerous individuals from
- * the Internet community; see the CONTRIBUTORS file for full
- * details. Many organizations have provided support for Squid's
- * development; see the SPONSORS file for full details. Squid is
- * Copyrighted (C) 2001 by the Regents of the University of
- * California; see the COPYRIGHT file for full details. Squid
- * incorporates software developed and/or copyrighted by other
- * sources; see the CREDITS file for full details.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
*/
+/* DEBUG: section 90 HTTP Cache Control Header */
+
#include "squid.h"
-#include "Store.h"
+#include "base/LookupTable.h"
+//#include "HttpHdrSc.h" // pulled in by HttpHdrScTarget.h
+#include "HttpHdrScTarget.h"
#include "HttpHeader.h"
-#include "HttpHdrSc.h"
+#include "HttpHeaderFieldStat.h"
+#include "HttpHeaderStat.h"
+#include "HttpHeaderTools.h"
+#include "Store.h"
+#include "StrList.h"
+#include "util.h"
-/* this table is used for parsing surrogate control header */
-static const HttpHeaderFieldAttrs ScAttrs[SC_ENUM_END] = {
- {"no-store", (http_hdr_type)SC_NO_STORE},
+#include <map>
+#include <vector>
- {"no-store-remote", (http_hdr_type)SC_NO_STORE_REMOTE},
- {"max-age", (http_hdr_type)SC_MAX_AGE},
- {"content", (http_hdr_type)SC_CONTENT},
- {"Other,", (http_hdr_type)SC_OTHER} /* ',' will protect from matches */
+/* this table is used for parsing surrogate control header */
+/* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
+// TODO: implement constraint
+static const LookupTable<http_hdr_sc_type>::Record ScAttrs[] {
+ {"no-store", SC_NO_STORE},
+ {"no-store-remote", SC_NO_STORE_REMOTE},
+ {"max-age", SC_MAX_AGE},
+ {"content", SC_CONTENT},
+ {"Other,", SC_OTHER}, /* ',' will protect from matches */
+ {nullptr, SC_ENUM_END} /* SC_ENUM_END taken as invalid value */
};
+LookupTable<http_hdr_sc_type> scLookupTable(SC_ENUM_END, ScAttrs);
+std::vector<HttpHeaderFieldStat> scHeaderStats(SC_ENUM_END);
-HttpHeaderFieldInfo *ScFieldsInfo = NULL;
-
+// used when iterating over flags
http_hdr_sc_type &operator++ (http_hdr_sc_type &aHeader)
{
- int tmp = (int)aHeader;
- aHeader = (http_hdr_sc_type)(++tmp);
+ int tmp = static_cast<int>(aHeader);
+ aHeader = static_cast<http_hdr_sc_type>(++tmp);
return aHeader;
}
-int operator - (http_hdr_sc_type const &anSc, http_hdr_sc_type const &anSc2)
-{
- return (int)anSc - (int)anSc2;
-}
-
-
-/* local prototypes */
-static int httpHdrScParseInit(HttpHdrSc * sc, const String * str);
-
-/* module initialization */
-
void
httpHdrScInitModule(void)
{
- ScFieldsInfo = httpHeaderBuildFieldsInfo(ScAttrs, SC_ENUM_END);
-}
-
-void
-httpHdrScCleanModule(void)
-{
- httpHeaderDestroyFieldsInfo(ScFieldsInfo, SC_ENUM_END);
- ScFieldsInfo = NULL;
+ // check invariant on ScAttrs
+ for (int i = 0; ScAttrs[i].name != nullptr; ++i)
+ assert(i == ScAttrs[i].id);
}
/* implementation */
-HttpHdrSc *
-httpHdrScCreate(void)
-{
- return new HttpHdrSc();
-}
-
/* creates an sc object from a 0-terminating string */
HttpHdrSc *
-httpHdrScParseCreate(const String * str)
+httpHdrScParseCreate(const String & str)
{
- HttpHdrSc *sc = httpHdrScCreate();
+ HttpHdrSc *sc = new HttpHdrSc();
- if (!httpHdrScParseInit(sc, str)) {
- httpHdrScDestroy(sc);
+ if (!sc->parse(&str)) {
+ delete sc;
sc = NULL;
}
}
/* parses a 0-terminating string and inits sc */
-static int
-httpHdrScParseInit(HttpHdrSc * sc, const String * str)
+bool
+HttpHdrSc::parse(const String * str)
{
+ HttpHdrSc * sc=this;
const char *item;
- const char *p; /* '=' parameter */
+ const char *p; /* '=' parameter */
const char *pos = NULL;
const char *target = NULL; /* ;foo */
const char *temp = NULL; /* temp buffer */
- int type;
+ http_hdr_sc_type type;
int ilen, vlen;
int initiallen;
HttpHdrScTarget *sct;
- assert(sc && str);
+ assert(str);
/* iterate through comma separated list */
if ((p = strchr(item, '=')) && (p - item < ilen)) {
vlen = ilen - (p + 1 - item);
ilen = p - item;
- p++;
+ ++p;
}
- /* decrease ilen to still match the token for ';' qualified non '=' statments */
- else if ((p = strchr(item, ';')) && (p - item < ilen))
- ilen = p++ - item;
+ /* decrease ilen to still match the token for ';' qualified non '=' statements */
+ else if ((p = strchr(item, ';')) && (p - item < ilen)) {
+ ilen = p - item;
+ ++p;
+ }
/* find type */
- type = httpHeaderIdByName(item, ilen,
- ScFieldsInfo, SC_ENUM_END);
+ type = scLookupTable.lookup(SBuf(item,ilen));
- if (type < 0) {
+ if (type == SC_ENUM_END) {
debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'");
type = SC_OTHER;
}
/* Is this a targeted directive? */
- /* TODO sometime: implement a strnrchr that looks at a substring */
+ /* TODO: remove the temporary usage and use memrchr and the information we have instead */
temp = xstrndup (item, initiallen + 1);
if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0'))
else
++target;
- sct = httpHdrScFindTarget (sc, target);
+ sct = sc->findTarget(target);
if (!sct) {
- sct = httpHdrScTargetCreate (target);
- dlinkAdd(sct, &sct->node, &sc->targets);
+ sct = new HttpHdrScTarget(target);
+ addTarget(sct);
}
safe_free (temp);
- if (EBIT_TEST(sct->mask, type)) {
+ if (sct->isSet(type)) {
if (type != SC_OTHER)
debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'");
- ScFieldsInfo[type].stat.repCount++;
+ ++ scHeaderStats[type].repCount;
continue;
}
- /* update mask */
- EBIT_SET(sct->mask, type);
-
- /* post-processing special cases */
+ /* process directives */
switch (type) {
+ case SC_NO_STORE:
+ sct->noStore(true);
+ break;
- case SC_MAX_AGE:
+ case SC_NO_STORE_REMOTE:
+ sct->noStoreRemote(true);
+ break;
- if (!p || !httpHeaderParseInt(p, &sct->max_age)) {
+ case SC_MAX_AGE: {
+ int ma;
+ if (p && httpHeaderParseInt(p, &ma)) {
+ sct->maxAge(ma);
+
+ if ((p = strchr (p, '+'))) {
+ int ms;
+ ++p; //skip the + char
+ if (httpHeaderParseInt(p, &ms)) {
+ sct->maxStale(ms);
+ } else {
+ debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'");
+ sct->clearMaxStale();
+ /* leave the max-age alone */
+ }
+ }
+ } else {
debugs(90, 2, "sc: invalid max-age specs near '" << item << "'");
- sct->max_age = -1;
- EBIT_CLR(sct->mask, type);
+ sct->clearMaxAge();
}
- if ((p = strchr (p, '+')))
- if (!httpHeaderParseInt(++p, &sct->max_stale)) {
- debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'");
- sct->max_stale = 0;
- /* leave the max-age alone */
- }
-
break;
+ }
case SC_CONTENT:
- if (!p || !httpHeaderParseQuotedString(p, vlen, &sct->content)) {
+ if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) {
+ sct->setMask(SC_CONTENT,true); // ugly but saves a copy
+ } else {
debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'");
- sct->content.clean();
- EBIT_CLR(sct->mask, type);
+ sct->clearContent();
}
+ break;
+ case SC_OTHER:
default:
break;
}
return sc->targets.head != NULL;
}
-void
-httpHdrScDestroy(HttpHdrSc * sc)
+HttpHdrSc::~HttpHdrSc()
{
- assert(sc);
-
- if (sc->targets.head) {
- dlink_node *sct = sc->targets.head;
+ if (targets.head) {
+ dlink_node *sct = targets.head;
while (sct) {
- HttpHdrScTarget *t = (HttpHdrScTarget *)sct->data;
+ HttpHdrScTarget *t = static_cast<HttpHdrScTarget *>(sct->data);
sct = sct->next;
- dlinkDelete (&t->node, &sc->targets);
- httpHdrScTargetDestroy (t);
+ dlinkDelete (&t->node, &targets);
+ delete t;
}
}
-
- delete sc;
}
-HttpHdrSc *
-httpHdrScDup(const HttpHdrSc * sc)
+HttpHdrSc::HttpHdrSc(const HttpHdrSc &sc)
{
- HttpHdrSc *dup;
- dlink_node *node;
- assert(sc);
- node = sc->targets.head;
- dup = httpHdrScCreate();
+ dlink_node *node = sc.targets.head;
while (node) {
- HttpHdrScTarget *dupsct;
- dupsct = httpHdrScTargetDup ((HttpHdrScTarget *)node->data);
- dlinkAddTail (dupsct, &dupsct->node, &dup->targets);
+ HttpHdrScTarget *dupsct = new HttpHdrScTarget(*static_cast<HttpHdrScTarget *>(node->data));
+ addTargetAtTail(dupsct);
node = node->next;
}
-
- return dup;
}
void
-httpHdrScTargetPackInto(const HttpHdrScTarget * sc, Packer * p)
+HttpHdrScTarget::packInto(Packable * p) const
{
http_hdr_sc_type flag;
int pcount = 0;
- assert(sc && p);
+ assert (p);
for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) {
- if (EBIT_TEST(sc->mask, flag) && flag != SC_OTHER) {
+ if (isSet(flag) && flag != SC_OTHER) {
/* print option name */
- packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH),
- SQUIDSTRINGPRINT(ScFieldsInfo[flag].name));
+ p->appendf((pcount ? ", %s" : "%s"), ScAttrs[flag].name);
/* handle options with values */
if (flag == SC_MAX_AGE)
- packerPrintf(p, "=%d", (int) sc->max_age);
+ p->appendf("=%d", (int) max_age);
if (flag == SC_CONTENT)
- packerPrintf(p, "=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(sc->content));
+ p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(content_));
- pcount++;
+ ++pcount;
}
}
- if (sc->target.size())
- packerPrintf (p, ";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(sc->target));
+ if (hasTarget())
+ p->appendf(";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(target));
}
void
-httpHdrScPackInto(const HttpHdrSc * sc, Packer * p)
+HttpHdrSc::packInto(Packable * p) const
{
dlink_node *node;
- assert(sc && p);
- node = sc->targets.head;
+ assert(p);
+ node = targets.head;
while (node) {
- httpHdrScTargetPackInto((HttpHdrScTarget *)node->data, p);
+ static_cast<HttpHdrScTarget *>(node->data)->packInto(p);
node = node->next;
}
}
-void
-httpHdrScJoinWith(HttpHdrSc * sc, const HttpHdrSc * new_sc)
-{
- assert(sc && new_sc);
-#if 0
- /* RC TODO: check that both have the same target */
-
- if (sc->max_age < 0)
- sc->max_age = new_sc->max_age;
-
- /* RC TODO: copy unique missing stringlist entries */
- cc->mask |= new_cc->mask;
-
-#endif
-}
-
/* negative max_age will clean old max_Age setting */
void
-httpHdrScSetMaxAge(HttpHdrSc * sc, char const *target, int max_age)
+HttpHdrSc::setMaxAge(char const *target, int max_age)
{
- HttpHdrScTarget *sct;
- assert(sc);
- sct = httpHdrScFindTarget (sc, target);
+ HttpHdrScTarget *sct = findTarget(target);
if (!sct) {
- sct = httpHdrScTargetCreate (target);
- dlinkAddTail (sct, &sct->node, &sc->targets);
+ sct = new HttpHdrScTarget(target);
+ dlinkAddTail (sct, &sct->node, &targets);
}
- httpHdrScTargetSetMaxAge(sct, max_age);
+ sct->maxAge(max_age);
}
void
-httpHdrScUpdateStats(const HttpHdrSc * sc, StatHist * hist)
+HttpHdrSc::updateStats(StatHist * hist) const
{
- dlink_node *sct;
- assert(sc);
- sct = sc->targets.head;
+ dlink_node *sct = targets.head;
while (sct) {
- httpHdrScTargetUpdateStats((HttpHdrScTarget *)sct->data, hist);
+ static_cast<HttpHdrScTarget *>(sct->data)->updateStats(hist);
sct = sct->next;
}
}
void
-httpHdrScTargetStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
+httpHdrScTargetStatDumper(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 < SC_ENUM_END;
- const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
+ const bool valid_id = id >= 0 && id < SC_ENUM_END;
+ const char *name = valid_id ? ScAttrs[id].name : "INVALID";
if (count || valid_id)
storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
}
void
-httpHdrScStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
+httpHdrScStatDumper(StoreEntry * sentry, int, double val, double, int count)
{
- extern const HttpHeaderStat *dump_stat; /* argh! */
+ extern const HttpHeaderStat *dump_stat; /* argh! */
const int id = (int) val;
- const int valid_id = id >= 0 && id < SC_ENUM_END;
- const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
+ const bool valid_id = id >= 0 && id < SC_ENUM_END;
+ const char *name = valid_id ? ScAttrs[id].name : "INVALID";
if (count || valid_id)
storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
}
HttpHdrScTarget *
-httpHdrScFindTarget (HttpHdrSc *sc, const char *target)
+HttpHdrSc::findTarget(const char *target)
{
dlink_node *node;
- assert (sc);
- node = sc->targets.head;
+ node = targets.head;
while (node) {
HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data;
- if (target && sct->target.defined() && !strcmp (target, sct->target.termedBuf()))
+ if (target && sct->target.size() > 0 && !strcmp(target, sct->target.termedBuf()))
return sct;
- else if (!target && sct->target.undefined())
+ else if (!target && sct->target.size() == 0)
return sct;
node = node->next;
}
HttpHdrScTarget *
-httpHdrScGetMergedTarget (HttpHdrSc *sc, const char *ourtarget)
+HttpHdrSc::getMergedTarget(const char *ourtarget)
{
- HttpHdrScTarget *sctus = httpHdrScFindTarget (sc, ourtarget);
- HttpHdrScTarget *sctgeneric = httpHdrScFindTarget (sc, NULL);
+ HttpHdrScTarget *sctus = findTarget(ourtarget);
+ HttpHdrScTarget *sctgeneric = findTarget(NULL);
if (sctgeneric || sctus) {
- HttpHdrScTarget *sctusable = httpHdrScTargetCreate (NULL);
+ HttpHdrScTarget *sctusable = new HttpHdrScTarget(NULL);
if (sctgeneric)
- httpHdrScTargetMergeWith (sctusable, sctgeneric);
+ sctusable->mergeWith(sctgeneric);
if (sctus)
- httpHdrScTargetMergeWith (sctusable, sctus);
+ sctusable->mergeWith(sctus);
return sctusable;
}
return NULL;
}
+
+void
+HttpHdrSc::addTarget(HttpHdrScTarget *t) {
+ dlinkAdd(t, &t->node, &targets);
+}
+
+void
+HttpHdrSc::addTargetAtTail(HttpHdrScTarget *t) {
+ dlinkAddTail (t, &t->node, &targets);
+}
+