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