]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrCc.cc
448b08223c427e6d0b451fe13c5329f5c46bcca1
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 65 HTTP Cache Control Header */
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"
30 constexpr LookupTable
<HttpHdrCcType
>::Record attrsList
[] = {
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 */
46 {nullptr, HttpHdrCcType::CC_ENUM_END
}
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
);
63 ccTypeByName(const SBuf
&name
) {
64 const static auto table
= new LookupTable
<HttpHdrCcType
>(HttpHdrCcType::CC_OTHER
, CcAttrs());
65 return table
->lookup(name
);
68 /// Safely converts an integer into a Cache-Control directive name.
69 /// \returns std::nullopt if the given integer is not a valid index of a named attrsList entry
70 template <typename RawId
>
71 static std::optional
<const char *>
72 ccNameByType(const RawId rawId
)
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
;
82 /// used to walk a table of http_header_cc_type structs
83 static HttpHdrCcType
&
84 operator++ (HttpHdrCcType
&aHeader
)
86 int tmp
= (int)aHeader
;
87 aHeader
= (HttpHdrCcType
)(++tmp
);
97 /// set a data member to a new value, and set the corresponding mask-bit.
98 /// if setting is false, then the mask-bit is cleared.
100 HttpHdrCc::setValue(int32_t &value
, int32_t new_value
, HttpHdrCcType hdr
, bool setting
)
104 debugs(65, 3, "rejecting negative-value Cache-Control directive " << hdr
105 << " value " << new_value
);
109 new_value
= -1; //rely on the convention that "unknown" is -1
113 setMask(hdr
,setting
);
117 HttpHdrCc::parse(const String
& str
)
120 const char *p
; /* '=' parameter */
121 const char *pos
= nullptr;
125 /* iterate through comma separated list */
127 while (strListGetItem(&str
, ',', &item
, &ilen
, &pos
)) {
128 /* isolate directive name */
130 if ((p
= (const char *)memchr(item
, '=', ilen
)) && (p
- item
< ilen
)) {
138 const auto type
= ccTypeByName(SBuf(item
, nlen
));
140 // ignore known duplicate directives
142 if (type
!= HttpHdrCcType::CC_OTHER
) {
143 debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item
<< "' in '" << str
<< "'");
148 /* special-case-parsing and attribute-setting */
151 case HttpHdrCcType::CC_MAX_AGE
:
152 if (!p
|| !httpHeaderParseInt(p
, &max_age
) || max_age
< 0) {
153 debugs(65, 2, "cc: invalid max-age specs near '" << item
<< "'");
160 case HttpHdrCcType::CC_S_MAXAGE
:
161 if (!p
|| !httpHeaderParseInt(p
, &s_maxage
) || s_maxage
< 0) {
162 debugs(65, 2, "cc: invalid s-maxage specs near '" << item
<< "'");
169 case HttpHdrCcType::CC_MAX_STALE
:
170 if (!p
|| !httpHeaderParseInt(p
, &max_stale
) || max_stale
< 0) {
171 debugs(65, 2, "cc: max-stale directive is valid without value");
172 maxStale(MAX_STALE_ANY
);
178 case HttpHdrCcType::CC_MIN_FRESH
:
179 if (!p
|| !httpHeaderParseInt(p
, &min_fresh
) || min_fresh
< 0) {
180 debugs(65, 2, "cc: invalid min-fresh specs near '" << item
<< "'");
187 case HttpHdrCcType::CC_STALE_IF_ERROR
:
188 if (!p
|| !httpHeaderParseInt(p
, &stale_if_error
) || stale_if_error
< 0) {
189 debugs(65, 2, "cc: invalid stale-if-error specs near '" << item
<< "'");
196 case HttpHdrCcType::CC_PRIVATE
: {
199 // Value parameter is optional.
201 } else if (/* p &&*/ httpHeaderParseQuotedString(p
, (ilen
-nlen
-1), &temp
)) {
202 private_
.append(temp
);
204 debugs(65, 2, "cc: invalid private= specs near '" << item
<< "'");
206 // to be safe we ignore broken parameters, but always remember the 'private' part.
211 case HttpHdrCcType::CC_NO_CACHE
: {
214 // On Requests, missing value parameter is expected syntax.
215 // On Responses, value parameter is optional.
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.
222 no_cache
.append(temp
);
224 debugs(65, 2, "cc: invalid no-cache= specs near '" << item
<< "'");
229 case HttpHdrCcType::CC_PUBLIC
:
232 case HttpHdrCcType::CC_NO_STORE
:
235 case HttpHdrCcType::CC_NO_TRANSFORM
:
238 case HttpHdrCcType::CC_MUST_REVALIDATE
:
239 mustRevalidate(true);
241 case HttpHdrCcType::CC_PROXY_REVALIDATE
:
242 proxyRevalidate(true);
244 case HttpHdrCcType::CC_ONLY_IF_CACHED
:
247 case HttpHdrCcType::CC_IMMUTABLE
:
251 case HttpHdrCcType::CC_OTHER
:
255 other
.append(item
, ilen
);
259 /* note that we ignore most of '=' specs (RFCVIOLATION) */
268 HttpHdrCc::packInto(Packable
* p
) const
270 // optimization: if the mask is empty do nothing
278 for (flag
= HttpHdrCcType::CC_PUBLIC
; flag
< HttpHdrCcType::CC_ENUM_END
; ++flag
) {
279 if (isSet(flag
) && flag
!= HttpHdrCcType::CC_OTHER
) {
281 /* print option name for all options */
282 p
->appendf((pcount
? ", %s": "%s"), *ccNameByType(flag
));
284 /* for all options having values, "=value" after the name */
286 case HttpHdrCcType::CC_PUBLIC
:
288 case HttpHdrCcType::CC_PRIVATE
:
290 p
->appendf("=\"" SQUIDSTRINGPH
"\"", SQUIDSTRINGPRINT(private_
));
293 case HttpHdrCcType::CC_NO_CACHE
:
295 p
->appendf("=\"" SQUIDSTRINGPH
"\"", SQUIDSTRINGPRINT(no_cache
));
297 case HttpHdrCcType::CC_NO_STORE
:
299 case HttpHdrCcType::CC_NO_TRANSFORM
:
301 case HttpHdrCcType::CC_MUST_REVALIDATE
:
303 case HttpHdrCcType::CC_PROXY_REVALIDATE
:
305 case HttpHdrCcType::CC_MAX_AGE
:
306 p
->appendf("=%d", max_age
);
308 case HttpHdrCcType::CC_S_MAXAGE
:
309 p
->appendf("=%d", s_maxage
);
311 case HttpHdrCcType::CC_MAX_STALE
:
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
);
317 case HttpHdrCcType::CC_MIN_FRESH
:
318 p
->appendf("=%d", min_fresh
);
320 case HttpHdrCcType::CC_ONLY_IF_CACHED
:
322 case HttpHdrCcType::CC_STALE_IF_ERROR
:
323 p
->appendf("=%d", stale_if_error
);
325 case HttpHdrCcType::CC_IMMUTABLE
:
327 case HttpHdrCcType::CC_OTHER
:
328 case HttpHdrCcType::CC_ENUM_END
:
329 // done below after the loop
337 if (other
.size() != 0)
338 p
->appendf((pcount
? ", " SQUIDSTRINGPH
: SQUIDSTRINGPH
), SQUIDSTRINGPRINT(other
));
342 httpHdrCcUpdateStats(const HttpHdrCc
* cc
, StatHist
* hist
)
346 for (HttpHdrCcType c
= HttpHdrCcType::CC_PUBLIC
; c
< HttpHdrCcType::CC_ENUM_END
; ++c
)
352 httpHdrCcStatDumper(StoreEntry
* sentry
, int, double val
, double, int count
)
354 extern const HttpHeaderStat
*dump_stat
; /* argh! */
355 const int id
= static_cast<int>(val
);
356 const auto name
= ccNameByType(id
);
358 storeAppendPrintf(sentry
, "%2d\t %-20s\t %5d\t %6.2f\n",
359 id
, name
.value_or("INVALID"), count
, xdiv(count
, dump_stat
->ccParsedCount
));
363 operator<< (std::ostream
&s
, HttpHdrCcType c
)
365 s
<< ccNameByType(c
).value_or("INVALID") << '[' << static_cast<int>(c
) << ']';