]>
Commit | Line | Data |
---|---|---|
43ae1d95 | 1 | |
2 | /* | |
262a0e14 | 3 | * $Id$ |
43ae1d95 | 4 | * |
5 | * DEBUG: section 90 HTTP Cache Control Header | |
6 | * AUTHOR: Alex Rousskov | |
7 | * Robert Collins (Surrogate-Control is derived from | |
8 | * Cache-Control). | |
45a58345 | 9 | * Francesco Chemolli (c++ refactoring) |
43ae1d95 | 10 | * |
11 | * SQUID Web Proxy Cache http://www.squid-cache.org/ | |
12 | * ---------------------------------------------------------- | |
13 | * | |
14 | * Squid is the result of efforts by numerous individuals from | |
15 | * the Internet community; see the CONTRIBUTORS file for full | |
16 | * details. Many organizations have provided support for Squid's | |
17 | * development; see the SPONSORS file for full details. Squid is | |
18 | * Copyrighted (C) 2001 by the Regents of the University of | |
19 | * California; see the COPYRIGHT file for full details. Squid | |
20 | * incorporates software developed and/or copyrighted by other | |
21 | * sources; see the CREDITS file for full details. | |
22 | * | |
23 | * This program is free software; you can redistribute it and/or modify | |
24 | * it under the terms of the GNU General Public License as published by | |
25 | * the Free Software Foundation; either version 2 of the License, or | |
26 | * (at your option) any later version. | |
26ac0430 | 27 | * |
43ae1d95 | 28 | * This program is distributed in the hope that it will be useful, |
29 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
30 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
31 | * GNU General Public License for more details. | |
26ac0430 | 32 | * |
43ae1d95 | 33 | * You should have received a copy of the GNU General Public License |
34 | * along with this program; if not, write to the Free Software | |
35 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. | |
36 | * | |
37 | */ | |
38 | ||
f7f3304a | 39 | #include "squid-old.h" |
43ae1d95 | 40 | #include "Store.h" |
41 | #include "HttpHeader.h" | |
e1656dc4 | 42 | #include "HttpHeaderStat.h" |
25b6a907 | 43 | #include "HttpHdrSc.h" |
43ae1d95 | 44 | |
45a58345 FC |
45 | #if HAVE_MAP |
46 | #include <map> | |
47 | #endif | |
48 | ||
49 | /* a row in the table used for parsing surrogate-control header and statistics */ | |
50 | typedef struct { | |
633b9845 A |
51 | const char *name; |
52 | http_hdr_sc_type id; | |
53 | HttpHeaderFieldStat stat; | |
45a58345 FC |
54 | } HttpHeaderScFields; |
55 | ||
43ae1d95 | 56 | /* this table is used for parsing surrogate control header */ |
45a58345 FC |
57 | /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */ |
58 | //todo: implement constraint | |
26ac0430 AJ |
59 | static const HttpHeaderFieldAttrs ScAttrs[SC_ENUM_END] = { |
60 | {"no-store", (http_hdr_type)SC_NO_STORE}, | |
26ac0430 AJ |
61 | {"no-store-remote", (http_hdr_type)SC_NO_STORE_REMOTE}, |
62 | {"max-age", (http_hdr_type)SC_MAX_AGE}, | |
63 | {"content", (http_hdr_type)SC_CONTENT}, | |
64 | {"Other,", (http_hdr_type)SC_OTHER} /* ',' will protect from matches */ | |
65 | }; | |
43ae1d95 | 66 | |
67 | HttpHeaderFieldInfo *ScFieldsInfo = NULL; | |
68 | ||
69 | http_hdr_sc_type &operator++ (http_hdr_sc_type &aHeader) | |
70 | { | |
892e7ba7 | 71 | int tmp = (int)aHeader; |
72 | aHeader = (http_hdr_sc_type)(++tmp); | |
43ae1d95 | 73 | return aHeader; |
74 | } | |
75 | ||
76 | int operator - (http_hdr_sc_type const &anSc, http_hdr_sc_type const &anSc2) | |
77 | { | |
78 | return (int)anSc - (int)anSc2; | |
79 | } | |
80 | ||
81 | ||
43ae1d95 | 82 | /* module initialization */ |
83 | ||
84 | void | |
85 | httpHdrScInitModule(void) | |
86 | { | |
87 | ScFieldsInfo = httpHeaderBuildFieldsInfo(ScAttrs, SC_ENUM_END); | |
88 | } | |
89 | ||
90 | void | |
91 | httpHdrScCleanModule(void) | |
92 | { | |
93 | httpHeaderDestroyFieldsInfo(ScFieldsInfo, SC_ENUM_END); | |
94 | ScFieldsInfo = NULL; | |
95 | } | |
96 | ||
97 | /* implementation */ | |
98 | ||
43ae1d95 | 99 | /* creates an sc object from a 0-terminating string */ |
100 | HttpHdrSc * | |
45a58345 | 101 | httpHdrScParseCreate(const String & str) |
43ae1d95 | 102 | { |
45a58345 | 103 | HttpHdrSc *sc = new HttpHdrSc(); |
43ae1d95 | 104 | |
45a58345 FC |
105 | if (!sc->parse(&str)) { |
106 | delete sc; | |
43ae1d95 | 107 | sc = NULL; |
108 | } | |
109 | ||
110 | return sc; | |
111 | } | |
112 | ||
113 | /* parses a 0-terminating string and inits sc */ | |
45a58345 FC |
114 | bool |
115 | HttpHdrSc::parse(const String * str) | |
43ae1d95 | 116 | { |
45a58345 | 117 | HttpHdrSc * sc=this; |
43ae1d95 | 118 | const char *item; |
119 | const char *p; /* '=' parameter */ | |
120 | const char *pos = NULL; | |
121 | const char *target = NULL; /* ;foo */ | |
122 | const char *temp = NULL; /* temp buffer */ | |
123 | int type; | |
34460e19 | 124 | int ilen, vlen; |
43ae1d95 | 125 | int initiallen; |
126 | HttpHdrScTarget *sct; | |
45a58345 | 127 | assert(str); |
43ae1d95 | 128 | |
129 | /* iterate through comma separated list */ | |
130 | ||
131 | while (strListGetItem(str, ',', &item, &ilen, &pos)) { | |
132 | initiallen = ilen; | |
34460e19 | 133 | vlen = 0; |
43ae1d95 | 134 | /* decrease ilen to still match the token for '=' statements */ |
135 | ||
34460e19 | 136 | if ((p = strchr(item, '=')) && (p - item < ilen)) { |
7c0f9f7e | 137 | vlen = ilen - (p + 1 - item); |
34460e19 | 138 | ilen = p - item; |
7c0f9f7e | 139 | p++; |
34460e19 | 140 | } |
43ae1d95 | 141 | |
142 | /* decrease ilen to still match the token for ';' qualified non '=' statments */ | |
143 | else if ((p = strchr(item, ';')) && (p - item < ilen)) | |
144 | ilen = p++ - item; | |
145 | ||
146 | /* find type */ | |
45a58345 | 147 | /* TODO: use a type-safe map-based lookup */ |
30abd221 | 148 | type = httpHeaderIdByName(item, ilen, |
149 | ScFieldsInfo, SC_ENUM_END); | |
43ae1d95 | 150 | |
151 | if (type < 0) { | |
550cc09e | 152 | debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 153 | type = SC_OTHER; |
154 | } | |
155 | ||
156 | /* Is this a targeted directive? */ | |
45a58345 | 157 | /* TODO: remove the temporary useage and use memrchr and the information we have instead */ |
43ae1d95 | 158 | temp = xstrndup (item, initiallen + 1); |
159 | ||
160 | if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0')) | |
161 | target = NULL; | |
162 | else | |
163 | ++target; | |
164 | ||
45a58345 | 165 | sct = sc->findTarget(target); |
43ae1d95 | 166 | |
167 | if (!sct) { | |
45a58345 FC |
168 | sct = new HttpHdrScTarget(target); |
169 | addTarget(sct); | |
43ae1d95 | 170 | } |
171 | ||
172 | safe_free (temp); | |
173 | ||
45a58345 | 174 | if (sct->isSet(static_cast<http_hdr_sc_type>(type))) { |
43ae1d95 | 175 | if (type != SC_OTHER) |
550cc09e | 176 | debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 177 | |
178 | ScFieldsInfo[type].stat.repCount++; | |
179 | ||
180 | continue; | |
181 | } | |
182 | ||
45a58345 | 183 | /* process directives */ |
43ae1d95 | 184 | switch (type) { |
45a58345 FC |
185 | case SC_NO_STORE: |
186 | sct->noStore(true); | |
187 | break; | |
43ae1d95 | 188 | |
45a58345 FC |
189 | case SC_NO_STORE_REMOTE: |
190 | sct->noStoreRemote(true); | |
43ae1d95 | 191 | break; |
192 | ||
633b9845 A |
193 | case SC_MAX_AGE: { |
194 | int ma; | |
195 | if (p && httpHeaderParseInt(p, &ma)) { | |
196 | sct->maxAge(ma); | |
197 | } else { | |
198 | debugs(90, 2, "sc: invalid max-age specs near '" << item << "'"); | |
199 | sct->clearMaxAge(); | |
200 | } | |
201 | ||
202 | if ((p = strchr (p, '+'))) { | |
203 | int ms; | |
204 | ++p; //skip the + char | |
205 | if (httpHeaderParseInt(p, &ms)) { | |
206 | sct->maxStale(ms); | |
207 | } else { | |
208 | debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'"); | |
209 | sct->clearMaxStale(); | |
210 | /* leave the max-age alone */ | |
211 | } | |
212 | } | |
213 | break; | |
214 | } | |
45a58345 | 215 | |
43ae1d95 | 216 | case SC_CONTENT: |
217 | ||
633b9845 A |
218 | if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) { |
219 | sct->setMask(SC_CONTENT,true); // ugly but saves a copy | |
220 | } else { | |
bf8fe701 | 221 | debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'"); |
45a58345 | 222 | sct->clearContent(); |
43ae1d95 | 223 | } |
633b9845 | 224 | break; |
43ae1d95 | 225 | |
45a58345 | 226 | case SC_OTHER: |
43ae1d95 | 227 | default: |
228 | break; | |
229 | } | |
230 | } | |
231 | ||
232 | return sc->targets.head != NULL; | |
233 | } | |
234 | ||
45a58345 | 235 | HttpHdrSc::~HttpHdrSc() |
43ae1d95 | 236 | { |
45a58345 FC |
237 | if (targets.head) { |
238 | dlink_node *sct = targets.head; | |
43ae1d95 | 239 | |
240 | while (sct) { | |
45a58345 | 241 | HttpHdrScTarget *t = static_cast<HttpHdrScTarget *>(sct->data); |
43ae1d95 | 242 | sct = sct->next; |
45a58345 FC |
243 | dlinkDelete (&t->node, &targets); |
244 | delete t; | |
43ae1d95 | 245 | } |
246 | } | |
43ae1d95 | 247 | } |
248 | ||
45a58345 FC |
249 | |
250 | HttpHdrSc::HttpHdrSc(const HttpHdrSc &sc) | |
43ae1d95 | 251 | { |
45a58345 | 252 | dlink_node *node = sc.targets.head; |
43ae1d95 | 253 | |
254 | while (node) { | |
45a58345 FC |
255 | HttpHdrScTarget *dupsct = new HttpHdrScTarget(*static_cast<HttpHdrScTarget *>(node->data)); |
256 | addTargetAtTail(dupsct); | |
43ae1d95 | 257 | node = node->next; |
258 | } | |
43ae1d95 | 259 | } |
260 | ||
261 | void | |
45a58345 | 262 | HttpHdrScTarget::packInto(Packer * p) const |
43ae1d95 | 263 | { |
264 | http_hdr_sc_type flag; | |
265 | int pcount = 0; | |
45a58345 | 266 | assert (p); |
43ae1d95 | 267 | |
268 | for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) { | |
45a58345 | 269 | if (isSet(flag) && flag != SC_OTHER) { |
43ae1d95 | 270 | |
271 | /* print option name */ | |
826a1fed | 272 | packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), |
af6a12ee | 273 | SQUIDSTRINGPRINT(ScFieldsInfo[flag].name)); |
43ae1d95 | 274 | |
275 | /* handle options with values */ | |
276 | ||
277 | if (flag == SC_MAX_AGE) | |
45a58345 | 278 | packerPrintf(p, "=%d", (int) max_age); |
43ae1d95 | 279 | |
280 | if (flag == SC_CONTENT) | |
45a58345 | 281 | packerPrintf(p, "=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(content_)); |
43ae1d95 | 282 | |
283 | pcount++; | |
284 | } | |
285 | } | |
286 | ||
45a58345 FC |
287 | if (hasTarget()) |
288 | packerPrintf (p, ";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(target)); | |
43ae1d95 | 289 | } |
290 | ||
291 | void | |
45a58345 | 292 | HttpHdrSc::packInto(Packer * p) const |
43ae1d95 | 293 | { |
294 | dlink_node *node; | |
45a58345 FC |
295 | assert(p); |
296 | node = targets.head; | |
43ae1d95 | 297 | |
298 | while (node) { | |
633b9845 | 299 | static_cast<HttpHdrScTarget *>(node->data)->packInto(p); |
43ae1d95 | 300 | node = node->next; |
301 | } | |
302 | } | |
303 | ||
43ae1d95 | 304 | /* negative max_age will clean old max_Age setting */ |
305 | void | |
45a58345 | 306 | HttpHdrSc::setMaxAge(char const *target, int max_age) |
43ae1d95 | 307 | { |
45a58345 | 308 | HttpHdrScTarget *sct = findTarget(target); |
43ae1d95 | 309 | |
310 | if (!sct) { | |
45a58345 FC |
311 | sct = new HttpHdrScTarget(target); |
312 | dlinkAddTail (sct, &sct->node, &targets); | |
43ae1d95 | 313 | } |
314 | ||
45a58345 | 315 | sct->maxAge(max_age); |
43ae1d95 | 316 | } |
317 | ||
318 | void | |
45a58345 | 319 | HttpHdrSc::updateStats(StatHist * hist) const |
43ae1d95 | 320 | { |
45a58345 | 321 | dlink_node *sct = targets.head; |
43ae1d95 | 322 | |
323 | while (sct) { | |
633b9845 | 324 | static_cast<HttpHdrScTarget *>(sct->data)->updateStats(hist); |
43ae1d95 | 325 | sct = sct->next; |
326 | } | |
327 | } | |
328 | ||
329 | void | |
330 | httpHdrScTargetStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) | |
331 | { | |
332 | extern const HttpHeaderStat *dump_stat; /* argh! */ | |
333 | const int id = (int) val; | |
334 | const int valid_id = id >= 0 && id < SC_ENUM_END; | |
550cc09e | 335 | const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID"; |
43ae1d95 | 336 | |
337 | if (count || valid_id) | |
338 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
339 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
340 | } | |
341 | ||
342 | void | |
343 | httpHdrScStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) | |
344 | { | |
345 | extern const HttpHeaderStat *dump_stat; /* argh! */ | |
346 | const int id = (int) val; | |
347 | const int valid_id = id >= 0 && id < SC_ENUM_END; | |
550cc09e | 348 | const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID"; |
43ae1d95 | 349 | |
350 | if (count || valid_id) | |
351 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
352 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
353 | } | |
354 | ||
355 | HttpHdrScTarget * | |
45a58345 | 356 | HttpHdrSc::findTarget(const char *target) |
43ae1d95 | 357 | { |
358 | dlink_node *node; | |
45a58345 | 359 | node = targets.head; |
43ae1d95 | 360 | |
361 | while (node) { | |
362 | HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data; | |
363 | ||
550cc09e | 364 | if (target && sct->target.defined() && !strcmp (target, sct->target.termedBuf())) |
43ae1d95 | 365 | return sct; |
550cc09e | 366 | else if (!target && sct->target.undefined()) |
43ae1d95 | 367 | return sct; |
368 | ||
369 | node = node->next; | |
370 | } | |
371 | ||
372 | return NULL; | |
373 | } | |
374 | ||
375 | HttpHdrScTarget * | |
45a58345 | 376 | HttpHdrSc::getMergedTarget(const char *ourtarget) |
43ae1d95 | 377 | { |
45a58345 FC |
378 | HttpHdrScTarget *sctus = findTarget(ourtarget); |
379 | HttpHdrScTarget *sctgeneric = findTarget(NULL); | |
43ae1d95 | 380 | |
381 | if (sctgeneric || sctus) { | |
45a58345 | 382 | HttpHdrScTarget *sctusable = new HttpHdrScTarget(NULL); |
43ae1d95 | 383 | |
384 | if (sctgeneric) | |
45a58345 | 385 | sctusable->mergeWith(sctgeneric); |
43ae1d95 | 386 | |
387 | if (sctus) | |
45a58345 | 388 | sctusable->mergeWith(sctus); |
43ae1d95 | 389 | |
390 | return sctusable; | |
391 | } | |
392 | ||
393 | return NULL; | |
394 | } |