Squid Web Cache master
Loading...
Searching...
No Matches
HttpHdrSc.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 90 HTTP Cache Control Header */
10
11#include "squid.h"
12#include "base/LookupTable.h"
13//#include "HttpHdrSc.h" // pulled in by HttpHdrScTarget.h
14#include "HttpHdrScTarget.h"
15#include "HttpHeader.h"
16#include "HttpHeaderFieldStat.h"
17#include "HttpHeaderStat.h"
18#include "HttpHeaderTools.h"
19#include "Store.h"
20#include "StrList.h"
21#include "util.h"
22
23#include <map>
24#include <vector>
25
26/* this table is used for parsing surrogate control header */
27/* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
28// TODO: implement constraint
30 {"no-store", SC_NO_STORE},
31 {"no-store-remote", SC_NO_STORE_REMOTE},
32 {"max-age", SC_MAX_AGE},
33 {"content", SC_CONTENT},
34 {"Other,", SC_OTHER}, /* ',' will protect from matches */
35 {nullptr, SC_ENUM_END} /* SC_ENUM_END taken as invalid value */
36};
38
39// used when iterating over flags
41{
42 int tmp = static_cast<int>(aHeader);
43 aHeader = static_cast<http_hdr_sc_type>(++tmp);
44 return aHeader;
45}
46
47void
49{
50 // check invariant on ScAttrs
51 for (int i = 0; ScAttrs[i].name != nullptr; ++i)
52 assert(i == ScAttrs[i].id);
53}
54
55/* implementation */
56
57/* creates an sc object from a 0-terminating string */
60{
61 HttpHdrSc *sc = new HttpHdrSc();
62
63 if (!sc->parse(&str)) {
64 delete sc;
65 sc = nullptr;
66 }
67
68 return sc;
69}
70
71/* parses a 0-terminating string and inits sc */
72bool
74{
75 HttpHdrSc * sc=this;
76 const char *item;
77 const char *p; /* '=' parameter */
78 const char *pos = nullptr;
79 const char *target = nullptr; /* ;foo */
80 const char *temp = nullptr; /* temp buffer */
82 int ilen, vlen;
83 int initiallen;
84 HttpHdrScTarget *sct;
85 assert(str);
86
87 /* iterate through comma separated list */
88
89 while (strListGetItem(str, ',', &item, &ilen, &pos)) {
90 initiallen = ilen;
91 vlen = 0;
92 /* decrease ilen to still match the token for '=' statements */
93
94 if ((p = strchr(item, '=')) && (p - item < ilen)) {
95 vlen = ilen - (p + 1 - item);
96 ilen = p - item;
97 ++p;
98 }
99
100 /* decrease ilen to still match the token for ';' qualified non '=' statements */
101 else if ((p = strchr(item, ';')) && (p - item < ilen)) {
102 ilen = p - item;
103 ++p;
104 }
105
106 /* find type */
107 type = scLookupTable.lookup(SBuf(item,ilen));
108
109 if (type == SC_ENUM_END) {
110 debugs(90, 2, "unknown control-directive near '" << item << "' in '" << *str << "'");
111 type = SC_OTHER;
112 }
113
114 /* Is this a targeted directive? */
115 /* TODO: remove the temporary usage and use memrchr and the information we have instead */
116 temp = xstrndup (item, initiallen + 1);
117
118 if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0'))
119 target = nullptr;
120 else
121 ++target;
122
123 sct = sc->findTarget(target);
124
125 if (!sct) {
126 // XXX: if parse is left-to-right over field-value this should be emplace_back()
127 // currently placing on the front reverses the order of headers passed on downstream.
128 sct = &targets.emplace_front(target);
129 }
130
131 safe_free (temp);
132
133 if (sct->isSet(type)) {
134 if (type != SC_OTHER)
135 debugs(90, 2, "ignoring duplicate control-directive near '" << item << "' in '" << *str << "'");
136 continue;
137 }
138
139 /* process directives */
140 switch (type) {
141 case SC_NO_STORE:
142 sct->noStore(true);
143 break;
144
146 sct->noStoreRemote(true);
147 break;
148
149 case SC_MAX_AGE: {
150 int ma;
151 if (p && httpHeaderParseInt(p, &ma)) {
152 sct->maxAge(ma);
153
154 if ((p = strchr (p, '+'))) {
155 int ms;
156 ++p; //skip the + char
157 if (httpHeaderParseInt(p, &ms)) {
158 sct->maxStale(ms);
159 } else {
160 debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'");
161 sct->clearMaxStale();
162 /* leave the max-age alone */
163 }
164 }
165 } else {
166 debugs(90, 2, "sc: invalid max-age specs near '" << item << "'");
167 sct->clearMaxAge();
168 }
169
170 break;
171 }
172
173 case SC_CONTENT:
174
175 if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) {
176 sct->setMask(SC_CONTENT,true); // ugly but saves a copy
177 } else {
178 debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'");
179 sct->clearContent();
180 }
181 break;
182
183 case SC_OTHER:
184 default:
185 break;
186 }
187 }
188
189 return !sc->targets.empty();
190}
191
193void
195{
196 http_hdr_sc_type flag;
197 int pcount = 0;
198 assert (p);
199
200 for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) {
201 if (isSet(flag) && flag != SC_OTHER) {
202
203 /* print option name */
204 p->appendf((pcount ? ", %s" : "%s"), ScAttrs[flag].name);
205
206 /* handle options with values */
207
208 if (flag == SC_MAX_AGE)
209 p->appendf("=%d", (int) max_age);
210
211 if (flag == SC_CONTENT)
213
214 ++pcount;
215 }
216 }
217
218 if (hasTarget())
220}
221
222void
224{
225 assert(p);
226 for (const auto &t : targets) {
227 t.packInto(p);
228 }
229}
230
231/* negative max_age will clean old max_Age setting */
232void
233HttpHdrSc::setMaxAge(char const *target, int max_age)
234{
235 HttpHdrScTarget *sct = findTarget(target);
236
237 if (!sct) {
238 sct = &targets.emplace_back(target);
239 }
240
241 sct->maxAge(max_age);
242}
243
244void
246{
247 for (auto &t : targets) {
248 t.updateStats(hist);
249 }
250}
251
252void
253httpHdrScTargetStatDumper(StoreEntry * sentry, int, double val, double, int count)
254{
255 extern const HttpHeaderStat *dump_stat; /* argh! */
256 const int id = (int) val;
257 const bool valid_id = id >= 0 && id < SC_ENUM_END;
258 const char *name = valid_id ? ScAttrs[id].name : "INVALID";
259
260 if (count || valid_id)
261 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
262 id, name, count, xdiv(count, dump_stat->scParsedCount));
263}
264
265void
266httpHdrScStatDumper(StoreEntry * sentry, int, double val, double, int count)
267{
268 extern const HttpHeaderStat *dump_stat; /* argh! */
269 const int id = (int) val;
270 const bool valid_id = id >= 0 && id < SC_ENUM_END;
271 const char *name = valid_id ? ScAttrs[id].name : "INVALID";
272
273 if (count || valid_id)
274 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
275 id, name, count, xdiv(count, dump_stat->scParsedCount));
276}
277
279HttpHdrSc::findTarget(const char *target)
280{
281 for (auto &sct : targets) {
282 if (sct.target.cmp(target) == 0)
283 return &sct;
284 }
285
286 return nullptr;
287}
288
290HttpHdrSc::getMergedTarget(const char *ourtarget)
291{
292 HttpHdrScTarget *sctus = findTarget(ourtarget);
293 HttpHdrScTarget *sctgeneric = findTarget(nullptr);
294
295 /* W3C Edge Architecture Specification 1.0 section 3
296 *
297 * "If more than one is targeted at a surrogate, the most specific applies.
298 * For example,
299 * Surrogate-Control: max-age=60, no-store;abc
300 * The surrogate that identified itself as 'abc' would apply no-store;
301 * others would apply max-age=60.
302 *
303 * XXX: the if statements below will *merge* the no-store and max-age settings.
304 */
305 if (sctgeneric || sctus) {
306 HttpHdrScTarget *sctusable = new HttpHdrScTarget(nullptr);
307
308 if (sctgeneric)
309 sctusable->mergeWith(sctgeneric);
310
311 if (sctus)
312 sctusable->mergeWith(sctus);
313
314 return sctusable;
315 }
316
317 return nullptr;
318}
319
void httpHdrScStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition HttpHdrSc.cc:266
http_hdr_sc_type & operator++(http_hdr_sc_type &aHeader)
Definition HttpHdrSc.cc:40
LookupTable< http_hdr_sc_type > scLookupTable(SC_ENUM_END, ScAttrs)
void httpHdrScTargetStatDumper(StoreEntry *sentry, int, double val, double, int count)
Definition HttpHdrSc.cc:253
static const LookupTable< http_hdr_sc_type >::Record ScAttrs[]
Definition HttpHdrSc.cc:29
HttpHdrSc * httpHdrScParseCreate(const String &str)
Definition HttpHdrSc.cc:59
void httpHdrScInitModule(void)
Definition HttpHdrSc.cc:48
int httpHeaderParseInt(const char *start, int *value)
const HttpHeaderStat * dump_stat
int httpHeaderParseQuotedString(const char *start, const int len, String *val)
#define SQUIDSTRINGPH
Definition SquidString.h:22
#define SQUIDSTRINGPRINT(s)
Definition SquidString.h:23
int strListGetItem(const String *str, char del, const char **item, int *ilen, const char **pos)
Definition StrList.cc:78
#define assert(EX)
Definition assert.h:17
void maxAge(int v)
void noStore(bool v)
bool isSet(http_hdr_sc_type id) const
void mergeWith(const HttpHdrScTarget *new_sc)
bool hasTarget() const
void setMask(http_hdr_sc_type id, bool newval)
void packInto(Packable *p) const
XXX: this function should be in HttpHdrScTarget.cc.
Definition HttpHdrSc.cc:194
void noStoreRemote(bool v)
void maxStale(int v)
void setMaxAge(char const *target, int max_age)
Definition HttpHdrSc.cc:233
void packInto(Packable *p) const
Definition HttpHdrSc.cc:223
HttpHdrScTarget * findTarget(const char *target)
Definition HttpHdrSc.cc:279
std::list< HttpHdrScTarget, PoolingAllocator< HttpHdrScTarget > > targets
Definition HttpHdrSc.h:38
HttpHdrScTarget * getMergedTarget(const char *ourtarget)
Definition HttpHdrSc.cc:290
bool parse(const String *str)
Definition HttpHdrSc.cc:73
void updateStats(StatHist *) const
Definition HttpHdrSc.cc:245
HTTP per header statistics.
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition Packable.h:61
Definition SBuf.h:94
#define debugs(SECTION, LEVEL, CONTENT)
Definition Stream.h:192
http_hdr_sc_type
Definition forward.h:31
@ SC_OTHER
Definition forward.h:36
@ SC_CONTENT
Definition forward.h:35
@ SC_NO_STORE
Definition forward.h:32
@ SC_NO_STORE_REMOTE
Definition forward.h:33
@ SC_ENUM_END
Definition forward.h:37
@ SC_MAX_AGE
Definition forward.h:34
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition store.cc:855
int unsigned int
Definition stub_fd.cc:19
double xdiv(double nom, double denom)
Definition util.cc:53
#define safe_free(x)
Definition xalloc.h:73
char * xstrndup(const char *s, size_t n)
Definition xstring.cc:56