]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrSc.cc
Fix parsing of max-stale values in Surrogate-Control header
[thirdparty/squid.git] / src / HttpHdrSc.cc
1
2 /*
3 * $Id$
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 * Francesco Chemolli (c++ refactoring)
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.
27 *
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.
32 *
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
39 #include "squid-old.h"
40 #include "Store.h"
41 #include "HttpHeader.h"
42 #include "HttpHeaderStat.h"
43 #include "HttpHdrSc.h"
44
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 {
51 const char *name;
52 http_hdr_sc_type id;
53 HttpHeaderFieldStat stat;
54 } HttpHeaderScFields;
55
56 /* this table is used for parsing surrogate control header */
57 /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
58 //todo: implement constraint
59 static const HttpHeaderFieldAttrs ScAttrs[SC_ENUM_END] = {
60 {"no-store", (http_hdr_type)SC_NO_STORE},
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 };
66
67 HttpHeaderFieldInfo *ScFieldsInfo = NULL;
68
69 http_hdr_sc_type &operator++ (http_hdr_sc_type &aHeader)
70 {
71 int tmp = (int)aHeader;
72 aHeader = (http_hdr_sc_type)(++tmp);
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
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
99 /* creates an sc object from a 0-terminating string */
100 HttpHdrSc *
101 httpHdrScParseCreate(const String & str)
102 {
103 HttpHdrSc *sc = new HttpHdrSc();
104
105 if (!sc->parse(&str)) {
106 delete sc;
107 sc = NULL;
108 }
109
110 return sc;
111 }
112
113 /* parses a 0-terminating string and inits sc */
114 bool
115 HttpHdrSc::parse(const String * str)
116 {
117 HttpHdrSc * sc=this;
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;
124 int ilen, vlen;
125 int initiallen;
126 HttpHdrScTarget *sct;
127 assert(str);
128
129 /* iterate through comma separated list */
130
131 while (strListGetItem(str, ',', &item, &ilen, &pos)) {
132 initiallen = ilen;
133 vlen = 0;
134 /* decrease ilen to still match the token for '=' statements */
135
136 if ((p = strchr(item, '=')) && (p - item < ilen)) {
137 vlen = ilen - (p + 1 - item);
138 ilen = p - item;
139 ++p;
140 }
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 ++p;
146 }
147
148 /* find type */
149 /* TODO: use a type-safe map-based lookup */
150 type = httpHeaderIdByName(item, ilen,
151 ScFieldsInfo, SC_ENUM_END);
152
153 if (type < 0) {
154 debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'");
155 type = SC_OTHER;
156 }
157
158 /* Is this a targeted directive? */
159 /* TODO: remove the temporary useage and use memrchr and the information we have instead */
160 temp = xstrndup (item, initiallen + 1);
161
162 if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0'))
163 target = NULL;
164 else
165 ++target;
166
167 sct = sc->findTarget(target);
168
169 if (!sct) {
170 sct = new HttpHdrScTarget(target);
171 addTarget(sct);
172 }
173
174 safe_free (temp);
175
176 if (sct->isSet(static_cast<http_hdr_sc_type>(type))) {
177 if (type != SC_OTHER)
178 debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'");
179
180 ++ ScFieldsInfo[type].stat.repCount;
181
182 continue;
183 }
184
185 /* process directives */
186 switch (type) {
187 case SC_NO_STORE:
188 sct->noStore(true);
189 break;
190
191 case SC_NO_STORE_REMOTE:
192 sct->noStoreRemote(true);
193 break;
194
195 case SC_MAX_AGE: {
196 int ma;
197 if (p && httpHeaderParseInt(p, &ma)) {
198 sct->maxAge(ma);
199
200 if ((p = strchr (p, '+'))) {
201 int ms;
202 ++p; //skip the + char
203 if (httpHeaderParseInt(p, &ms)) {
204 sct->maxStale(ms);
205 } else {
206 debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'");
207 sct->clearMaxStale();
208 /* leave the max-age alone */
209 }
210 }
211 } else {
212 debugs(90, 2, "sc: invalid max-age specs near '" << item << "'");
213 sct->clearMaxAge();
214 }
215
216 break;
217 }
218
219 case SC_CONTENT:
220
221 if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) {
222 sct->setMask(SC_CONTENT,true); // ugly but saves a copy
223 } else {
224 debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'");
225 sct->clearContent();
226 }
227 break;
228
229 case SC_OTHER:
230 default:
231 break;
232 }
233 }
234
235 return sc->targets.head != NULL;
236 }
237
238 HttpHdrSc::~HttpHdrSc()
239 {
240 if (targets.head) {
241 dlink_node *sct = targets.head;
242
243 while (sct) {
244 HttpHdrScTarget *t = static_cast<HttpHdrScTarget *>(sct->data);
245 sct = sct->next;
246 dlinkDelete (&t->node, &targets);
247 delete t;
248 }
249 }
250 }
251
252
253 HttpHdrSc::HttpHdrSc(const HttpHdrSc &sc)
254 {
255 dlink_node *node = sc.targets.head;
256
257 while (node) {
258 HttpHdrScTarget *dupsct = new HttpHdrScTarget(*static_cast<HttpHdrScTarget *>(node->data));
259 addTargetAtTail(dupsct);
260 node = node->next;
261 }
262 }
263
264 void
265 HttpHdrScTarget::packInto(Packer * p) const
266 {
267 http_hdr_sc_type flag;
268 int pcount = 0;
269 assert (p);
270
271 for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) {
272 if (isSet(flag) && flag != SC_OTHER) {
273
274 /* print option name */
275 packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH),
276 SQUIDSTRINGPRINT(ScFieldsInfo[flag].name));
277
278 /* handle options with values */
279
280 if (flag == SC_MAX_AGE)
281 packerPrintf(p, "=%d", (int) max_age);
282
283 if (flag == SC_CONTENT)
284 packerPrintf(p, "=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(content_));
285
286 ++pcount;
287 }
288 }
289
290 if (hasTarget())
291 packerPrintf (p, ";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(target));
292 }
293
294 void
295 HttpHdrSc::packInto(Packer * p) const
296 {
297 dlink_node *node;
298 assert(p);
299 node = targets.head;
300
301 while (node) {
302 static_cast<HttpHdrScTarget *>(node->data)->packInto(p);
303 node = node->next;
304 }
305 }
306
307 /* negative max_age will clean old max_Age setting */
308 void
309 HttpHdrSc::setMaxAge(char const *target, int max_age)
310 {
311 HttpHdrScTarget *sct = findTarget(target);
312
313 if (!sct) {
314 sct = new HttpHdrScTarget(target);
315 dlinkAddTail (sct, &sct->node, &targets);
316 }
317
318 sct->maxAge(max_age);
319 }
320
321 void
322 HttpHdrSc::updateStats(StatHist * hist) const
323 {
324 dlink_node *sct = targets.head;
325
326 while (sct) {
327 static_cast<HttpHdrScTarget *>(sct->data)->updateStats(hist);
328 sct = sct->next;
329 }
330 }
331
332 void
333 httpHdrScTargetStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
334 {
335 extern const HttpHeaderStat *dump_stat; /* argh! */
336 const int id = (int) val;
337 const int valid_id = id >= 0 && id < SC_ENUM_END;
338 const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
339
340 if (count || valid_id)
341 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
342 id, name, count, xdiv(count, dump_stat->scParsedCount));
343 }
344
345 void
346 httpHdrScStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
347 {
348 extern const HttpHeaderStat *dump_stat; /* argh! */
349 const int id = (int) val;
350 const int valid_id = id >= 0 && id < SC_ENUM_END;
351 const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
352
353 if (count || valid_id)
354 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
355 id, name, count, xdiv(count, dump_stat->scParsedCount));
356 }
357
358 HttpHdrScTarget *
359 HttpHdrSc::findTarget(const char *target)
360 {
361 dlink_node *node;
362 node = targets.head;
363
364 while (node) {
365 HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data;
366
367 if (target && sct->target.defined() && !strcmp (target, sct->target.termedBuf()))
368 return sct;
369 else if (!target && sct->target.undefined())
370 return sct;
371
372 node = node->next;
373 }
374
375 return NULL;
376 }
377
378 HttpHdrScTarget *
379 HttpHdrSc::getMergedTarget(const char *ourtarget)
380 {
381 HttpHdrScTarget *sctus = findTarget(ourtarget);
382 HttpHdrScTarget *sctgeneric = findTarget(NULL);
383
384 if (sctgeneric || sctus) {
385 HttpHdrScTarget *sctusable = new HttpHdrScTarget(NULL);
386
387 if (sctgeneric)
388 sctusable->mergeWith(sctgeneric);
389
390 if (sctus)
391 sctusable->mergeWith(sctus);
392
393 return sctusable;
394 }
395
396 return NULL;
397 }