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