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