]>
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 | 130 | if ((p = strchr(item, '=')) && (p - item < ilen)) { |
7c0f9f7e | 131 | vlen = ilen - (p + 1 - item); |
34460e19 | 132 | ilen = p - item; |
7c0f9f7e | 133 | p++; |
34460e19 | 134 | } |
43ae1d95 | 135 | |
136 | /* decrease ilen to still match the token for ';' qualified non '=' statments */ | |
137 | else if ((p = strchr(item, ';')) && (p - item < ilen)) | |
138 | ilen = p++ - item; | |
139 | ||
140 | /* find type */ | |
30abd221 | 141 | type = httpHeaderIdByName(item, ilen, |
142 | ScFieldsInfo, SC_ENUM_END); | |
43ae1d95 | 143 | |
144 | if (type < 0) { | |
550cc09e | 145 | debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 146 | type = SC_OTHER; |
147 | } | |
148 | ||
149 | /* Is this a targeted directive? */ | |
150 | /* TODO sometime: implement a strnrchr that looks at a substring */ | |
151 | temp = xstrndup (item, initiallen + 1); | |
152 | ||
153 | if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0')) | |
154 | target = NULL; | |
155 | else | |
156 | ++target; | |
157 | ||
158 | sct = httpHdrScFindTarget (sc, target); | |
159 | ||
160 | if (!sct) { | |
161 | sct = httpHdrScTargetCreate (target); | |
162 | dlinkAdd(sct, &sct->node, &sc->targets); | |
163 | } | |
164 | ||
165 | safe_free (temp); | |
166 | ||
167 | if (EBIT_TEST(sct->mask, type)) { | |
168 | if (type != SC_OTHER) | |
550cc09e | 169 | debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'"); |
43ae1d95 | 170 | |
171 | ScFieldsInfo[type].stat.repCount++; | |
172 | ||
173 | continue; | |
174 | } | |
175 | ||
176 | /* update mask */ | |
177 | EBIT_SET(sct->mask, type); | |
178 | ||
179 | /* post-processing special cases */ | |
180 | switch (type) { | |
181 | ||
182 | case SC_MAX_AGE: | |
183 | ||
184 | if (!p || !httpHeaderParseInt(p, &sct->max_age)) { | |
bf8fe701 | 185 | debugs(90, 2, "sc: invalid max-age specs near '" << item << "'"); |
43ae1d95 | 186 | sct->max_age = -1; |
187 | EBIT_CLR(sct->mask, type); | |
188 | } | |
189 | ||
190 | if ((p = strchr (p, '+'))) | |
191 | if (!httpHeaderParseInt(++p, &sct->max_stale)) { | |
bf8fe701 | 192 | debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'"); |
43ae1d95 | 193 | sct->max_stale = 0; |
194 | /* leave the max-age alone */ | |
195 | } | |
196 | ||
197 | break; | |
198 | ||
199 | case SC_CONTENT: | |
200 | ||
34460e19 | 201 | if (!p || !httpHeaderParseQuotedString(p, vlen, &sct->content)) { |
bf8fe701 | 202 | debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'"); |
30abd221 | 203 | sct->content.clean(); |
43ae1d95 | 204 | EBIT_CLR(sct->mask, type); |
205 | } | |
206 | ||
207 | default: | |
208 | break; | |
209 | } | |
210 | } | |
211 | ||
212 | return sc->targets.head != NULL; | |
213 | } | |
214 | ||
215 | void | |
216 | httpHdrScDestroy(HttpHdrSc * sc) | |
217 | { | |
218 | assert(sc); | |
219 | ||
220 | if (sc->targets.head) { | |
221 | dlink_node *sct = sc->targets.head; | |
222 | ||
223 | while (sct) { | |
224 | HttpHdrScTarget *t = (HttpHdrScTarget *)sct->data; | |
225 | sct = sct->next; | |
226 | dlinkDelete (&t->node, &sc->targets); | |
227 | httpHdrScTargetDestroy (t); | |
228 | } | |
229 | } | |
230 | ||
25b6a907 | 231 | delete sc; |
43ae1d95 | 232 | } |
233 | ||
234 | HttpHdrSc * | |
235 | httpHdrScDup(const HttpHdrSc * sc) | |
236 | { | |
237 | HttpHdrSc *dup; | |
238 | dlink_node *node; | |
239 | assert(sc); | |
240 | node = sc->targets.head; | |
241 | dup = httpHdrScCreate(); | |
242 | ||
243 | while (node) { | |
244 | HttpHdrScTarget *dupsct; | |
245 | dupsct = httpHdrScTargetDup ((HttpHdrScTarget *)node->data); | |
246 | dlinkAddTail (dupsct, &dupsct->node, &dup->targets); | |
247 | node = node->next; | |
248 | } | |
249 | ||
250 | return dup; | |
251 | } | |
252 | ||
253 | void | |
254 | httpHdrScTargetPackInto(const HttpHdrScTarget * sc, Packer * p) | |
255 | { | |
256 | http_hdr_sc_type flag; | |
257 | int pcount = 0; | |
258 | assert(sc && p); | |
259 | ||
260 | for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) { | |
261 | if (EBIT_TEST(sc->mask, flag) && flag != SC_OTHER) { | |
262 | ||
263 | /* print option name */ | |
826a1fed | 264 | packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), |
af6a12ee | 265 | SQUIDSTRINGPRINT(ScFieldsInfo[flag].name)); |
43ae1d95 | 266 | |
267 | /* handle options with values */ | |
268 | ||
269 | if (flag == SC_MAX_AGE) | |
270 | packerPrintf(p, "=%d", (int) sc->max_age); | |
271 | ||
272 | if (flag == SC_CONTENT) | |
826a1fed | 273 | packerPrintf(p, "=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(sc->content)); |
43ae1d95 | 274 | |
275 | pcount++; | |
276 | } | |
277 | } | |
278 | ||
279 | if (sc->target.size()) | |
826a1fed | 280 | packerPrintf (p, ";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(sc->target)); |
43ae1d95 | 281 | } |
282 | ||
283 | void | |
284 | httpHdrScPackInto(const HttpHdrSc * sc, Packer * p) | |
285 | { | |
286 | dlink_node *node; | |
287 | assert(sc && p); | |
288 | node = sc->targets.head; | |
289 | ||
290 | while (node) { | |
291 | httpHdrScTargetPackInto((HttpHdrScTarget *)node->data, p); | |
292 | node = node->next; | |
293 | } | |
294 | } | |
295 | ||
296 | void | |
297 | httpHdrScJoinWith(HttpHdrSc * sc, const HttpHdrSc * new_sc) | |
298 | { | |
299 | assert(sc && new_sc); | |
300 | #if 0 | |
301 | /* RC TODO: check that both have the same target */ | |
302 | ||
303 | if (sc->max_age < 0) | |
304 | sc->max_age = new_sc->max_age; | |
305 | ||
306 | /* RC TODO: copy unique missing stringlist entries */ | |
307 | cc->mask |= new_cc->mask; | |
308 | ||
309 | #endif | |
310 | } | |
311 | ||
312 | /* negative max_age will clean old max_Age setting */ | |
313 | void | |
314 | httpHdrScSetMaxAge(HttpHdrSc * sc, char const *target, int max_age) | |
315 | { | |
316 | HttpHdrScTarget *sct; | |
317 | assert(sc); | |
318 | sct = httpHdrScFindTarget (sc, target); | |
319 | ||
320 | if (!sct) { | |
321 | sct = httpHdrScTargetCreate (target); | |
322 | dlinkAddTail (sct, &sct->node, &sc->targets); | |
323 | } | |
324 | ||
325 | httpHdrScTargetSetMaxAge(sct, max_age); | |
326 | } | |
327 | ||
328 | void | |
329 | httpHdrScUpdateStats(const HttpHdrSc * sc, StatHist * hist) | |
330 | { | |
331 | dlink_node *sct; | |
332 | assert(sc); | |
333 | sct = sc->targets.head; | |
334 | ||
335 | while (sct) { | |
336 | httpHdrScTargetUpdateStats((HttpHdrScTarget *)sct->data, hist); | |
337 | sct = sct->next; | |
338 | } | |
339 | } | |
340 | ||
341 | void | |
342 | httpHdrScTargetStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) | |
343 | { | |
344 | extern const HttpHeaderStat *dump_stat; /* argh! */ | |
345 | const int id = (int) val; | |
346 | const int valid_id = id >= 0 && id < SC_ENUM_END; | |
550cc09e | 347 | const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID"; |
43ae1d95 | 348 | |
349 | if (count || valid_id) | |
350 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
351 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
352 | } | |
353 | ||
354 | void | |
355 | httpHdrScStatDumper(StoreEntry * sentry, int idx, double val, double size, int count) | |
356 | { | |
357 | extern const HttpHeaderStat *dump_stat; /* argh! */ | |
358 | const int id = (int) val; | |
359 | const int valid_id = id >= 0 && id < SC_ENUM_END; | |
550cc09e | 360 | const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID"; |
43ae1d95 | 361 | |
362 | if (count || valid_id) | |
363 | storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n", | |
364 | id, name, count, xdiv(count, dump_stat->scParsedCount)); | |
365 | } | |
366 | ||
367 | HttpHdrScTarget * | |
368 | httpHdrScFindTarget (HttpHdrSc *sc, const char *target) | |
369 | { | |
370 | dlink_node *node; | |
371 | assert (sc); | |
372 | node = sc->targets.head; | |
373 | ||
374 | while (node) { | |
375 | HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data; | |
376 | ||
550cc09e | 377 | if (target && sct->target.defined() && !strcmp (target, sct->target.termedBuf())) |
43ae1d95 | 378 | return sct; |
550cc09e | 379 | else if (!target && sct->target.undefined()) |
43ae1d95 | 380 | return sct; |
381 | ||
382 | node = node->next; | |
383 | } | |
384 | ||
385 | return NULL; | |
386 | } | |
387 | ||
388 | HttpHdrScTarget * | |
389 | httpHdrScGetMergedTarget (HttpHdrSc *sc, const char *ourtarget) | |
390 | { | |
391 | HttpHdrScTarget *sctus = httpHdrScFindTarget (sc, ourtarget); | |
392 | HttpHdrScTarget *sctgeneric = httpHdrScFindTarget (sc, NULL); | |
393 | ||
394 | if (sctgeneric || sctus) { | |
395 | HttpHdrScTarget *sctusable = httpHdrScTargetCreate (NULL); | |
396 | ||
397 | if (sctgeneric) | |
398 | httpHdrScTargetMergeWith (sctusable, sctgeneric); | |
399 | ||
400 | if (sctus) | |
401 | httpHdrScTargetMergeWith (sctusable, sctus); | |
402 | ||
403 | return sctusable; | |
404 | } | |
405 | ||
406 | return NULL; | |
407 | } |