]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrSc.cc
Boilerplate: update copyright blurbs on src/
[thirdparty/squid.git] / src / HttpHdrSc.cc
1 /*
2 * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
3 *
4 * Squid software is distributed under GPLv2+ license and includes
5 * contributions from numerous individuals and organizations.
6 * Please see the COPYING and CONTRIBUTORS files for details.
7 */
8
9 /* DEBUG: section 90 HTTP Cache Control Header */
10
11 #include "squid.h"
12 #include "HttpHdrSc.h"
13 #include "HttpHeader.h"
14 #include "HttpHeaderFieldInfo.h"
15 #include "HttpHeaderFieldStat.h"
16 #include "HttpHeaderStat.h"
17 #include "HttpHeaderTools.h"
18 #include "Store.h"
19 #include "StrList.h"
20
21 #include <map>
22
23 /* a row in the table used for parsing surrogate-control header and statistics */
24 typedef struct {
25 const char *name;
26 http_hdr_sc_type id;
27 HttpHeaderFieldStat stat;
28 } HttpHeaderScFields;
29
30 /* this table is used for parsing surrogate control header */
31 /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
32 //todo: implement constraint
33 static const HttpHeaderFieldAttrs ScAttrs[SC_ENUM_END] = {
34 {"no-store", (http_hdr_type)SC_NO_STORE},
35 {"no-store-remote", (http_hdr_type)SC_NO_STORE_REMOTE},
36 {"max-age", (http_hdr_type)SC_MAX_AGE},
37 {"content", (http_hdr_type)SC_CONTENT},
38 {"Other,", (http_hdr_type)SC_OTHER} /* ',' will protect from matches */
39 };
40
41 HttpHeaderFieldInfo *ScFieldsInfo = NULL;
42
43 http_hdr_sc_type &operator++ (http_hdr_sc_type &aHeader)
44 {
45 int tmp = (int)aHeader;
46 aHeader = (http_hdr_sc_type)(++tmp);
47 return aHeader;
48 }
49
50 int operator - (http_hdr_sc_type const &anSc, http_hdr_sc_type const &anSc2)
51 {
52 return (int)anSc - (int)anSc2;
53 }
54
55 /* module initialization */
56
57 void
58 httpHdrScInitModule(void)
59 {
60 ScFieldsInfo = httpHeaderBuildFieldsInfo(ScAttrs, SC_ENUM_END);
61 }
62
63 void
64 httpHdrScCleanModule(void)
65 {
66 httpHeaderDestroyFieldsInfo(ScFieldsInfo, SC_ENUM_END);
67 ScFieldsInfo = NULL;
68 }
69
70 /* implementation */
71
72 /* creates an sc object from a 0-terminating string */
73 HttpHdrSc *
74 httpHdrScParseCreate(const String & str)
75 {
76 HttpHdrSc *sc = new HttpHdrSc();
77
78 if (!sc->parse(&str)) {
79 delete sc;
80 sc = NULL;
81 }
82
83 return sc;
84 }
85
86 /* parses a 0-terminating string and inits sc */
87 bool
88 HttpHdrSc::parse(const String * str)
89 {
90 HttpHdrSc * sc=this;
91 const char *item;
92 const char *p; /* '=' parameter */
93 const char *pos = NULL;
94 const char *target = NULL; /* ;foo */
95 const char *temp = NULL; /* temp buffer */
96 int type;
97 int ilen, vlen;
98 int initiallen;
99 HttpHdrScTarget *sct;
100 assert(str);
101
102 /* iterate through comma separated list */
103
104 while (strListGetItem(str, ',', &item, &ilen, &pos)) {
105 initiallen = ilen;
106 vlen = 0;
107 /* decrease ilen to still match the token for '=' statements */
108
109 if ((p = strchr(item, '=')) && (p - item < ilen)) {
110 vlen = ilen - (p + 1 - item);
111 ilen = p - item;
112 ++p;
113 }
114
115 /* decrease ilen to still match the token for ';' qualified non '=' statments */
116 else if ((p = strchr(item, ';')) && (p - item < ilen)) {
117 ilen = p - item;
118 ++p;
119 }
120
121 /* find type */
122 /* TODO: use a type-safe map-based lookup */
123 type = httpHeaderIdByName(item, ilen,
124 ScFieldsInfo, SC_ENUM_END);
125
126 if (type < 0) {
127 debugs(90, 2, "hdr sc: unknown control-directive: near '" << item << "' in '" << str << "'");
128 type = SC_OTHER;
129 }
130
131 /* Is this a targeted directive? */
132 /* TODO: remove the temporary useage and use memrchr and the information we have instead */
133 temp = xstrndup (item, initiallen + 1);
134
135 if (!((target = strrchr (temp, ';')) && !strchr (target, '"') && *(target + 1) != '\0'))
136 target = NULL;
137 else
138 ++target;
139
140 sct = sc->findTarget(target);
141
142 if (!sct) {
143 sct = new HttpHdrScTarget(target);
144 addTarget(sct);
145 }
146
147 safe_free (temp);
148
149 if (sct->isSet(static_cast<http_hdr_sc_type>(type))) {
150 if (type != SC_OTHER)
151 debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item << "' in '" << str << "'");
152
153 ++ ScFieldsInfo[type].stat.repCount;
154
155 continue;
156 }
157
158 /* process directives */
159 switch (type) {
160 case SC_NO_STORE:
161 sct->noStore(true);
162 break;
163
164 case SC_NO_STORE_REMOTE:
165 sct->noStoreRemote(true);
166 break;
167
168 case SC_MAX_AGE: {
169 int ma;
170 if (p && httpHeaderParseInt(p, &ma)) {
171 sct->maxAge(ma);
172
173 if ((p = strchr (p, '+'))) {
174 int ms;
175 ++p; //skip the + char
176 if (httpHeaderParseInt(p, &ms)) {
177 sct->maxStale(ms);
178 } else {
179 debugs(90, 2, "sc: invalid max-stale specs near '" << item << "'");
180 sct->clearMaxStale();
181 /* leave the max-age alone */
182 }
183 }
184 } else {
185 debugs(90, 2, "sc: invalid max-age specs near '" << item << "'");
186 sct->clearMaxAge();
187 }
188
189 break;
190 }
191
192 case SC_CONTENT:
193
194 if ( p && httpHeaderParseQuotedString(p, vlen, &sct->content_)) {
195 sct->setMask(SC_CONTENT,true); // ugly but saves a copy
196 } else {
197 debugs(90, 2, "sc: invalid content= quoted string near '" << item << "'");
198 sct->clearContent();
199 }
200 break;
201
202 case SC_OTHER:
203 default:
204 break;
205 }
206 }
207
208 return sc->targets.head != NULL;
209 }
210
211 HttpHdrSc::~HttpHdrSc()
212 {
213 if (targets.head) {
214 dlink_node *sct = targets.head;
215
216 while (sct) {
217 HttpHdrScTarget *t = static_cast<HttpHdrScTarget *>(sct->data);
218 sct = sct->next;
219 dlinkDelete (&t->node, &targets);
220 delete t;
221 }
222 }
223 }
224
225 HttpHdrSc::HttpHdrSc(const HttpHdrSc &sc)
226 {
227 dlink_node *node = sc.targets.head;
228
229 while (node) {
230 HttpHdrScTarget *dupsct = new HttpHdrScTarget(*static_cast<HttpHdrScTarget *>(node->data));
231 addTargetAtTail(dupsct);
232 node = node->next;
233 }
234 }
235
236 void
237 HttpHdrScTarget::packInto(Packer * p) const
238 {
239 http_hdr_sc_type flag;
240 int pcount = 0;
241 assert (p);
242
243 for (flag = SC_NO_STORE; flag < SC_ENUM_END; ++flag) {
244 if (isSet(flag) && flag != SC_OTHER) {
245
246 /* print option name */
247 packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH),
248 SQUIDSTRINGPRINT(ScFieldsInfo[flag].name));
249
250 /* handle options with values */
251
252 if (flag == SC_MAX_AGE)
253 packerPrintf(p, "=%d", (int) max_age);
254
255 if (flag == SC_CONTENT)
256 packerPrintf(p, "=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(content_));
257
258 ++pcount;
259 }
260 }
261
262 if (hasTarget())
263 packerPrintf (p, ";" SQUIDSTRINGPH, SQUIDSTRINGPRINT(target));
264 }
265
266 void
267 HttpHdrSc::packInto(Packer * p) const
268 {
269 dlink_node *node;
270 assert(p);
271 node = targets.head;
272
273 while (node) {
274 static_cast<HttpHdrScTarget *>(node->data)->packInto(p);
275 node = node->next;
276 }
277 }
278
279 /* negative max_age will clean old max_Age setting */
280 void
281 HttpHdrSc::setMaxAge(char const *target, int max_age)
282 {
283 HttpHdrScTarget *sct = findTarget(target);
284
285 if (!sct) {
286 sct = new HttpHdrScTarget(target);
287 dlinkAddTail (sct, &sct->node, &targets);
288 }
289
290 sct->maxAge(max_age);
291 }
292
293 void
294 HttpHdrSc::updateStats(StatHist * hist) const
295 {
296 dlink_node *sct = targets.head;
297
298 while (sct) {
299 static_cast<HttpHdrScTarget *>(sct->data)->updateStats(hist);
300 sct = sct->next;
301 }
302 }
303
304 void
305 httpHdrScTargetStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
306 {
307 extern const HttpHeaderStat *dump_stat; /* argh! */
308 const int id = (int) val;
309 const int valid_id = id >= 0 && id < SC_ENUM_END;
310 const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
311
312 if (count || valid_id)
313 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
314 id, name, count, xdiv(count, dump_stat->scParsedCount));
315 }
316
317 void
318 httpHdrScStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
319 {
320 extern const HttpHeaderStat *dump_stat; /* argh! */
321 const int id = (int) val;
322 const int valid_id = id >= 0 && id < SC_ENUM_END;
323 const char *name = valid_id ? ScFieldsInfo[id].name.termedBuf() : "INVALID";
324
325 if (count || valid_id)
326 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
327 id, name, count, xdiv(count, dump_stat->scParsedCount));
328 }
329
330 HttpHdrScTarget *
331 HttpHdrSc::findTarget(const char *target)
332 {
333 dlink_node *node;
334 node = targets.head;
335
336 while (node) {
337 HttpHdrScTarget *sct = (HttpHdrScTarget *)node->data;
338
339 if (target && sct->target.size() > 0 && !strcmp(target, sct->target.termedBuf()))
340 return sct;
341 else if (!target && sct->target.size() == 0)
342 return sct;
343
344 node = node->next;
345 }
346
347 return NULL;
348 }
349
350 HttpHdrScTarget *
351 HttpHdrSc::getMergedTarget(const char *ourtarget)
352 {
353 HttpHdrScTarget *sctus = findTarget(ourtarget);
354 HttpHdrScTarget *sctgeneric = findTarget(NULL);
355
356 if (sctgeneric || sctus) {
357 HttpHdrScTarget *sctusable = new HttpHdrScTarget(NULL);
358
359 if (sctgeneric)
360 sctusable->mergeWith(sctgeneric);
361
362 if (sctus)
363 sctusable->mergeWith(sctus);
364
365 return sctusable;
366 }
367
368 return NULL;
369 }