2 * Copyright (C) 1996-2015 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 "HttpHdrSc.h"
13 #include "HttpHeader.h"
14 #include "HttpHeaderFieldInfo.h"
15 #include "HttpHeaderFieldStat.h"
16 #include "HttpHeaderStat.h"
17 #include "HttpHeaderTools.h"
24 /* a row in the table used for parsing surrogate-control header and statistics */
28 HttpHeaderFieldStat stat
;
31 /* this table is used for parsing surrogate control header */
32 /* order must match that of enum http_hdr_sc_type. The constraint is verified at initialization time */
33 //todo: implement constraint
34 static const HttpHeaderFieldAttrs ScAttrs
[SC_ENUM_END
] = {
35 HttpHeaderFieldAttrs("no-store", (http_hdr_type
)SC_NO_STORE
),
36 HttpHeaderFieldAttrs("no-store-remote", (http_hdr_type
)SC_NO_STORE_REMOTE
),
37 HttpHeaderFieldAttrs("max-age", (http_hdr_type
)SC_MAX_AGE
),
38 HttpHeaderFieldAttrs("content", (http_hdr_type
)SC_CONTENT
),
39 HttpHeaderFieldAttrs("Other,", (http_hdr_type
)SC_OTHER
) /* ',' will protect from matches */
42 HttpHeaderFieldInfo
*ScFieldsInfo
= NULL
;
44 http_hdr_sc_type
&operator++ (http_hdr_sc_type
&aHeader
)
46 int tmp
= (int)aHeader
;
47 aHeader
= (http_hdr_sc_type
)(++tmp
);
51 int operator - (http_hdr_sc_type
const &anSc
, http_hdr_sc_type
const &anSc2
)
53 return (int)anSc
- (int)anSc2
;
56 /* module initialization */
59 httpHdrScInitModule(void)
61 ScFieldsInfo
= httpHeaderBuildFieldsInfo(ScAttrs
, SC_ENUM_END
);
65 httpHdrScCleanModule(void)
67 httpHeaderDestroyFieldsInfo(ScFieldsInfo
, SC_ENUM_END
);
73 /* creates an sc object from a 0-terminating string */
75 httpHdrScParseCreate(const String
& str
)
77 HttpHdrSc
*sc
= new HttpHdrSc();
79 if (!sc
->parse(&str
)) {
87 /* parses a 0-terminating string and inits sc */
89 HttpHdrSc::parse(const String
* str
)
93 const char *p
; /* '=' parameter */
94 const char *pos
= NULL
;
95 const char *target
= NULL
; /* ;foo */
96 const char *temp
= NULL
; /* temp buffer */
100 HttpHdrScTarget
*sct
;
103 /* iterate through comma separated list */
105 while (strListGetItem(str
, ',', &item
, &ilen
, &pos
)) {
108 /* decrease ilen to still match the token for '=' statements */
110 if ((p
= strchr(item
, '=')) && (p
- item
< ilen
)) {
111 vlen
= ilen
- (p
+ 1 - item
);
116 /* decrease ilen to still match the token for ';' qualified non '=' statments */
117 else if ((p
= strchr(item
, ';')) && (p
- item
< ilen
)) {
123 /* TODO: use a type-safe map-based lookup */
124 type
= httpHeaderIdByName(item
, ilen
,
125 ScFieldsInfo
, SC_ENUM_END
);
128 debugs(90, 2, "hdr sc: unknown control-directive: near '" << item
<< "' in '" << str
<< "'");
132 /* Is this a targeted directive? */
133 /* TODO: remove the temporary useage and use memrchr and the information we have instead */
134 temp
= xstrndup (item
, initiallen
+ 1);
136 if (!((target
= strrchr (temp
, ';')) && !strchr (target
, '"') && *(target
+ 1) != '\0'))
141 sct
= sc
->findTarget(target
);
144 sct
= new HttpHdrScTarget(target
);
150 if (sct
->isSet(static_cast<http_hdr_sc_type
>(type
))) {
151 if (type
!= SC_OTHER
)
152 debugs(90, 2, "hdr sc: ignoring duplicate control-directive: near '" << item
<< "' in '" << str
<< "'");
154 ++ ScFieldsInfo
[type
].stat
.repCount
;
159 /* process directives */
165 case SC_NO_STORE_REMOTE
:
166 sct
->noStoreRemote(true);
171 if (p
&& httpHeaderParseInt(p
, &ma
)) {
174 if ((p
= strchr (p
, '+'))) {
176 ++p
; //skip the + char
177 if (httpHeaderParseInt(p
, &ms
)) {
180 debugs(90, 2, "sc: invalid max-stale specs near '" << item
<< "'");
181 sct
->clearMaxStale();
182 /* leave the max-age alone */
186 debugs(90, 2, "sc: invalid max-age specs near '" << item
<< "'");
195 if ( p
&& httpHeaderParseQuotedString(p
, vlen
, &sct
->content_
)) {
196 sct
->setMask(SC_CONTENT
,true); // ugly but saves a copy
198 debugs(90, 2, "sc: invalid content= quoted string near '" << item
<< "'");
209 return sc
->targets
.head
!= NULL
;
212 HttpHdrSc::~HttpHdrSc()
215 dlink_node
*sct
= targets
.head
;
218 HttpHdrScTarget
*t
= static_cast<HttpHdrScTarget
*>(sct
->data
);
220 dlinkDelete (&t
->node
, &targets
);
226 HttpHdrSc::HttpHdrSc(const HttpHdrSc
&sc
)
228 dlink_node
*node
= sc
.targets
.head
;
231 HttpHdrScTarget
*dupsct
= new HttpHdrScTarget(*static_cast<HttpHdrScTarget
*>(node
->data
));
232 addTargetAtTail(dupsct
);
238 HttpHdrScTarget::packInto(Packable
* p
) const
240 http_hdr_sc_type flag
;
244 for (flag
= SC_NO_STORE
; flag
< SC_ENUM_END
; ++flag
) {
245 if (isSet(flag
) && flag
!= SC_OTHER
) {
247 /* print option name */
248 p
->appendf((pcount
? ", " SQUIDSTRINGPH
: SQUIDSTRINGPH
),
249 SQUIDSTRINGPRINT(ScFieldsInfo
[flag
].name
));
251 /* handle options with values */
253 if (flag
== SC_MAX_AGE
)
254 p
->appendf("=%d", (int) max_age
);
256 if (flag
== SC_CONTENT
)
257 p
->appendf("=\"" SQUIDSTRINGPH
"\"", SQUIDSTRINGPRINT(content_
));
264 p
->appendf(";" SQUIDSTRINGPH
, SQUIDSTRINGPRINT(target
));
268 HttpHdrSc::packInto(Packable
* p
) const
275 static_cast<HttpHdrScTarget
*>(node
->data
)->packInto(p
);
280 /* negative max_age will clean old max_Age setting */
282 HttpHdrSc::setMaxAge(char const *target
, int max_age
)
284 HttpHdrScTarget
*sct
= findTarget(target
);
287 sct
= new HttpHdrScTarget(target
);
288 dlinkAddTail (sct
, &sct
->node
, &targets
);
291 sct
->maxAge(max_age
);
295 HttpHdrSc::updateStats(StatHist
* hist
) const
297 dlink_node
*sct
= targets
.head
;
300 static_cast<HttpHdrScTarget
*>(sct
->data
)->updateStats(hist
);
306 httpHdrScTargetStatDumper(StoreEntry
* sentry
, int, double val
, double, int count
)
308 extern const HttpHeaderStat
*dump_stat
; /* argh! */
309 const int id
= (int) val
;
310 const int valid_id
= id
>= 0 && id
< SC_ENUM_END
;
311 const char *name
= valid_id
? ScFieldsInfo
[id
].name
.termedBuf() : "INVALID";
313 if (count
|| valid_id
)
314 storeAppendPrintf(sentry
, "%2d\t %-20s\t %5d\t %6.2f\n",
315 id
, name
, count
, xdiv(count
, dump_stat
->scParsedCount
));
319 httpHdrScStatDumper(StoreEntry
* sentry
, int, double val
, double, int count
)
321 extern const HttpHeaderStat
*dump_stat
; /* argh! */
322 const int id
= (int) val
;
323 const int valid_id
= id
>= 0 && id
< SC_ENUM_END
;
324 const char *name
= valid_id
? ScFieldsInfo
[id
].name
.termedBuf() : "INVALID";
326 if (count
|| valid_id
)
327 storeAppendPrintf(sentry
, "%2d\t %-20s\t %5d\t %6.2f\n",
328 id
, name
, count
, xdiv(count
, dump_stat
->scParsedCount
));
332 HttpHdrSc::findTarget(const char *target
)
338 HttpHdrScTarget
*sct
= (HttpHdrScTarget
*)node
->data
;
340 if (target
&& sct
->target
.size() > 0 && !strcmp(target
, sct
->target
.termedBuf()))
342 else if (!target
&& sct
->target
.size() == 0)
352 HttpHdrSc::getMergedTarget(const char *ourtarget
)
354 HttpHdrScTarget
*sctus
= findTarget(ourtarget
);
355 HttpHdrScTarget
*sctgeneric
= findTarget(NULL
);
357 if (sctgeneric
|| sctus
) {
358 HttpHdrScTarget
*sctusable
= new HttpHdrScTarget(NULL
);
361 sctusable
->mergeWith(sctgeneric
);
364 sctusable
->mergeWith(sctus
);