]>
Commit | Line | Data |
---|---|---|
43ae1d95 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 The Squid Software Foundation and contributors |
43ae1d95 | 3 | * |
bbc27441 AJ |
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. | |
43ae1d95 | 7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 90 HTTP Cache Control Header */ |
10 | ||
582c2af2 | 11 | #include "squid.h" |
4277583a | 12 | #include "base/LookupTable.h" |
1da82544 | 13 | //#include "HttpHdrSc.h" // pulled in by HttpHdrScTarget.h |
96d2a063 | 14 | #include "HttpHdrScTarget.h" |
43ae1d95 | 15 | #include "HttpHeader.h" |
db2de30a | 16 | #include "HttpHeaderFieldStat.h" |
e1656dc4 | 17 | #include "HttpHeaderStat.h" |
a5bac1d2 | 18 | #include "HttpHeaderTools.h" |
582c2af2 | 19 | #include "Store.h" |
28204b3b | 20 | #include "StrList.h" |
ed6e9fb9 | 21 | #include "util.h" |
43ae1d95 | 22 | |
45a58345 | 23 | #include <map> |
4277583a | 24 | #include <vector> |
45a58345 | 25 | |
43ae1d95 | 26 | /* this table is used for parsing surrogate control header */ |
45a58345 FC |
27 | /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */ |
28 | //todo: implement constraint | |
4277583a FC |
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 */ | |
26ac0430 | 36 | }; |
4277583a FC |
37 | LookupTable<http_hdr_sc_type> scLookupTable(SC_ENUM_END, ScAttrs); |
38 | std::vector<HttpHeaderFieldStat> scHeaderStats(SC_ENUM_END); | |
43ae1d95 | 39 | |
a9cfbb83 | 40 | // used when iterating over flags |
43ae1d95 | 41 | http_hdr_sc_type &operator++ (http_hdr_sc_type &aHeader) |
42 | { | |
a9cfbb83 FC |
43 | int tmp = static_cast<int>(aHeader); |
44 | aHeader = static_cast<http_hdr_sc_type>(++tmp); | |
43ae1d95 | 45 | return aHeader; |
46 | } | |
47 | ||
43ae1d95 | 48 | void |
49 | httpHdrScInitModule(void) | |
50 | { | |
4277583a FC |
51 | // check invariant on ScAttrs |
52 | for (int i = 0; ScAttrs[i].name != nullptr; ++i) | |
53 | assert(i == ScAttrs[i].id); | |
43ae1d95 | 54 | } |
55 | ||
43ae1d95 | 56 | /* implementation */ |
57 | ||
43ae1d95 | 58 | /* creates an sc object from a 0-terminating string */ |
59 | HttpHdrSc * | |
45a58345 | 60 | httpHdrScParseCreate(const String & str) |
43ae1d95 | 61 | { |
45a58345 | 62 | HttpHdrSc *sc = new HttpHdrSc(); |
43ae1d95 | 63 | |
45a58345 FC |
64 | if (!sc->parse(&str)) { |
65 | delete sc; | |
43ae1d95 | 66 | sc = NULL; |
67 | } | |
68 | ||
69 | return sc; | |
70 | } | |
71 | ||
72 | /* parses a 0-terminating string and inits sc */ | |
45a58345 FC |
73 | bool |
74 | HttpHdrSc::parse(const String * str) | |
43ae1d95 | 75 | { |
45a58345 | 76 | HttpHdrSc * sc=this; |
43ae1d95 | 77 | const char *item; |
f53969cc | 78 | const char *p; /* '=' parameter */ |
43ae1d95 | 79 | const char *pos = NULL; |
80 | const char *target = NULL; /* ;foo */ | |
81 | const char *temp = NULL; /* temp buffer */ | |
4277583a | 82 | http_hdr_sc_type type; |
34460e19 | 83 | int ilen, vlen; |
43ae1d95 | 84 | int initiallen; |
85 | HttpHdrScTarget *sct; | |
45a58345 | 86 | assert(str); |
43ae1d95 | 87 | |
88 | /* iterate through comma separated list */ | |
89 | ||
90 | while (strListGetItem(str, ',', &item, &ilen, &pos)) { | |
91 | initiallen = ilen; | |
34460e19 | 92 | vlen = 0; |
43ae1d95 | 93 | /* decrease ilen to still match the token for '=' statements */ |
94 | ||
34460e19 | 95 | if ((p = strchr(item, '=')) && (p - item < ilen)) { |
7c0f9f7e | 96 | vlen = ilen - (p + 1 - item); |
34460e19 | 97 | ilen = p - item; |
95dc7ff4 | 98 | ++p; |
34460e19 | 99 | } |
43ae1d95 | 100 | |
101 | /* decrease ilen to still match the token for ';' qualified non '=' statments */ | |
a38ec4b1 FC |
102 | else if ((p = strchr(item, ';')) && (p - item < ilen)) { |
103 | ilen = p - item; | |
104 | ++p; | |
105 | } | |
43ae1d95 | 106 | |
107 | /* find type */ | |
4277583a | 108 | type = scLookupTable.lookup(SBuf(item,ilen)); |
43ae1d95 | 109 | |
4277583a | 110 | if (type == SC_ENUM_END) { |
550cc09e | 111 | debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 112 | type = SC_OTHER; |
113 | } | |
114 | ||
115 | /* Is this a targeted directive? */ | |
45a58345 | 116 | /* TODO: remove the temporary useage and use memrchr and the information we have instead */ |
43ae1d95 | 117 | temp = xstrndup (item, initiallen + 1); |
118 | ||
119 | if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0')) | |
120 | target = NULL; | |
121 | else | |
122 | ++target; | |
123 | ||
45a58345 | 124 | sct = sc->findTarget(target); |
43ae1d95 | 125 | |
126 | if (!sct) { | |
45a58345 FC |
127 | sct = new HttpHdrScTarget(target); |
128 | addTarget(sct); | |
43ae1d95 | 129 | } |
130 | ||
131 | safe_free (temp); | |
132 | ||
4277583a | 133 | if (sct->isSet(type)) { |
43ae1d95 | 134 | if (type != SC_OTHER) |
550cc09e | 135 | debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 136 | |
4277583a | 137 | ++ scHeaderStats[type].repCount; |
43ae1d95 | 138 | |
139 | continue; | |
140 | } | |
141 | ||
45a58345 | 142 | /* process directives */ |
43ae1d95 | 143 | switch (type) { |
45a58345 FC |
144 | case SC_NO_STORE: |
145 | sct->noStore(true); | |
146 | break; | |
43ae1d95 | 147 | |
45a58345 FC |
148 | case SC_NO_STORE_REMOTE: |
149 | sct->noStoreRemote(true); | |
43ae1d95 | 150 | break; |
151 | ||
633b9845 A |
152 | case SC_MAX_AGE: { |
153 | int ma; | |
154 | if (p && httpHeaderParseInt(p, &ma)) { | |
155 | sct->maxAge(ma); | |
76d0bd38 AJ |
156 | |
157 | if ((p = strchr (p, '+'))) { | |
158 | int ms; | |
159 | ++p; //skip the + char | |
160 | if (httpHeaderParseInt(p, &ms)) { | |
161 | sct->maxStale(ms); | |
162 | } else { | |
163 | debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'"); | |
164 | sct->clearMaxStale(); | |
165 | /* leave the max-age alone */ | |
166 | } | |
167 | } | |
633b9845 A |
168 | } else { |
169 | debugs(90, 2, "sc: invalid max-age specs near '" << item << "'"); | |
170 | sct->clearMaxAge(); | |
171 | } | |
172 | ||
633b9845 A |
173 | break; |
174 | } | |
45a58345 | 175 | |
43ae1d95 | 176 | case SC_CONTENT: |
177 | ||
633b9845 A |
178 | if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) { |
179 | sct->setMask(SC_CONTENT,true); // ugly but saves a copy | |
180 | } else { | |
bf8fe701 | 181 | debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'"); |
45a58345 | 182 | sct->clearContent(); |
43ae1d95 | 183 | } |
633b9845 | 184 | break; |
43ae1d95 | 185 | |
45a58345 | 186 | case SC_OTHER: |
43ae1d95 | 187 | default: |
188 | break; | |
189 | } | |
190 | } | |
191 | ||
192 | return sc->targets.head != NULL; | |
193 | } | |
194 | ||
45a58345 | 195 | HttpHdrSc::~HttpHdrSc() |
43ae1d95 | 196 | { |
45a58345 FC |
197 | if (targets.head) { |
198 | dlink_node *sct = targets.head; | |
43ae1d95 | 199 | |
200 | while (sct) { | |
45a58345 | 201 | HttpHdrScTarget *t = static_cast<HttpHdrScTarget *>(sct->data); |
43ae1d95 | 202 | sct = sct->next; |
45a58345 FC |
203 | dlinkDelete (&t->node, &targets); |
204 | delete t; | |
43ae1d95 | 205 | } |
206 | } | |
43ae1d95 | 207 | } |
208 | ||
45a58345 | 209 | HttpHdrSc::HttpHdrSc(const HttpHdrSc &sc) |
43ae1d95 | 210 | { |
45a58345 | 211 | dlink_node *node = sc.targets.head; |
43ae1d95 | 212 | |
213 | while (node) { | |
45a58345 FC |
214 | HttpHdrScTarget *dupsct = new HttpHdrScTarget(*static_cast<HttpHdrScTarget *>(node->data)); |
215 | addTargetAtTail(dupsct); | |
43ae1d95 | 216 | node = node->next; |
217 | } | |
43ae1d95 | 218 | } |
219 | ||
220 | void | |
17802cf1 | 221 | HttpHdrScTarget::packInto(Packable * p) const |
43ae1d95 | 222 | { |
223 | http_hdr_sc_type flag; | |
224 | int pcount = 0; | |
45a58345 | 225 | assert (p); |
43ae1d95 | 226 | |
227 | for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) { | |
45a58345 | 228 | if (isSet(flag) && flag != SC_OTHER) { |
43ae1d95 | 229 | |
230 | /* print option name */ | |
4277583a | 231 | p->appendf((pcount ? ", %s" : "%s"), ScAttrs[flag].name); |
43ae1d95 | 232 | |
233 | /* handle options with values */ | |
234 | ||
235 | if (flag == SC_MAX_AGE) | |
4391cd15 | 236 | p->appendf("=%d", (int) max_age); |
43ae1d95 | 237 | |
238 | if (flag == SC_CONTENT) | |
4391cd15 | 239 | p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(content_)); |
43ae1d95 | 240 | |
95dc7ff4 | 241 | ++pcount; |
43ae1d95 | 242 | } |
243 | } | |
244 | ||
45a58345 | 245 | if (hasTarget()) |
4391cd15 | 246 | p->appendf(";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(target)); |
43ae1d95 | 247 | } |
248 | ||
249 | void | |
17802cf1 | 250 | HttpHdrSc::packInto(Packable * p) const |
43ae1d95 | 251 | { |
252 | dlink_node *node; | |
45a58345 FC |
253 | assert(p); |
254 | node = targets.head; | |
43ae1d95 | 255 | |
256 | while (node) { | |
633b9845 | 257 | static_cast<HttpHdrScTarget *>(node->data)->packInto(p); |
43ae1d95 | 258 | node = node->next; |
259 | } | |
260 | } | |
261 | ||
43ae1d95 | 262 | /* negative max_age will clean old max_Age setting */ |
263 | void | |
45a58345 | 264 | HttpHdrSc::setMaxAge(char const *target, int max_age) |
43ae1d95 | 265 | { |
45a58345 | 266 | HttpHdrScTarget *sct = findTarget(target); |
43ae1d95 | 267 | |
268 | if (!sct) { | |
45a58345 FC |
269 | sct = new HttpHdrScTarget(target); |
270 | dlinkAddTail (sct, &sct->node, &targets); | |
43ae1d95 | 271 | } |
272 | ||
45a58345 | 273 | sct->maxAge(max_age); |
43ae1d95 | 274 | } |
275 | ||
276 | void | |
45a58345 | 277 | HttpHdrSc::updateStats(StatHist * hist) const |
43ae1d95 | 278 | { |
45a58345 | 279 | dlink_node *sct = targets.head; |
43ae1d95 | 280 | |
281 | while (sct) { | |
633b9845 | 282 | static_cast<HttpHdrScTarget *>(sct->data)->updateStats(hist); |
43ae1d95 | 283 | sct = sct->next; |
284 | } | |
285 | } | |
286 | ||
287 | void | |
ced8def3 | 288 | httpHdrScTargetStatDumper(StoreEntry * sentry, int, double val, double, int count) |
43ae1d95 | 289 | { |
290 | extern const HttpHeaderStat *dump_stat; /* argh! */ | |
291 | const int id = (int) val; | |
4277583a FC |
292 | const bool valid_id = id >= 0 && id < SC_ENUM_END; |
293 | const char *name = valid_id ? ScAttrs[id].name : "INVALID"; | |
43ae1d95 | 294 | |
295 | if (count || valid_id) | |
296 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
297 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
298 | } | |
299 | ||
300 | void | |
ced8def3 | 301 | httpHdrScStatDumper(StoreEntry * sentry, int, double val, double, int count) |
43ae1d95 | 302 | { |
f53969cc | 303 | extern const HttpHeaderStat *dump_stat; /* argh! */ |
43ae1d95 | 304 | const int id = (int) val; |
4277583a FC |
305 | const bool valid_id = id >= 0 && id < SC_ENUM_END; |
306 | const char *name = valid_id ? ScAttrs[id].name : "INVALID"; | |
43ae1d95 | 307 | |
308 | if (count || valid_id) | |
309 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
310 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
311 | } | |
312 | ||
313 | HttpHdrScTarget * | |
45a58345 | 314 | HttpHdrSc::findTarget(const char *target) |
43ae1d95 | 315 | { |
316 | dlink_node *node; | |
45a58345 | 317 | node = targets.head; |
43ae1d95 | 318 | |
319 | while (node) { | |
320 | HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data; | |
321 | ||
a1377698 | 322 | if (target && sct->target.size() > 0 && !strcmp(target, sct->target.termedBuf())) |
43ae1d95 | 323 | return sct; |
a1377698 | 324 | else if (!target && sct->target.size() == 0) |
43ae1d95 | 325 | return sct; |
326 | ||
327 | node = node->next; | |
328 | } | |
329 | ||
330 | return NULL; | |
331 | } | |
332 | ||
333 | HttpHdrScTarget * | |
45a58345 | 334 | HttpHdrSc::getMergedTarget(const char *ourtarget) |
43ae1d95 | 335 | { |
45a58345 FC |
336 | HttpHdrScTarget *sctus = findTarget(ourtarget); |
337 | HttpHdrScTarget *sctgeneric = findTarget(NULL); | |
43ae1d95 | 338 | |
339 | if (sctgeneric || sctus) { | |
45a58345 | 340 | HttpHdrScTarget *sctusable = new HttpHdrScTarget(NULL); |
43ae1d95 | 341 | |
342 | if (sctgeneric) | |
45a58345 | 343 | sctusable->mergeWith(sctgeneric); |
43ae1d95 | 344 | |
345 | if (sctus) | |
45a58345 | 346 | sctusable->mergeWith(sctus); |
43ae1d95 | 347 | |
348 | return sctusable; | |
349 | } | |
350 | ||
351 | return NULL; | |
352 | } | |
f53969cc | 353 | |
b56ab9e7 FC |
354 | void |
355 | HttpHdrSc::addTarget(HttpHdrScTarget *t) { | |
356 | dlinkAdd(t, &t->node, &targets); | |
357 | } | |
358 | ||
359 | void | |
360 | HttpHdrSc::addTargetAtTail(HttpHdrScTarget *t) { | |
361 | dlinkAddTail (t, &t->node, &targets); | |
362 | } | |
96d2a063 | 363 |