]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrCc.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / HttpHdrCc.cc
1 /*
2 * Copyright (C) 1996-2018 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 {"immutable", HttpHdrCcType::CC_IMMUTABLE},
44 {"Other,", HttpHdrCcType::CC_OTHER}, /* ',' will protect from matches */
45 {nullptr, HttpHdrCcType::CC_ENUM_END}
46 };
47 LookupTable<HttpHdrCcType> ccLookupTable(HttpHdrCcType::CC_OTHER,CcAttrs);
48 std::vector<HttpHeaderFieldStat> ccHeaderStats(HttpHdrCcType::CC_ENUM_END);
49
50 /// used to walk a table of http_header_cc_type structs
51 HttpHdrCcType &operator++ (HttpHdrCcType &aHeader)
52 {
53 int tmp = (int)aHeader;
54 aHeader = (HttpHdrCcType)(++tmp);
55 return aHeader;
56 }
57
58 /// Module initialization hook
59 void
60 httpHdrCcInitModule(void)
61 {
62 // check invariant on initialization table
63 for (unsigned int j = 0; CcAttrs[j].name != nullptr; ++j) {
64 assert (static_cast<int>(CcAttrs[j].id) == j);
65 }
66 }
67
68 void
69 HttpHdrCc::clear()
70 {
71 *this=HttpHdrCc();
72 }
73
74 /// set a data member to a new value, and set the corresponding mask-bit.
75 /// if setting is false, then the mask-bit is cleared.
76 void
77 HttpHdrCc::setValue(int32_t &value, int32_t new_value, HttpHdrCcType hdr, bool setting)
78 {
79 if (setting) {
80 if (new_value < 0) {
81 debugs(65, 3, "rejecting negative-value Cache-Control directive " << hdr
82 << " value " << new_value);
83 return;
84 }
85 } else {
86 new_value = -1; //rely on the convention that "unknown" is -1
87 }
88
89 value = new_value;
90 setMask(hdr,setting);
91 }
92
93 bool
94 HttpHdrCc::parse(const String & str)
95 {
96 const char *item;
97 const char *p; /* '=' parameter */
98 const char *pos = NULL;
99 int ilen;
100 int nlen;
101
102 /* iterate through comma separated list */
103
104 while (strListGetItem(&str, ',', &item, &ilen, &pos)) {
105 /* isolate directive name */
106
107 if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
108 nlen = p - item;
109 ++p;
110 } else {
111 nlen = ilen;
112 }
113
114 /* find type */
115 const HttpHdrCcType type = ccLookupTable.lookup(SBuf(item,nlen));
116
117 // ignore known duplicate directives
118 if (isSet(type)) {
119 if (type != HttpHdrCcType::CC_OTHER) {
120 debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
121 ++ ccHeaderStats[type].repCount;
122 continue;
123 }
124 }
125
126 /* special-case-parsing and attribute-setting */
127 switch (type) {
128
129 case HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::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 HttpHdrCcType::CC_PUBLIC:
208 Public(true);
209 break;
210 case HttpHdrCcType::CC_NO_STORE:
211 noStore(true);
212 break;
213 case HttpHdrCcType::CC_NO_TRANSFORM:
214 noTransform(true);
215 break;
216 case HttpHdrCcType::CC_MUST_REVALIDATE:
217 mustRevalidate(true);
218 break;
219 case HttpHdrCcType::CC_PROXY_REVALIDATE:
220 proxyRevalidate(true);
221 break;
222 case HttpHdrCcType::CC_ONLY_IF_CACHED:
223 onlyIfCached(true);
224 break;
225 case HttpHdrCcType::CC_IMMUTABLE:
226 Immutable(true);
227 break;
228
229 case HttpHdrCcType::CC_OTHER:
230 if (other.size())
231 other.append(", ");
232
233 other.append(item, ilen);
234 break;
235
236 default:
237 /* note that we ignore most of '=' specs (RFCVIOLATION) */
238 break;
239 }
240 }
241
242 return (mask != 0);
243 }
244
245 void
246 HttpHdrCc::packInto(Packable * p) const
247 {
248 // optimization: if the mask is empty do nothing
249 if (mask==0)
250 return;
251
252 HttpHdrCcType flag;
253 int pcount = 0;
254 assert(p);
255
256 for (flag = HttpHdrCcType::CC_PUBLIC; flag < HttpHdrCcType::CC_ENUM_END; ++flag) {
257 if (isSet(flag) && flag != HttpHdrCcType::CC_OTHER) {
258
259 /* print option name for all options */
260 p->appendf((pcount ? ", %s": "%s"), CcAttrs[flag].name);
261
262 /* for all options having values, "=value" after the name */
263 switch (flag) {
264 case HttpHdrCcType::CC_PUBLIC:
265 break;
266 case HttpHdrCcType::CC_PRIVATE:
267 if (private_.size())
268 p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(private_));
269 break;
270
271 case HttpHdrCcType::CC_NO_CACHE:
272 if (no_cache.size())
273 p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(no_cache));
274 break;
275 case HttpHdrCcType::CC_NO_STORE:
276 break;
277 case HttpHdrCcType::CC_NO_TRANSFORM:
278 break;
279 case HttpHdrCcType::CC_MUST_REVALIDATE:
280 break;
281 case HttpHdrCcType::CC_PROXY_REVALIDATE:
282 break;
283 case HttpHdrCcType::CC_MAX_AGE:
284 p->appendf("=%d", max_age);
285 break;
286 case HttpHdrCcType::CC_S_MAXAGE:
287 p->appendf("=%d", s_maxage);
288 break;
289 case HttpHdrCcType::CC_MAX_STALE:
290 /* max-stale's value is optional.
291 If we didn't receive it, don't send it */
292 if (max_stale != MAX_STALE_ANY)
293 p->appendf("=%d", max_stale);
294 break;
295 case HttpHdrCcType::CC_MIN_FRESH:
296 p->appendf("=%d", min_fresh);
297 break;
298 case HttpHdrCcType::CC_ONLY_IF_CACHED:
299 break;
300 case HttpHdrCcType::CC_STALE_IF_ERROR:
301 p->appendf("=%d", stale_if_error);
302 break;
303 case HttpHdrCcType::CC_IMMUTABLE:
304 break;
305 case HttpHdrCcType::CC_OTHER:
306 case HttpHdrCcType::CC_ENUM_END:
307 // done below after the loop
308 break;
309 }
310
311 ++pcount;
312 }
313 }
314
315 if (other.size() != 0)
316 p->appendf((pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(other));
317 }
318
319 void
320 httpHdrCcUpdateStats(const HttpHdrCc * cc, StatHist * hist)
321 {
322 assert(cc);
323
324 for (HttpHdrCcType c = HttpHdrCcType::CC_PUBLIC; c < HttpHdrCcType::CC_ENUM_END; ++c)
325 if (cc->isSet(c))
326 hist->count(c);
327 }
328
329 void
330 httpHdrCcStatDumper(StoreEntry * sentry, int, double val, double, int count)
331 {
332 extern const HttpHeaderStat *dump_stat; /* argh! */
333 const int id = static_cast<int>(val);
334 const bool valid_id = id >= 0 && id < static_cast<int>(HttpHdrCcType::CC_ENUM_END);
335 const char *name = valid_id ? CcAttrs[id].name : "INVALID";
336
337 if (count || valid_id)
338 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
339 id, name, count, xdiv(count, dump_stat->ccParsedCount));
340 }
341
342 std::ostream &
343 operator<< (std::ostream &s, HttpHdrCcType c)
344 {
345 const unsigned char ic = static_cast<int>(c);
346 if (c < HttpHdrCcType::CC_ENUM_END)
347 s << CcAttrs[ic].name << '[' << ic << ']' ;
348 else
349 s << "*invalid hdrcc* [" << ic << ']';
350 return s;
351 }
352