]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrCc.cc
SourceLayout: shuffle memory pool allocators to mem/libmem.la
[thirdparty/squid.git] / src / HttpHdrCc.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 65 HTTP Cache Control Header */
10
11 #include "squid.h"
12 #include "HttpHdrCc.h"
13 #include "HttpHeader.h"
14 #include "HttpHeaderFieldStat.h"
15 #include "HttpHeaderStat.h"
16 #include "HttpHeaderTools.h"
17 #include "SBuf.h"
18 #include "StatHist.h"
19 #include "Store.h"
20 #include "StrList.h"
21 #include "util.h"
22
23 #include <map>
24
25 /* a row in the table used for parsing cache control header and statistics */
26 typedef struct {
27 const char *name;
28 http_hdr_cc_type id;
29 HttpHeaderFieldStat stat;
30 } HttpHeaderCcFields;
31
32 /* order must match that of enum http_hdr_cc_type. The constraint is verified at initialization time */
33 static HttpHeaderCcFields CcAttrs[CC_ENUM_END] = {
34 {"public", CC_PUBLIC},
35 {"private", CC_PRIVATE},
36 {"no-cache", CC_NO_CACHE},
37 {"no-store", CC_NO_STORE},
38 {"no-transform", CC_NO_TRANSFORM},
39 {"must-revalidate", CC_MUST_REVALIDATE},
40 {"proxy-revalidate", CC_PROXY_REVALIDATE},
41 {"max-age", CC_MAX_AGE},
42 {"s-maxage", CC_S_MAXAGE},
43 {"max-stale", CC_MAX_STALE},
44 {"min-fresh", CC_MIN_FRESH},
45 {"only-if-cached", CC_ONLY_IF_CACHED},
46 {"stale-if-error", CC_STALE_IF_ERROR},
47 {"Other,", CC_OTHER} /* ',' will protect from matches */
48 };
49
50 /// Map an header name to its type, to expedite parsing
51 typedef std::map<const SBuf,http_hdr_cc_type> CcNameToIdMap_t;
52 static CcNameToIdMap_t CcNameToIdMap;
53
54 /// used to walk a table of http_header_cc_type structs
55 http_hdr_cc_type &operator++ (http_hdr_cc_type &aHeader)
56 {
57 int tmp = (int)aHeader;
58 aHeader = (http_hdr_cc_type)(++tmp);
59 return aHeader;
60 }
61
62 /// Module initialization hook
63 void
64 httpHdrCcInitModule(void)
65 {
66 /* build lookup and accounting structures */
67 for (int32_t i = 0; i < CC_ENUM_END; ++i) {
68 const HttpHeaderCcFields &f=CcAttrs[i];
69 assert(i == f.id); /* verify assumption: the id is the key into the array */
70 const SBuf k(f.name);
71 CcNameToIdMap[k]=f.id;
72 }
73 }
74
75 /// Module cleanup hook.
76 void
77 httpHdrCcCleanModule(void)
78 {
79 // HdrCcNameToIdMap is self-cleaning
80 }
81
82 void
83 HttpHdrCc::clear()
84 {
85 *this=HttpHdrCc();
86 }
87
88 bool
89 HttpHdrCc::parse(const String & str)
90 {
91 const char *item;
92 const char *p; /* '=' parameter */
93 const char *pos = NULL;
94 http_hdr_cc_type type;
95 int ilen;
96 int nlen;
97
98 /* iterate through comma separated list */
99
100 while (strListGetItem(&str, ',', &item, &ilen, &pos)) {
101 /* isolate directive name */
102
103 if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
104 nlen = p - item;
105 ++p;
106 } else {
107 nlen = ilen;
108 }
109
110 /* find type */
111 const CcNameToIdMap_t::const_iterator i=CcNameToIdMap.find(SBuf(item,nlen));
112 if (i==CcNameToIdMap.end())
113 type=CC_OTHER;
114 else
115 type=i->second;
116
117 // ignore known duplicate directives
118 if (isSet(type)) {
119 if (type != CC_OTHER) {
120 debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
121 ++CcAttrs[type].stat.repCount;
122 continue;
123 }
124 }
125
126 /* special-case-parsing and attribute-setting */
127 switch (type) {
128
129 case CC_MAX_AGE:
130 if (!p || !httpHeaderParseInt(p, &max_age) || max_age < 0) {
131 debugs(65, 2, "cc: invalid max-age specs near '" << item << "'");
132 clearMaxAge();
133 } else {
134 setMask(type,true);
135 }
136 break;
137
138 case CC_S_MAXAGE:
139 if (!p || !httpHeaderParseInt(p, &s_maxage) || s_maxage < 0) {
140 debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'");
141 clearSMaxAge();
142 } else {
143 setMask(type,true);
144 }
145 break;
146
147 case CC_MAX_STALE:
148 if (!p || !httpHeaderParseInt(p, &max_stale) || max_stale < 0) {
149 debugs(65, 2, "cc: max-stale directive is valid without value");
150 maxStale(MAX_STALE_ANY);
151 } else {
152 setMask(type,true);
153 }
154 break;
155
156 case CC_MIN_FRESH:
157 if (!p || !httpHeaderParseInt(p, &min_fresh) || min_fresh < 0) {
158 debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'");
159 clearMinFresh();
160 } else {
161 setMask(type,true);
162 }
163 break;
164
165 case CC_STALE_IF_ERROR:
166 if (!p || !httpHeaderParseInt(p, &stale_if_error) || stale_if_error < 0) {
167 debugs(65, 2, "cc: invalid stale-if-error specs near '" << item << "'");
168 clearStaleIfError();
169 } else {
170 setMask(type,true);
171 }
172 break;
173
174 case CC_PRIVATE: {
175 String temp;
176 if (!p) {
177 // Value parameter is optional.
178 private_.clean();
179 } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
180 private_.append(temp);
181 } else {
182 debugs(65, 2, "cc: invalid private= specs near '" << item << "'");
183 }
184 // to be safe we ignore broken parameters, but always remember the 'private' part.
185 setMask(type,true);
186 }
187 break;
188
189 case CC_NO_CACHE: {
190 String temp;
191 if (!p) {
192 // On Requests, missing value parameter is expected syntax.
193 // On Responses, value parameter is optional.
194 setMask(type,true);
195 no_cache.clean();
196 } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
197 // On Requests, a value parameter is invalid syntax.
198 // XXX: identify when parsing request header and dump err message here.
199 setMask(type,true);
200 no_cache.append(temp);
201 } else {
202 debugs(65, 2, "cc: invalid no-cache= specs near '" << item << "'");
203 }
204 }
205 break;
206
207 case CC_PUBLIC:
208 Public(true);
209 break;
210 case CC_NO_STORE:
211 noStore(true);
212 break;
213 case CC_NO_TRANSFORM:
214 noTransform(true);
215 break;
216 case CC_MUST_REVALIDATE:
217 mustRevalidate(true);
218 break;
219 case CC_PROXY_REVALIDATE:
220 proxyRevalidate(true);
221 break;
222 case CC_ONLY_IF_CACHED:
223 onlyIfCached(true);
224 break;
225
226 case CC_OTHER:
227 if (other.size())
228 other.append(", ");
229
230 other.append(item, ilen);
231 break;
232
233 default:
234 /* note that we ignore most of '=' specs (RFCVIOLATION) */
235 break;
236 }
237 }
238
239 return (mask != 0);
240 }
241
242 void
243 HttpHdrCc::packInto(Packer * p) const
244 {
245 // optimization: if the mask is empty do nothing
246 if (mask==0)
247 return;
248
249 http_hdr_cc_type flag;
250 int pcount = 0;
251 assert(p);
252
253 for (flag = CC_PUBLIC; flag < CC_ENUM_END; ++flag) {
254 if (isSet(flag) && flag != CC_OTHER) {
255
256 /* print option name for all options */
257 packerPrintf(p, (pcount ? ", %s": "%s") , CcAttrs[flag].name);
258
259 /* for all options having values, "=value" after the name */
260 switch (flag) {
261 case CC_MAX_AGE:
262 packerPrintf(p, "=%d", (int) maxAge());
263 break;
264 case CC_S_MAXAGE:
265 packerPrintf(p, "=%d", (int) sMaxAge());
266 break;
267 case CC_MAX_STALE:
268 /* max-stale's value is optional.
269 If we didn't receive it, don't send it */
270 if (maxStale()!=MAX_STALE_ANY)
271 packerPrintf(p, "=%d", (int) maxStale());
272 break;
273 case CC_MIN_FRESH:
274 packerPrintf(p, "=%d", (int) minFresh());
275 break;
276 default:
277 /* do nothing, directive was already printed */
278 break;
279 }
280
281 ++pcount;
282 }
283 }
284
285 if (other.size() != 0)
286 packerPrintf(p, (pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH),
287 SQUIDSTRINGPRINT(other));
288 }
289
290 void
291 httpHdrCcUpdateStats(const HttpHdrCc * cc, StatHist * hist)
292 {
293 http_hdr_cc_type c;
294 assert(cc);
295
296 for (c = CC_PUBLIC; c < CC_ENUM_END; ++c)
297 if (cc->isSet(c))
298 hist->count(c);
299 }
300
301 void
302 httpHdrCcStatDumper(StoreEntry * sentry, int idx, double val, double size, int count)
303 {
304 extern const HttpHeaderStat *dump_stat; /* argh! */
305 const int id = (int) val;
306 const int valid_id = id >= 0 && id < CC_ENUM_END;
307 const char *name = valid_id ? CcAttrs[id].name : "INVALID";
308
309 if (count || valid_id)
310 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
311 id, name, count, xdiv(count, dump_stat->ccParsedCount));
312 }
313
314 #if !_USE_INLINE_
315 #include "HttpHdrCc.cci"
316 #endif