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