HttpHdrCc.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 65 HTTP Cache Control Header */
10 
11 #include "squid.h"
12 #include "base/EnumIterator.h"
13 #include "base/LookupTable.h"
14 #include "HttpHdrCc.h"
15 #include "HttpHeader.h"
16 #include "HttpHeaderFieldStat.h"
17 #include "HttpHeaderStat.h"
18 #include "HttpHeaderTools.h"
19 #include "sbuf/SBuf.h"
20 #include "SquidMath.h"
21 #include "StatHist.h"
22 #include "Store.h"
23 #include "StrList.h"
24 #include "util.h"
25 
26 #include <map>
27 #include <vector>
28 #include <optional>
29 #include <ostream>
30 
32  {"public", HttpHdrCcType::CC_PUBLIC},
33  {"private", HttpHdrCcType::CC_PRIVATE},
34  {"no-cache", HttpHdrCcType::CC_NO_CACHE},
35  {"no-store", HttpHdrCcType::CC_NO_STORE},
36  {"no-transform", HttpHdrCcType::CC_NO_TRANSFORM},
37  {"must-revalidate", HttpHdrCcType::CC_MUST_REVALIDATE},
38  {"proxy-revalidate", HttpHdrCcType::CC_PROXY_REVALIDATE},
39  {"max-age", HttpHdrCcType::CC_MAX_AGE},
40  {"s-maxage", HttpHdrCcType::CC_S_MAXAGE},
41  {"max-stale", HttpHdrCcType::CC_MAX_STALE},
42  {"min-fresh", HttpHdrCcType::CC_MIN_FRESH},
43  {"only-if-cached", HttpHdrCcType::CC_ONLY_IF_CACHED},
44  {"stale-if-error", HttpHdrCcType::CC_STALE_IF_ERROR},
45  {"immutable", HttpHdrCcType::CC_IMMUTABLE},
46  {"Other,", HttpHdrCcType::CC_OTHER}, /* ',' will protect from matches */
48 };
49 
50 constexpr const auto &
52  // TODO: Move these compile-time checks into LookupTable
53  ConstexprForEnum<HttpHdrCcType::CC_PUBLIC, HttpHdrCcType::CC_ENUM_END>([](const auto ev) {
54  const auto idx = static_cast<std::underlying_type<HttpHdrCcType>::type>(ev);
55  // invariant: each row has a name except the last one
56  static_assert(!attrsList[idx].name == (ev == HttpHdrCcType::CC_ENUM_END));
57  // invariant: row[idx].id == idx
58  static_assert(attrsList[idx].id == ev);
59  });
60  return attrsList;
61 }
62 
63 static auto
64 ccTypeByName(const SBuf &name) {
65  const static auto table = new LookupTable<HttpHdrCcType>(HttpHdrCcType::CC_OTHER, CcAttrs());
66  return table->lookup(name);
67 }
68 
71 template <typename RawId>
72 static std::optional<const char *>
73 ccNameByType(const RawId rawId)
74 {
75  // TODO: Store a by-ID index in (and move this functionality into) LookupTable.
76  if (!Less(rawId, 0) && Less(rawId, int(HttpHdrCcType::CC_ENUM_END))) {
77  const auto idx = static_cast<std::underlying_type<HttpHdrCcType>::type>(rawId);
78  return CcAttrs()[idx].name;
79  }
80  return std::nullopt;
81 }
82 
83 std::vector<HttpHeaderFieldStat> ccHeaderStats(HttpHdrCcType::CC_ENUM_END);
84 
86 static HttpHdrCcType &
88 {
89  int tmp = (int)aHeader;
90  aHeader = (HttpHdrCcType)(++tmp);
91  return aHeader;
92 }
93 
94 void
96 {
97  *this=HttpHdrCc();
98 }
99 
102 void
103 HttpHdrCc::setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting)
104 {
105  if (setting) {
106  if (new_value < 0) {
107  debugs(65, 3, "rejecting negative-value Cache-Control directive " << hdr
108  << " value " << new_value);
109  return;
110  }
111  } else {
112  new_value = -1; //rely on the convention that "unknown" is -1
113  }
114 
115  value = new_value;
116  setMask(hdr,setting);
117 }
118 
119 bool
121 {
122  const char *item;
123  const char *p; /* '=' parameter */
124  const char *pos = nullptr;
125  int ilen;
126  int nlen;
127 
128  /* iterate through comma separated list */
129 
130  while (strListGetItem(&str, ',', &item, &ilen, &pos)) {
131  /* isolate directive name */
132 
133  if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
134  nlen = p - item;
135  ++p;
136  } else {
137  nlen = ilen;
138  }
139 
140  /* find type */
141  const auto type = ccTypeByName(SBuf(item, nlen));
142 
143  // ignore known duplicate directives
144  if (isSet(type)) {
145  if (type != HttpHdrCcType::CC_OTHER) {
146  debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
147  ++ ccHeaderStats[type].repCount;
148  continue;
149  }
150  }
151 
152  /* special-case-parsing and attribute-setting */
153  switch (type) {
154 
156  if (!p || !httpHeaderParseInt(p, &max_age) || max_age < 0) {
157  debugs(65, 2, "cc: invalid max-age specs near '" << item << "'");
158  clearMaxAge();
159  } else {
160  setMask(type,true);
161  }
162  break;
163 
165  if (!p || !httpHeaderParseInt(p, &s_maxage) || s_maxage < 0) {
166  debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'");
167  clearSMaxAge();
168  } else {
169  setMask(type,true);
170  }
171  break;
172 
174  if (!p || !httpHeaderParseInt(p, &max_stale) || max_stale < 0) {
175  debugs(65, 2, "cc: max-stale directive is valid without value");
177  } else {
178  setMask(type,true);
179  }
180  break;
181 
183  if (!p || !httpHeaderParseInt(p, &min_fresh) || min_fresh < 0) {
184  debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'");
185  clearMinFresh();
186  } else {
187  setMask(type,true);
188  }
189  break;
190 
192  if (!p || !httpHeaderParseInt(p, &stale_if_error) || stale_if_error < 0) {
193  debugs(65, 2, "cc: invalid stale-if-error specs near '" << item << "'");
195  } else {
196  setMask(type,true);
197  }
198  break;
199 
201  String temp;
202  if (!p) {
203  // Value parameter is optional.
204  private_.clean();
205  } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
206  private_.append(temp);
207  } else {
208  debugs(65, 2, "cc: invalid private= specs near '" << item << "'");
209  }
210  // to be safe we ignore broken parameters, but always remember the 'private' part.
211  setMask(type,true);
212  }
213  break;
214 
216  String temp;
217  if (!p) {
218  // On Requests, missing value parameter is expected syntax.
219  // On Responses, value parameter is optional.
220  setMask(type,true);
221  no_cache.clean();
222  } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
223  // On Requests, a value parameter is invalid syntax.
224  // XXX: identify when parsing request header and dump err message here.
225  setMask(type,true);
226  no_cache.append(temp);
227  } else {
228  debugs(65, 2, "cc: invalid no-cache= specs near '" << item << "'");
229  }
230  }
231  break;
232 
234  Public(true);
235  break;
237  noStore(true);
238  break;
240  noTransform(true);
241  break;
243  mustRevalidate(true);
244  break;
246  proxyRevalidate(true);
247  break;
249  onlyIfCached(true);
250  break;
252  Immutable(true);
253  break;
254 
256  if (other.size())
257  other.append(", ");
258 
259  other.append(item, ilen);
260  break;
261 
262  default:
263  /* note that we ignore most of '=' specs (RFCVIOLATION) */
264  break;
265  }
266  }
267 
268  return (mask != 0);
269 }
270 
271 void
273 {
274  // optimization: if the mask is empty do nothing
275  if (mask==0)
276  return;
277 
278  HttpHdrCcType flag;
279  int pcount = 0;
280  assert(p);
281 
282  for (flag = HttpHdrCcType::CC_PUBLIC; flag < HttpHdrCcType::CC_ENUM_END; ++flag) {
283  if (isSet(flag) && flag != HttpHdrCcType::CC_OTHER) {
284 
285  /* print option name for all options */
286  p->appendf((pcount ? ", %s": "%s"), *ccNameByType(flag));
287 
288  /* for all options having values, "=value" after the name */
289  switch (flag) {
291  break;
293  if (private_.size())
295  break;
296 
298  if (no_cache.size())
300  break;
302  break;
304  break;
306  break;
308  break;
310  p->appendf("=%d", max_age);
311  break;
313  p->appendf("=%d", s_maxage);
314  break;
316  /* max-stale's value is optional.
317  If we didn't receive it, don't send it */
318  if (max_stale != MAX_STALE_ANY)
319  p->appendf("=%d", max_stale);
320  break;
322  p->appendf("=%d", min_fresh);
323  break;
325  break;
327  p->appendf("=%d", stale_if_error);
328  break;
330  break;
333  // done below after the loop
334  break;
335  }
336 
337  ++pcount;
338  }
339  }
340 
341  if (other.size() != 0)
342  p->appendf((pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(other));
343 }
344 
345 void
347 {
348  assert(cc);
349 
351  if (cc->isSet(c))
352  hist->count(c);
353 }
354 
355 void
356 httpHdrCcStatDumper(StoreEntry * sentry, int, double val, double, int count)
357 {
358  extern const HttpHeaderStat *dump_stat; /* argh! */
359  const int id = static_cast<int>(val);
360  const auto name = ccNameByType(id);
361  if (count || name)
362  storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
363  id, name.value_or("INVALID"), count, xdiv(count, dump_stat->ccParsedCount));
364 }
365 
366 std::ostream &
367 operator<< (std::ostream &s, HttpHdrCcType c)
368 {
369  s << ccNameByType(c).value_or("INVALID") << '[' << static_cast<int>(c) << ']';
370  return s;
371 }
372 
@ CC_NO_TRANSFORM
Definition: HttpHdrCc.h:25
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
String other
Definition: HttpHdrCc.h:207
@ CC_MIN_FRESH
Definition: HttpHdrCc.h:31
int32_t max_stale
Definition: HttpHdrCc.h:176
HTTP per header statistics.
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
#define SQUIDSTRINGPRINT(s)
Definition: SquidString.h:22
const HttpHeaderStat * dump_stat
Definition: HttpHeader.cc:1561
HttpHdrCcType
Definition: HttpHdrCc.h:20
@ CC_MAX_AGE
Definition: HttpHdrCc.h:28
Definition: SBuf.h:93
constexpr LookupTable< HttpHdrCcType >::Record attrsList[]
Definition: HttpHdrCc.cc:31
@ CC_MAX_STALE
Definition: HttpHdrCc.h:30
void clearMaxAge()
Definition: HttpHdrCc.h:124
std::vector< HttpHeaderFieldStat > ccHeaderStats(HttpHdrCcType::CC_ENUM_END)
void httpHdrCcUpdateStats(const HttpHdrCc *cc, StatHist *hist)
Definition: HttpHdrCc.cc:346
int32_t s_maxage
Definition: HttpHdrCc.h:175
#define SQUIDSTRINGPH
Definition: SquidString.h:21
int32_t mask
Definition: HttpHdrCc.h:173
@ CC_OTHER
Definition: HttpHdrCc.h:35
HttpHdrCc()
Definition: HttpHdrCc.h:57
void maxStale(int32_t v)
Definition: HttpHdrCc.h:136
static auto ccTypeByName(const SBuf &name)
Definition: HttpHdrCc.cc:64
static const int32_t MAX_STALE_ANY
Definition: HttpHdrCc.h:53
@ CC_IMMUTABLE
Definition: HttpHdrCc.h:34
void clearSMaxAge()
Definition: HttpHdrCc.h:129
bool parse(const String &s)
parse a header-string and fill in appropriate values.
Definition: HttpHdrCc.cc:120
void setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting=true)
Definition: HttpHdrCc.cc:103
void append(char const *buf, int len)
Definition: String.cc:130
int httpHeaderParseQuotedString(const char *start, const int len, String *val)
int32_t min_fresh
Definition: HttpHdrCc.h:178
void noStore(bool v)
Definition: HttpHdrCc.h:103
void Public(bool v)
Definition: HttpHdrCc.h:70
@ CC_MUST_REVALIDATE
Definition: HttpHdrCc.h:26
@ CC_NO_STORE
Definition: HttpHdrCc.h:24
void noTransform(bool v)
Definition: HttpHdrCc.h:108
void count(double val)
Definition: StatHist.cc:55
#define assert(EX)
Definition: assert.h:17
static HttpHdrCcType & operator++(HttpHdrCcType &aHeader)
used to walk a table of http_header_cc_type structs
Definition: HttpHdrCc.cc:87
void mustRevalidate(bool v)
Definition: HttpHdrCc.h:113
static std::optional< const char * > ccNameByType(const RawId rawId)
Definition: HttpHdrCc.cc:73
@ CC_S_MAXAGE
Definition: HttpHdrCc.h:29
void onlyIfCached(bool v)
Definition: HttpHdrCc.h:146
int32_t stale_if_error
Definition: HttpHdrCc.h:177
bool isSet(HttpHdrCcType id) const
check whether the attribute value supplied by id is set
Definition: HttpHdrCc.h:160
void Immutable(bool v)
Definition: HttpHdrCc.h:156
constexpr const auto & CcAttrs()
Definition: HttpHdrCc.cc:51
void httpHdrCcStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition: HttpHdrCc.cc:356
String no_cache
List of headers sent as value for CC:no-cache="...". May be empty/undefined if the value is missing.
Definition: HttpHdrCc.h:180
@ CC_PRIVATE
Definition: HttpHdrCc.h:22
void proxyRevalidate(bool v)
Definition: HttpHdrCc.h:118
size_type size() const
Definition: SquidString.h:73
std::ostream & operator<<(std::ostream &s, HttpHdrCcType c)
Definition: HttpHdrCc.cc:367
void clearMinFresh()
Definition: HttpHdrCc.h:142
int httpHeaderParseInt(const char *start, int *value)
@ CC_STALE_IF_ERROR
Definition: HttpHdrCc.h:33
void setMask(HttpHdrCcType id, bool newval=true)
low-level part of the public set method, performs no checks
Definition: HttpHdrCc.h:194
@ CC_PROXY_REVALIDATE
Definition: HttpHdrCc.h:27
double xdiv(double nom, double denom)
Definition: util.cc:53
@ CC_ENUM_END
Definition: HttpHdrCc.h:36
@ CC_NO_CACHE
Definition: HttpHdrCc.h:23
constexpr bool Less(const A a, const B b)
whether integer a is less than integer b, with correct overflow handling
Definition: SquidMath.h:48
void clear()
reset data-members to default state
Definition: HttpHdrCc.cc:95
void packInto(Packable *p) const
Definition: HttpHdrCc.cc:272
String private_
List of headers sent as value for CC:private="...". May be empty/undefined if the value is missing.
Definition: HttpHdrCc.h:179
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
@ CC_PUBLIC
Definition: HttpHdrCc.h:21
int unsigned int
Definition: stub_fd.cc:19
void clearStaleIfError()
Definition: HttpHdrCc.h:152
int32_t max_age
Definition: HttpHdrCc.h:174
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
Definition: StrList.cc:78
void clean()
Definition: String.cc:103
@ CC_ONLY_IF_CACHED
Definition: HttpHdrCc.h:32

 

Introduction

Documentation

Support

Miscellaneous