HttpHdrCc.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 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 "HttpHeaderStat.h"
17 #include "HttpHeaderTools.h"
18 #include "sbuf/SBuf.h"
19 #include "SquidMath.h"
20 #include "StatHist.h"
21 #include "Store.h"
22 #include "StrList.h"
23 #include "util.h"
24 
25 #include <map>
26 #include <vector>
27 #include <optional>
28 #include <ostream>
29 
31  {"public", HttpHdrCcType::CC_PUBLIC},
32  {"private", HttpHdrCcType::CC_PRIVATE},
33  {"no-cache", HttpHdrCcType::CC_NO_CACHE},
34  {"no-store", HttpHdrCcType::CC_NO_STORE},
35  {"no-transform", HttpHdrCcType::CC_NO_TRANSFORM},
36  {"must-revalidate", HttpHdrCcType::CC_MUST_REVALIDATE},
37  {"proxy-revalidate", HttpHdrCcType::CC_PROXY_REVALIDATE},
38  {"max-age", HttpHdrCcType::CC_MAX_AGE},
39  {"s-maxage", HttpHdrCcType::CC_S_MAXAGE},
40  {"max-stale", HttpHdrCcType::CC_MAX_STALE},
41  {"min-fresh", HttpHdrCcType::CC_MIN_FRESH},
42  {"only-if-cached", HttpHdrCcType::CC_ONLY_IF_CACHED},
43  {"stale-if-error", HttpHdrCcType::CC_STALE_IF_ERROR},
44  {"immutable", HttpHdrCcType::CC_IMMUTABLE},
45  {"Other,", HttpHdrCcType::CC_OTHER}, /* ',' will protect from matches */
47 };
48 
49 constexpr const auto &
51  // TODO: Move these compile-time checks into LookupTable
52  ConstexprForEnum<HttpHdrCcType::CC_PUBLIC, HttpHdrCcType::CC_ENUM_END>([](const auto ev) {
53  const auto idx = static_cast<std::underlying_type<HttpHdrCcType>::type>(ev);
54  // invariant: each row has a name except the last one
55  static_assert(!attrsList[idx].name == (ev == HttpHdrCcType::CC_ENUM_END));
56  // invariant: row[idx].id == idx
57  static_assert(attrsList[idx].id == ev);
58  });
59  return attrsList;
60 }
61 
62 static auto
63 ccTypeByName(const SBuf &name) {
64  const static auto table = new LookupTable<HttpHdrCcType>(HttpHdrCcType::CC_OTHER, CcAttrs());
65  return table->lookup(name);
66 }
67 
70 template <typename RawId>
71 static std::optional<const char *>
72 ccNameByType(const RawId rawId)
73 {
74  // TODO: Store a by-ID index in (and move this functionality into) LookupTable.
75  if (!Less(rawId, 0) && Less(rawId, int(HttpHdrCcType::CC_ENUM_END))) {
76  const auto idx = static_cast<std::underlying_type<HttpHdrCcType>::type>(rawId);
77  return CcAttrs()[idx].name;
78  }
79  return std::nullopt;
80 }
81 
83 static HttpHdrCcType &
85 {
86  int tmp = (int)aHeader;
87  aHeader = (HttpHdrCcType)(++tmp);
88  return aHeader;
89 }
90 
91 void
93 {
94  *this=HttpHdrCc();
95 }
96 
99 void
100 HttpHdrCc::setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting)
101 {
102  if (setting) {
103  if (new_value < 0) {
104  debugs(65, 3, "rejecting negative-value Cache-Control directive " << hdr
105  << " value " << new_value);
106  return;
107  }
108  } else {
109  new_value = -1; //rely on the convention that "unknown" is -1
110  }
111 
112  value = new_value;
113  setMask(hdr,setting);
114 }
115 
116 bool
118 {
119  const char *item;
120  const char *p; /* '=' parameter */
121  const char *pos = nullptr;
122  int ilen;
123  int nlen;
124 
125  /* iterate through comma separated list */
126 
127  while (strListGetItem(&str, ',', &item, &ilen, &pos)) {
128  /* isolate directive name */
129 
130  if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
131  nlen = p - item;
132  ++p;
133  } else {
134  nlen = ilen;
135  }
136 
137  /* find type */
138  const auto type = ccTypeByName(SBuf(item, nlen));
139 
140  // ignore known duplicate directives
141  if (isSet(type)) {
142  if (type != HttpHdrCcType::CC_OTHER) {
143  debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
144  continue;
145  }
146  }
147 
148  /* special-case-parsing and attribute-setting */
149  switch (type) {
150 
152  if (!p || !httpHeaderParseInt(p, &max_age) || max_age < 0) {
153  debugs(65, 2, "cc: invalid max-age specs near '" << item << "'");
154  clearMaxAge();
155  } else {
156  setMask(type,true);
157  }
158  break;
159 
161  if (!p || !httpHeaderParseInt(p, &s_maxage) || s_maxage < 0) {
162  debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'");
163  clearSMaxAge();
164  } else {
165  setMask(type,true);
166  }
167  break;
168 
170  if (!p || !httpHeaderParseInt(p, &max_stale) || max_stale < 0) {
171  debugs(65, 2, "cc: max-stale directive is valid without value");
173  } else {
174  setMask(type,true);
175  }
176  break;
177 
179  if (!p || !httpHeaderParseInt(p, &min_fresh) || min_fresh < 0) {
180  debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'");
181  clearMinFresh();
182  } else {
183  setMask(type,true);
184  }
185  break;
186 
188  if (!p || !httpHeaderParseInt(p, &stale_if_error) || stale_if_error < 0) {
189  debugs(65, 2, "cc: invalid stale-if-error specs near '" << item << "'");
191  } else {
192  setMask(type,true);
193  }
194  break;
195 
197  String temp;
198  if (!p) {
199  // Value parameter is optional.
200  private_.clean();
201  } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
202  private_.append(temp);
203  } else {
204  debugs(65, 2, "cc: invalid private= specs near '" << item << "'");
205  }
206  // to be safe we ignore broken parameters, but always remember the 'private' part.
207  setMask(type,true);
208  }
209  break;
210 
212  String temp;
213  if (!p) {
214  // On Requests, missing value parameter is expected syntax.
215  // On Responses, value parameter is optional.
216  setMask(type,true);
217  no_cache.clean();
218  } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
219  // On Requests, a value parameter is invalid syntax.
220  // XXX: identify when parsing request header and dump err message here.
221  setMask(type,true);
222  no_cache.append(temp);
223  } else {
224  debugs(65, 2, "cc: invalid no-cache= specs near '" << item << "'");
225  }
226  }
227  break;
228 
230  Public(true);
231  break;
233  noStore(true);
234  break;
236  noTransform(true);
237  break;
239  mustRevalidate(true);
240  break;
242  proxyRevalidate(true);
243  break;
245  onlyIfCached(true);
246  break;
248  Immutable(true);
249  break;
250 
252  if (other.size())
253  other.append(", ");
254 
255  other.append(item, ilen);
256  break;
257 
258  default:
259  /* note that we ignore most of '=' specs (RFCVIOLATION) */
260  break;
261  }
262  }
263 
264  return (mask != 0);
265 }
266 
267 void
269 {
270  // optimization: if the mask is empty do nothing
271  if (mask==0)
272  return;
273 
274  HttpHdrCcType flag;
275  int pcount = 0;
276  assert(p);
277 
278  for (flag = HttpHdrCcType::CC_PUBLIC; flag < HttpHdrCcType::CC_ENUM_END; ++flag) {
279  if (isSet(flag) && flag != HttpHdrCcType::CC_OTHER) {
280 
281  /* print option name for all options */
282  p->appendf((pcount ? ", %s": "%s"), *ccNameByType(flag));
283 
284  /* for all options having values, "=value" after the name */
285  switch (flag) {
287  break;
289  if (private_.size())
291  break;
292 
294  if (no_cache.size())
296  break;
298  break;
300  break;
302  break;
304  break;
306  p->appendf("=%d", max_age);
307  break;
309  p->appendf("=%d", s_maxage);
310  break;
312  /* max-stale's value is optional.
313  If we didn't receive it, don't send it */
314  if (max_stale != MAX_STALE_ANY)
315  p->appendf("=%d", max_stale);
316  break;
318  p->appendf("=%d", min_fresh);
319  break;
321  break;
323  p->appendf("=%d", stale_if_error);
324  break;
326  break;
329  // done below after the loop
330  break;
331  }
332 
333  ++pcount;
334  }
335  }
336 
337  if (other.size() != 0)
338  p->appendf((pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(other));
339 }
340 
341 void
343 {
344  assert(cc);
345 
347  if (cc->isSet(c))
348  hist->count(c);
349 }
350 
351 void
352 httpHdrCcStatDumper(StoreEntry * sentry, int, double val, double, int count)
353 {
354  extern const HttpHeaderStat *dump_stat; /* argh! */
355  const int id = static_cast<int>(val);
356  const auto name = ccNameByType(id);
357  if (count || name)
358  storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
359  id, name.value_or("INVALID"), count, xdiv(count, dump_stat->ccParsedCount));
360 }
361 
362 std::ostream &
363 operator<< (std::ostream &s, HttpHdrCcType c)
364 {
365  s << ccNameByType(c).value_or("INVALID") << '[' << static_cast<int>(c) << ']';
366  return s;
367 }
368 
@ 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:30
@ CC_MAX_STALE
Definition: HttpHdrCc.h:30
void clearMaxAge()
Definition: HttpHdrCc.h:124
void httpHdrCcUpdateStats(const HttpHdrCc *cc, StatHist *hist)
Definition: HttpHdrCc.cc:342
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:63
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:117
void setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting=true)
Definition: HttpHdrCc.cc:100
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:84
void mustRevalidate(bool v)
Definition: HttpHdrCc.h:113
static std::optional< const char * > ccNameByType(const RawId rawId)
Definition: HttpHdrCc.cc:72
@ 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:50
void httpHdrCcStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition: HttpHdrCc.cc:352
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:363
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:92
void packInto(Packable *p) const
Definition: HttpHdrCc.cc:268
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