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