]> git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrCc.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / HttpHdrCc.cc
1 /*
2 * Copyright (C) 1996-2017 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 bool
75 HttpHdrCc::parse(const String & str)
76 {
77 const char *item;
78 const char *p; /* '=' parameter */
79 const char *pos = NULL;
80 int ilen;
81 int nlen;
82
83 /* iterate through comma separated list */
84
85 while (strListGetItem(&str, ',', &item, &ilen, &pos)) {
86 /* isolate directive name */
87
88 if ((p = (const char *)memchr(item, '=', ilen)) && (p - item < ilen)) {
89 nlen = p - item;
90 ++p;
91 } else {
92 nlen = ilen;
93 }
94
95 /* find type */
96 const HttpHdrCcType type = ccLookupTable.lookup(SBuf(item,nlen));
97
98 // ignore known duplicate directives
99 if (isSet(type)) {
100 if (type != HttpHdrCcType::CC_OTHER) {
101 debugs(65, 2, "hdr cc: ignoring duplicate cache-directive: near '" << item << "' in '" << str << "'");
102 ++ ccHeaderStats[type].repCount;
103 continue;
104 }
105 }
106
107 /* special-case-parsing and attribute-setting */
108 switch (type) {
109
110 case HttpHdrCcType::CC_MAX_AGE:
111 if (!p || !httpHeaderParseInt(p, &max_age) || max_age < 0) {
112 debugs(65, 2, "cc: invalid max-age specs near '" << item << "'");
113 clearMaxAge();
114 } else {
115 setMask(type,true);
116 }
117 break;
118
119 case HttpHdrCcType::CC_S_MAXAGE:
120 if (!p || !httpHeaderParseInt(p, &s_maxage) || s_maxage < 0) {
121 debugs(65, 2, "cc: invalid s-maxage specs near '" << item << "'");
122 clearSMaxAge();
123 } else {
124 setMask(type,true);
125 }
126 break;
127
128 case HttpHdrCcType::CC_MAX_STALE:
129 if (!p || !httpHeaderParseInt(p, &max_stale) || max_stale < 0) {
130 debugs(65, 2, "cc: max-stale directive is valid without value");
131 maxStale(MAX_STALE_ANY);
132 } else {
133 setMask(type,true);
134 }
135 break;
136
137 case HttpHdrCcType::CC_MIN_FRESH:
138 if (!p || !httpHeaderParseInt(p, &min_fresh) || min_fresh < 0) {
139 debugs(65, 2, "cc: invalid min-fresh specs near '" << item << "'");
140 clearMinFresh();
141 } else {
142 setMask(type,true);
143 }
144 break;
145
146 case HttpHdrCcType::CC_STALE_IF_ERROR:
147 if (!p || !httpHeaderParseInt(p, &stale_if_error) || stale_if_error < 0) {
148 debugs(65, 2, "cc: invalid stale-if-error specs near '" << item << "'");
149 clearStaleIfError();
150 } else {
151 setMask(type,true);
152 }
153 break;
154
155 case HttpHdrCcType::CC_PRIVATE: {
156 String temp;
157 if (!p) {
158 // Value parameter is optional.
159 private_.clean();
160 } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
161 private_.append(temp);
162 } else {
163 debugs(65, 2, "cc: invalid private= specs near '" << item << "'");
164 }
165 // to be safe we ignore broken parameters, but always remember the 'private' part.
166 setMask(type,true);
167 }
168 break;
169
170 case HttpHdrCcType::CC_NO_CACHE: {
171 String temp;
172 if (!p) {
173 // On Requests, missing value parameter is expected syntax.
174 // On Responses, value parameter is optional.
175 setMask(type,true);
176 no_cache.clean();
177 } else if (/* p &&*/ httpHeaderParseQuotedString(p, (ilen-nlen-1), &temp)) {
178 // On Requests, a value parameter is invalid syntax.
179 // XXX: identify when parsing request header and dump err message here.
180 setMask(type,true);
181 no_cache.append(temp);
182 } else {
183 debugs(65, 2, "cc: invalid no-cache= specs near '" << item << "'");
184 }
185 }
186 break;
187
188 case HttpHdrCcType::CC_PUBLIC:
189 Public(true);
190 break;
191 case HttpHdrCcType::CC_NO_STORE:
192 noStore(true);
193 break;
194 case HttpHdrCcType::CC_NO_TRANSFORM:
195 noTransform(true);
196 break;
197 case HttpHdrCcType::CC_MUST_REVALIDATE:
198 mustRevalidate(true);
199 break;
200 case HttpHdrCcType::CC_PROXY_REVALIDATE:
201 proxyRevalidate(true);
202 break;
203 case HttpHdrCcType::CC_ONLY_IF_CACHED:
204 onlyIfCached(true);
205 break;
206 case HttpHdrCcType::CC_IMMUTABLE:
207 Immutable(true);
208 break;
209
210 case HttpHdrCcType::CC_OTHER:
211 if (other.size())
212 other.append(", ");
213
214 other.append(item, ilen);
215 break;
216
217 default:
218 /* note that we ignore most of '=' specs (RFCVIOLATION) */
219 break;
220 }
221 }
222
223 return (mask != 0);
224 }
225
226 void
227 HttpHdrCc::packInto(Packable * p) const
228 {
229 // optimization: if the mask is empty do nothing
230 if (mask==0)
231 return;
232
233 HttpHdrCcType flag;
234 int pcount = 0;
235 assert(p);
236
237 for (flag = HttpHdrCcType::CC_PUBLIC; flag < HttpHdrCcType::CC_ENUM_END; ++flag) {
238 if (isSet(flag) && flag != HttpHdrCcType::CC_OTHER) {
239
240 /* print option name for all options */
241 p->appendf((pcount ? ", %s": "%s") , CcAttrs[flag].name);
242
243 /* for all options having values, "=value" after the name */
244 switch (flag) {
245 case HttpHdrCcType::CC_PUBLIC:
246 break;
247 case HttpHdrCcType::CC_PRIVATE:
248 if (Private().size())
249 p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(Private()));
250 break;
251
252 case HttpHdrCcType::CC_NO_CACHE:
253 if (noCache().size())
254 p->appendf("=\"" SQUIDSTRINGPH "\"", SQUIDSTRINGPRINT(noCache()));
255 break;
256 case HttpHdrCcType::CC_NO_STORE:
257 break;
258 case HttpHdrCcType::CC_NO_TRANSFORM:
259 break;
260 case HttpHdrCcType::CC_MUST_REVALIDATE:
261 break;
262 case HttpHdrCcType::CC_PROXY_REVALIDATE:
263 break;
264 case HttpHdrCcType::CC_MAX_AGE:
265 p->appendf("=%d", maxAge());
266 break;
267 case HttpHdrCcType::CC_S_MAXAGE:
268 p->appendf("=%d", sMaxAge());
269 break;
270 case HttpHdrCcType::CC_MAX_STALE:
271 /* max-stale's value is optional.
272 If we didn't receive it, don't send it */
273 if (maxStale()!=MAX_STALE_ANY)
274 p->appendf("=%d", maxStale());
275 break;
276 case HttpHdrCcType::CC_MIN_FRESH:
277 p->appendf("=%d", minFresh());
278 break;
279 case HttpHdrCcType::CC_ONLY_IF_CACHED:
280 break;
281 case HttpHdrCcType::CC_STALE_IF_ERROR:
282 p->appendf("=%d", staleIfError());
283 break;
284 case HttpHdrCcType::CC_IMMUTABLE:
285 break;
286 case HttpHdrCcType::CC_OTHER:
287 case HttpHdrCcType::CC_ENUM_END:
288 // done below after the loop
289 break;
290 }
291
292 ++pcount;
293 }
294 }
295
296 if (other.size() != 0)
297 p->appendf((pcount ? ", " SQUIDSTRINGPH : SQUIDSTRINGPH), SQUIDSTRINGPRINT(other));
298 }
299
300 void
301 httpHdrCcUpdateStats(const HttpHdrCc * cc, StatHist * hist)
302 {
303 assert(cc);
304
305 for (HttpHdrCcType c = HttpHdrCcType::CC_PUBLIC; c < HttpHdrCcType::CC_ENUM_END; ++c)
306 if (cc->isSet(c))
307 hist->count(c);
308 }
309
310 void
311 httpHdrCcStatDumper(StoreEntry * sentry, int, double val, double, int count)
312 {
313 extern const HttpHeaderStat *dump_stat; /* argh! */
314 const int id = static_cast<int>(val);
315 const bool valid_id = id >= 0 && id < static_cast<int>(HttpHdrCcType::CC_ENUM_END);
316 const char *name = valid_id ? CcAttrs[id].name : "INVALID";
317
318 if (count || valid_id)
319 storeAppendPrintf(sentry, "%2d\t %-20s\t %5d\t %6.2f\n",
320 id, name, count, xdiv(count, dump_stat->ccParsedCount));
321 }
322
323 std::ostream &
324 operator<< (std::ostream &s, HttpHdrCcType c)
325 {
326 const unsigned char ic = static_cast<int>(c);
327 if (c < HttpHdrCcType::CC_ENUM_END)
328 s << CcAttrs[ic].name << '[' << ic << ']' ;
329 else
330 s << "*invalid hdrcc* [" << ic << ']';
331 return s;
332 }
333
334 #if !_USE_INLINE_
335 #include "HttpHdrCc.cci"
336 #endif
337