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