]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/HttpHdrSc.cc
2 * Copyright (C) 1996-2021 The Squid Software Foundation and contributors
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.
9 /* DEBUG: section 90 HTTP Cache Control Header */
12 #include "base/LookupTable.h"
13 //#include "HttpHdrSc.h" // pulled in by HttpHdrScTarget.h
14 #include "HttpHdrScTarget.h"
15 #include "HttpHeader.h"
16 #include "HttpHeaderFieldStat.h"
17 #include "HttpHeaderStat.h"
18 #include "HttpHeaderTools.h"
26 /* this table is used for parsing surrogate control header */
27 /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
28 // TODO: implement constraint
29 static const LookupTable
<http_hdr_sc_type
>::Record ScAttrs
[] {
30 {"no-store", SC_NO_STORE
},
31 {"no-store-remote", SC_NO_STORE_REMOTE
},
32 {"max-age", SC_MAX_AGE
},
33 {"content", SC_CONTENT
},
34 {"Other,", SC_OTHER
}, /* ',' will protect from matches */
35 {nullptr, SC_ENUM_END
} /* SC_ENUM_END taken as invalid value */
37 LookupTable
<http_hdr_sc_type
> scLookupTable(SC_ENUM_END
, ScAttrs
);
38 std::vector
<HttpHeaderFieldStat
> scHeaderStats(SC_ENUM_END
);
40 // used when iterating over flags
41 http_hdr_sc_type
&operator++ (http_hdr_sc_type
&aHeader
)
43 int tmp
= static_cast<int>(aHeader
);
44 aHeader
= static_cast<http_hdr_sc_type
>(++tmp
);
49 httpHdrScInitModule(void)
51 // check invariant on ScAttrs
52 for (int i
= 0; ScAttrs
[i
].name
!= nullptr; ++i
)
53 assert(i
== ScAttrs
[i
].id
);
58 /* creates an sc object from a 0-terminating string */
60 httpHdrScParseCreate(const String
& str
)
62 HttpHdrSc
*sc
= new HttpHdrSc();
64 if (!sc
->parse(&str
)) {
72 /* parses a 0-terminating string and inits sc */
74 HttpHdrSc::parse(const String
* str
)
78 const char *p
; /* '=' parameter */
79 const char *pos
= NULL
;
80 const char *target
= NULL
; /* ;foo */
81 const char *temp
= NULL
; /* temp buffer */
82 http_hdr_sc_type type
;
88 /* iterate through comma separated list */
90 while (strListGetItem(str
, ',', &item
, &ilen
, &pos
)) {
93 /* decrease ilen to still match the token for '=' statements */
95 if ((p
= strchr(item
, '=')) && (p
- item
< ilen
)) {
96 vlen
= ilen
- (p
+ 1 - item
);
101 /* decrease ilen to still match the token for ';' qualified non '=' statements */
102 else if ((p
= strchr(item
, ';')) && (p
- item
< ilen
)) {
108 type
= scLookupTable
.lookup(SBuf(item
,ilen
));
110 if (type
== SC_ENUM_END
) {
111 debugs(90, 2, "unknown control-directive near '" << item
<< "' in '" << *str
<< "'");
115 /* Is this a targeted directive? */
116 /* TODO: remove the temporary usage and use memrchr and the information we have instead */
117 temp
= xstrndup (item
, initiallen
+ 1);
119 if (!((target
= strrchr (temp
, ';')) && !strchr (target
, '"') && *(target
+ 1) != '\0'))
124 sct
= sc
->findTarget(target
);
127 // XXX: if parse is left-to-right over field-value this should be emplace_back()
128 // currently placing on the front reverses the order of headers passed on downstream.
129 targets
.emplace_front(target
);
130 sct
= &targets
.front();
135 if (sct
->isSet(type
)) {
136 if (type
!= SC_OTHER
)
137 debugs(90, 2, "ignoring duplicate control-directive near '" << item
<< "' in '" << *str
<< "'");
139 ++ scHeaderStats
[type
].repCount
;
144 /* process directives */
150 case SC_NO_STORE_REMOTE
:
151 sct
->noStoreRemote(true);
156 if (p
&& httpHeaderParseInt(p
, &ma
)) {
159 if ((p
= strchr (p
, '+'))) {
161 ++p
; //skip the + char
162 if (httpHeaderParseInt(p
, &ms
)) {
165 debugs(90, 2, "sc: invalid max-stale specs near '" << item
<< "'");
166 sct
->clearMaxStale();
167 /* leave the max-age alone */
171 debugs(90, 2, "sc: invalid max-age specs near '" << item
<< "'");
180 if ( p
&& httpHeaderParseQuotedString(p
, vlen
, &sct
->content_
)) {
181 sct
->setMask(SC_CONTENT
,true); // ugly but saves a copy
183 debugs(90, 2, "sc: invalid content= quoted string near '" << item
<< "'");
194 return !sc
->targets
.empty();
197 /// XXX: this function should be in HttpHdrScTarget.cc
199 HttpHdrScTarget::packInto(Packable
* p
) const
201 http_hdr_sc_type flag
;
205 for (flag
= SC_NO_STORE
; flag
< SC_ENUM_END
; ++flag
) {
206 if (isSet(flag
) && flag
!= SC_OTHER
) {
208 /* print option name */
209 p
->appendf((pcount
? ", %s" : "%s"), ScAttrs
[flag
].name
);
211 /* handle options with values */
213 if (flag
== SC_MAX_AGE
)
214 p
->appendf("=%d", (int) max_age
);
216 if (flag
== SC_CONTENT
)
217 p
->appendf("=\"" SQUIDSTRINGPH
"\"", SQUIDSTRINGPRINT(content_
));
224 p
->appendf(";" SQUIDSTRINGPH
, SQUIDSTRINGPRINT(target
));
228 HttpHdrSc::packInto(Packable
* p
) const
231 for (const auto &t
: targets
) {
236 /* negative max_age will clean old max_Age setting */
238 HttpHdrSc::setMaxAge(char const *target
, int max_age
)
240 HttpHdrScTarget
*sct
= findTarget(target
);
243 targets
.emplace_back(target
);
244 sct
= &targets
.back();
247 sct
->maxAge(max_age
);
251 HttpHdrSc::updateStats(StatHist
* hist
) const
253 for (auto &t
: targets
) {
259 httpHdrScTargetStatDumper(StoreEntry
* sentry
, int, double val
, double, int count
)
261 extern const HttpHeaderStat
*dump_stat
; /* argh! */
262 const int id
= (int) val
;
263 const bool valid_id
= id
>= 0 && id
< SC_ENUM_END
;
264 const char *name
= valid_id
? ScAttrs
[id
].name
: "INVALID";
266 if (count
|| valid_id
)
267 storeAppendPrintf(sentry
, "%2d\t %-20s\t %5d\t %6.2f\n",
268 id
, name
, count
, xdiv(count
, dump_stat
->scParsedCount
));
272 httpHdrScStatDumper(StoreEntry
* sentry
, int, double val
, double, int count
)
274 extern const HttpHeaderStat
*dump_stat
; /* argh! */
275 const int id
= (int) val
;
276 const bool valid_id
= id
>= 0 && id
< SC_ENUM_END
;
277 const char *name
= valid_id
? ScAttrs
[id
].name
: "INVALID";
279 if (count
|| valid_id
)
280 storeAppendPrintf(sentry
, "%2d\t %-20s\t %5d\t %6.2f\n",
281 id
, name
, count
, xdiv(count
, dump_stat
->scParsedCount
));
285 HttpHdrSc::findTarget(const char *target
)
287 for (auto &sct
: targets
) {
288 if (sct
.target
.cmp(target
) == 0)
296 HttpHdrSc::getMergedTarget(const char *ourtarget
)
298 HttpHdrScTarget
*sctus
= findTarget(ourtarget
);
299 HttpHdrScTarget
*sctgeneric
= findTarget(NULL
);
301 /* W3C Edge Architecture Specification 1.0 section 3
303 * "If more than one is targeted at a surrogate, the most specific applies.
305 * Surrogate-Control: max-age=60, no-store;abc
306 * The surrogate that identified itself as 'abc' would apply no-store;
307 * others would apply max-age=60.
309 * XXX: the if statements below will *merge* the no-store and max-age settings.
311 if (sctgeneric
|| sctus
) {
312 HttpHdrScTarget
*sctusable
= new HttpHdrScTarget(NULL
);
315 sctusable
->mergeWith(sctgeneric
);
318 sctusable
->mergeWith(sctus
);