]> git.ipfire.org Git - thirdparty/squid.git/blob - src/refresh.cc
Merged from trunk rev.14189
[thirdparty/squid.git] / src / refresh.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 22 Refresh Calculation */
10
11 #include "squid.h"
12 #include "HttpHdrCc.h"
13 #include "HttpReply.h"
14 #include "HttpRequest.h"
15 #include "MemObject.h"
16 #include "mgr/Registration.h"
17 #include "RefreshPattern.h"
18 #include "SquidConfig.h"
19 #include "SquidTime.h"
20 #include "Store.h"
21 #include "URL.h"
22 #include "util.h"
23
24 typedef enum {
25 rcHTTP,
26 rcICP,
27 #if USE_HTCP
28 rcHTCP,
29 #endif
30 #if USE_CACHE_DIGESTS
31 rcCDigest,
32 #endif
33 rcStore,
34 rcCount
35 } refreshCountsEnum;
36
37 /**
38 * Flags indicating which staleness algorithm has been applied.
39 */
40 typedef struct {
41 bool expires; ///< Expires: header absolute timestamp limit
42 bool min; ///< Heuristic minimum age limited
43 bool lmfactor; ///< Last-Modified with heuristic determines limit
44 bool max; ///< Configured maximum age limit
45 } stale_flags;
46
47 /*
48 * This enumerated list assigns specific values, ala HTTP/FTP status
49 * codes. All Fresh codes are in the range 100-199 and all stale
50 * codes are 200-299. We might want to use these codes in logging,
51 * so best to keep them consistent over time.
52 */
53 enum {
54 FRESH_REQUEST_MAX_STALE_ALL = 100,
55 FRESH_REQUEST_MAX_STALE_VALUE,
56 FRESH_EXPIRES,
57 FRESH_LMFACTOR_RULE,
58 FRESH_MIN_RULE,
59 FRESH_OVERRIDE_EXPIRES,
60 FRESH_OVERRIDE_LASTMOD,
61 STALE_MUST_REVALIDATE = 200,
62 STALE_RELOAD_INTO_IMS,
63 STALE_FORCED_RELOAD,
64 STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
65 STALE_EXPIRES,
66 STALE_MAX_RULE,
67 STALE_LMFACTOR_RULE,
68 STALE_MAX_STALE,
69 STALE_DEFAULT = 299
70 };
71
72 static struct RefreshCounts {
73 const char *proto;
74 int total;
75 int status[STALE_DEFAULT + 1];
76 } refreshCounts[rcCount];
77
78 static const RefreshPattern *refreshUncompiledPattern(const char *);
79 static OBJH refreshStats;
80 static int refreshStaleness(const StoreEntry * entry, time_t check_time, const time_t age, const RefreshPattern * R, stale_flags * sf);
81
82 static RefreshPattern DefaultRefresh(".",std::regex_constants::syntax_option_type());
83
84 /** Locate the first refresh_pattern rule that matches the given URL by regex.
85 *
86 * \return A pointer to the refresh_pattern parameters to use, or nullptr if there is no match.
87 */
88 const RefreshPattern *
89 refreshLimits(const char *url)
90 {
91 for (auto R = Config.Refresh; R; R = R->next) {
92 ++(R->stats.matchTests);
93 if (R->pattern.match(url)) {
94 ++(R->stats.matchCount);
95 return R;
96 }
97 }
98
99 return nullptr;
100 }
101
102 /** Locate the first refresh_pattern rule that has the given uncompiled regex.
103 *
104 * \note There is only one reference to this function, below. It always passes "." as the pattern.
105 * This function is only ever called if there is no URI. Because a regex match is impossible, Squid
106 * forces the "." rule to apply (if it exists)
107 *
108 * \return A pointer to the refresh_pattern parameters to use, or nullptr if there is no match.
109 */
110 static const RefreshPattern *
111 refreshUncompiledPattern(const char *pat)
112 {
113 for (auto R = Config.Refresh; R; R = R->next) {
114 if (0 == strcmp(R->pattern.c_str(), pat))
115 return R;
116 }
117
118 return nullptr;
119 }
120
121 /**
122 * Calculate how stale the response is (or will be at the check_time).
123 *
124 * We try the following ways until one gives a result:
125 *
126 * 1. response expiration time, if one was set
127 * 2. age greater than configured maximum
128 * 3. last-modified factor algorithm
129 * 4. age less than configured minimum
130 * 5. default (stale)
131 *
132 * \param entry the StoreEntry being examined
133 * \param check_time the time (maybe future) at which we want to know whether $
134 * \param age the age of the entry at check_time
135 * \param R the refresh_pattern rule that matched this entry
136 * \param sf small struct to indicate reason for stale/fresh decision
137 *
138 * \retval -1 If the response is fresh.
139 * \retval >0 The amount of staleness.
140 * \retval 0 NOTE return value of 0 means the response is stale.
141 */
142 static int
143 refreshStaleness(const StoreEntry * entry, time_t check_time, const time_t age, const RefreshPattern * R, stale_flags * sf)
144 {
145 // 1. If the cached object has an explicit expiration time, then we rely on this and
146 // completely ignore the Min, Percent and Max values in the refresh_pattern.
147 if (entry->expires > -1) {
148 sf->expires = true;
149
150 if (entry->expires > check_time) {
151 debugs(22, 3, "FRESH: expires " << entry->expires <<
152 " >= check_time " << check_time << " ");
153
154 return -1;
155 } else {
156 debugs(22, 3, "STALE: expires " << entry->expires <<
157 " < check_time " << check_time << " ");
158
159 return (check_time - entry->expires);
160 }
161 }
162
163 debugs(22, 3, "No explicit expiry given, using heuristics to determine freshness");
164
165 // 2. If the entry is older than the maximum age in the refresh_pattern, it is STALE.
166 if (age > R->max) {
167 debugs(22, 3, "STALE: age " << age << " > max " << R->max << " ");
168 sf->max = true;
169 return (age - R->max);
170 }
171
172 // 3. If there is a Last-Modified header, try the last-modified factor algorithm.
173 if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
174
175 /* lastmod_delta is the difference between the last-modified date of the response
176 * and the time we cached it. It's how "old" the response was when we got it.
177 */
178 time_t lastmod_delta = entry->timestamp - entry->lastmod;
179
180 /* stale_age is the age of the response when it became/becomes stale according to
181 * the last-modified factor algorithm. It's how long we can consider the response
182 * fresh from the time we cached it.
183 */
184 time_t stale_age = static_cast<time_t>(lastmod_delta * R->pct);
185
186 debugs(22,3, "Last modified " << lastmod_delta << " sec before we cached it, L-M factor " <<
187 (100.0 * R->pct) << "% = " << stale_age << " sec freshness lifetime");
188 sf->lmfactor = true;
189
190 if (age >= stale_age) {
191 debugs(22, 3, "STALE: age " << age << " > stale_age " << stale_age);
192 return (age - stale_age);
193 } else {
194 debugs(22, 3, "FRESH: age " << age << " <= stale_age " << stale_age);
195 return -1;
196 }
197 }
198
199 // 4. If the entry is not as old as the minimum age in the refresh_pattern, it is FRESH.
200 if (age < R->min) {
201 debugs(22, 3, "FRESH: age (" << age << " sec) is less than configured minimum (" << R->min << " sec)");
202 sf->min = true;
203 return -1;
204 }
205
206 // 5. default is stale, by the amount we missed the minimum by
207 debugs(22, 3, "STALE: No explicit expiry, no last modified, and older than configured minimum.");
208 return (age - R->min);
209 }
210
211 /** Checks whether a store entry is fresh or stale, and why.
212 *
213 * This is where all aspects of request, response and squid configuration
214 * meet to decide whether a response is cacheable or not:
215 *
216 * 1. Client request headers that affect cacheability, e.g.
217 * - Cache-Control: no-cache
218 * - Cache-Control: max-age=N
219 * - Cache-Control: max-stale[=N]
220 * - Pragma: no-cache
221 *
222 * 2. Server response headers that affect cacheability, e.g.
223 * - Age:
224 * - Cache-Control: proxy-revalidate
225 * - Cache-Control: must-revalidate
226 * - Cache-Control: no-cache
227 * - Cache-Control: max-age=N
228 * - Cache-Control: s-maxage=N
229 * - Date:
230 * - Expires:
231 * - Last-Modified:
232 *
233 * 3. Configuration options, e.g.
234 * - reload-into-ims (refresh_pattern)
235 * - ignore-reload (refresh_pattern)
236 * - refresh-ims (refresh_pattern)
237 * - override-lastmod (refresh_pattern)
238 * - override-expire (refresh_pattern)
239 * - reload_into_ims (global option)
240 * - refresh_all_ims (global option)
241 *
242 * \returns a status code (from enum above):
243 * - FRESH_REQUEST_MAX_STALE_ALL
244 * - FRESH_REQUEST_MAX_STALE_VALUE
245 * - FRESH_EXPIRES
246 * - FRESH_LMFACTOR_RULE
247 * - FRESH_MIN_RULE
248 * - FRESH_OVERRIDE_EXPIRES
249 * - FRESH_OVERRIDE_LASTMOD
250 * - STALE_MUST_REVALIDATE
251 * - STALE_RELOAD_INTO_IMS
252 * - STALE_FORCED_RELOAD
253 * - STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE
254 * - STALE_EXPIRES
255 * - STALE_MAX_RULE
256 * - STALE_LMFACTOR_RULE
257 * - STALE_MAX_STALE
258 * - STALE_DEFAULT
259 *
260 * \note request may be NULL (e.g. for cache digests build)
261 *
262 * \note the store entry being examined is not necessarily cached (e.g. if
263 * this response is being evaluated for the first time)
264 */
265 static int
266 refreshCheck(const StoreEntry * entry, HttpRequest * request, time_t delta)
267 {
268 time_t age = 0;
269 time_t check_time = squid_curtime + delta;
270 int staleness;
271 stale_flags sf;
272
273 // get the URL of this entry, if there is one
274 static const SBuf nilUri("<none>");
275 SBuf uri = nilUri;
276 if (entry->mem_obj)
277 uri = entry->mem_obj->storeId();
278 else if (request)
279 uri = request->effectiveRequestUri();
280
281 debugs(22, 3, "checking freshness of URI: " << uri);
282
283 // age is not necessarily the age now, but the age at the given check_time
284 if (check_time > entry->timestamp)
285 age = check_time - entry->timestamp;
286
287 // FIXME: what to do when age < 0 or counter overflow?
288 assert(age >= 0);
289
290 /* We need a refresh rule. In order of preference:
291 *
292 * 1. the rule that matches this URI by regex
293 * 2. the "." rule from the config file
294 * 3. the default "." rule
295 */
296 // XXX: performance regression. c_str() reallocates
297 const RefreshPattern *R = (uri != nilUri) ? refreshLimits(uri.c_str()) : refreshUncompiledPattern(".");
298 if (NULL == R)
299 R = &DefaultRefresh;
300
301 debugs(22, 3, "Matched '" << R->pattern.c_str() << " " <<
302 (int) R->min << " " << (int) (100.0 * R->pct) << "%% " <<
303 (int) R->max << "'");
304
305 debugs(22, 3, "\tage:\t" << age);
306
307 debugs(22, 3, "\tcheck_time:\t" << mkrfc1123(check_time));
308
309 debugs(22, 3, "\tentry->timestamp:\t" << mkrfc1123(entry->timestamp));
310
311 if (request && !request->flags.ignoreCc) {
312 const HttpHdrCc *const cc = request->cache_control;
313 if (cc && cc->hasMinFresh()) {
314 const int32_t minFresh=cc->minFresh();
315 debugs(22, 3, "\tage + min-fresh:\t" << age << " + " <<
316 minFresh << " = " << age + minFresh);
317 debugs(22, 3, "\tcheck_time + min-fresh:\t" << check_time << " + "
318 << minFresh << " = " <<
319 mkrfc1123(check_time + minFresh));
320 age += minFresh;
321 check_time += minFresh;
322 }
323 }
324
325 memset(&sf, '\0', sizeof(sf));
326
327 staleness = refreshStaleness(entry, check_time, age, R, &sf);
328
329 debugs(22, 3, "Staleness = " << staleness);
330
331 // stale-if-error requires any failure be passed thru when its period is over.
332 if (request && entry->mem_obj && entry->mem_obj->getReply() && entry->mem_obj->getReply()->cache_control &&
333 entry->mem_obj->getReply()->cache_control->hasStaleIfError() &&
334 entry->mem_obj->getReply()->cache_control->staleIfError() < staleness) {
335
336 debugs(22, 3, "stale-if-error period expired. Will produce error if validation fails.");
337 request->flags.failOnValidationError = true;
338 }
339
340 /* If the origin server specified either of:
341 * Cache-Control: must-revalidate
342 * Cache-Control: proxy-revalidate
343 * the spec says the response must always be revalidated if stale.
344 */
345 if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1) {
346 debugs(22, 3, "YES: Must revalidate stale object (origin set must-revalidate, proxy-revalidate, no-cache, s-maxage, or private)");
347 if (request)
348 request->flags.failOnValidationError = true;
349 return STALE_MUST_REVALIDATE;
350 }
351
352 /* request-specific checks */
353 if (request && !request->flags.ignoreCc) {
354 HttpHdrCc *cc = request->cache_control;
355
356 /* If the request is an IMS request, and squid is configured NOT to service this from cache
357 * (either by 'refresh-ims' in the refresh pattern or 'refresh_all_ims on' globally)
358 * then force a reload from the origin.
359 */
360 if (request->flags.ims && (R->flags.refresh_ims || Config.onoff.refresh_all_ims)) {
361 // The client's no-cache header is changed into a IMS query
362 debugs(22, 3, "YES: Client IMS request forcing revalidation of object (refresh-ims option)");
363 return STALE_FORCED_RELOAD;
364 }
365
366 #if USE_HTTP_VIOLATIONS
367 /* Normally a client reload request ("Cache-Control: no-cache" or "Pragma: no-cache")
368 * means we must treat this reponse as STALE and fetch a new one.
369 *
370 * However, some options exist to override this behaviour. For example, we might just
371 * revalidate our existing response, or even just serve it up without revalidating it.
372 *
373 * ---- Note on the meaning of nocache_hack -----
374 *
375 * The nocache_hack flag has a very specific and complex meaning:
376 *
377 * (a) this is a reload request ("Cache-Control: no-cache" or "Pragma: no-cache" header)
378 * and (b) the configuration file either has at least one refresh_pattern with
379 * ignore-reload or reload-into-ims (not necessarily the rule matching this request) or
380 * the global reload_into_ims is set to on
381 *
382 * In other words: this is a client reload, and we might need to override
383 * the default behaviour (but we might not).
384 *
385 * "nocache_hack" is a pretty deceptive name for such a complicated meaning.
386 */
387 if (request->flags.noCacheHack()) {
388
389 if (R->flags.ignore_reload) {
390 /* The client's no-cache header is ignored completely - we'll try to serve
391 * what we have (assuming it's still fresh, etc.)
392 */
393 debugs(22, 3, "MAYBE: Ignoring client reload request - trying to serve from cache (ignore-reload option)");
394 } else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) {
395 /* The client's no-cache header is not honoured completely - we'll just try
396 * to revalidate our cached copy (IMS to origin) instead of fetching a new
397 * copy with an unconditional GET.
398 */
399 debugs(22, 3, "YES: Client reload request - cheating, only revalidating with origin (reload-into-ims option)");
400 return STALE_RELOAD_INTO_IMS;
401 } else {
402 /* The client's no-cache header is honoured - we fetch a new copy from origin */
403 debugs(22, 3, "YES: Client reload request - fetching new copy from origin");
404 request->flags.noCache = true;
405 return STALE_FORCED_RELOAD;
406 }
407 }
408 #endif
409
410 // Check the Cache-Control client request header
411 if (NULL != cc) {
412
413 // max-age directive
414 if (cc->hasMaxAge()) {
415 #if USE_HTTP_VIOLATIONS
416 // Ignore client "Cache-Control: max-age=0" header
417 if (R->flags.ignore_reload && cc->maxAge() == 0) {
418 debugs(22, 3, "MAYBE: Ignoring client reload request - trying to serve from cache (ignore-reload option)");
419 } else
420 #endif
421 {
422 // Honour client "Cache-Control: max-age=x" header
423 if (age > cc->maxAge() || cc->maxAge() == 0) {
424 debugs(22, 3, "YES: Revalidating object - client 'Cache-Control: max-age=" << cc->maxAge() << "'");
425 return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
426 }
427 }
428 }
429
430 // max-stale directive
431 if (cc->hasMaxStale() && staleness > -1) {
432 if (cc->maxStale()==HttpHdrCc::MAX_STALE_ANY) {
433 debugs(22, 3, "NO: Client accepts a stale response of any age - 'Cache-Control: max-stale'");
434 return FRESH_REQUEST_MAX_STALE_ALL;
435 } else if (staleness < cc->maxStale()) {
436 debugs(22, 3, "NO: Client accepts a stale response - 'Cache-Control: max-stale=" << cc->maxStale() << "'");
437 return FRESH_REQUEST_MAX_STALE_VALUE;
438 }
439 }
440 }
441 }
442
443 // If the object is fresh, return the right FRESH_ code
444 if (-1 == staleness) {
445 debugs(22, 3, "Object isn't stale..");
446 if (sf.expires) {
447 debugs(22, 3, "returning FRESH_EXPIRES");
448 return FRESH_EXPIRES;
449 }
450
451 assert(!sf.max);
452
453 if (sf.lmfactor) {
454 debugs(22, 3, "returning FRESH_LMFACTOR_RULE");
455 return FRESH_LMFACTOR_RULE;
456 }
457
458 assert(sf.min);
459
460 debugs(22, 3, "returning FRESH_MIN_RULE");
461 return FRESH_MIN_RULE;
462 }
463
464 /*
465 * At this point the response is stale, unless one of
466 * the override options kicks in.
467 * NOTE: max-stale config blocks the overrides.
468 */
469 int max_stale = (R->max_stale >= 0 ? R->max_stale : Config.maxStale);
470 if ( max_stale >= 0 && staleness > max_stale) {
471 debugs(22, 3, "YES: refresh_pattern max-stale=N limit from squid.conf");
472 if (request)
473 request->flags.failOnValidationError = true;
474 return STALE_MAX_STALE;
475 }
476
477 if (sf.expires) {
478 #if USE_HTTP_VIOLATIONS
479
480 if (R->flags.override_expire && age < R->min) {
481 debugs(22, 3, "NO: Serving from cache - even though explicit expiry has passed, we enforce Min value (override-expire option)");
482 return FRESH_OVERRIDE_EXPIRES;
483 }
484
485 #endif
486 return STALE_EXPIRES;
487 }
488
489 if (sf.max)
490 return STALE_MAX_RULE;
491
492 if (sf.lmfactor) {
493 #if USE_HTTP_VIOLATIONS
494 if (R->flags.override_lastmod && age < R->min) {
495 debugs(22, 3, "NO: Serving from cache - even though L-M factor says the object is stale, we enforce Min value (override-lastmod option)");
496 return FRESH_OVERRIDE_LASTMOD;
497 }
498 #endif
499 debugs(22, 3, "YES: L-M factor says the object is stale'");
500 return STALE_LMFACTOR_RULE;
501 }
502
503 debugs(22, 3, "returning STALE_DEFAULT");
504 return STALE_DEFAULT;
505 }
506
507 /**
508 * This is called by http.cc once it has received and parsed the origin server's
509 * response headers. It uses the result as part of its algorithm to decide whether a
510 * response should be cached.
511 *
512 * \retval true if the entry is cacheable, regardless of whether FRESH or STALE
513 * \retval false if the entry is not cacheable
514 *
515 * TODO: this algorithm seems a bit odd and might not be quite right. Verify against HTTPbis.
516 */
517 bool
518 refreshIsCachable(const StoreEntry * entry)
519 {
520 /*
521 * Don't look at the request to avoid no-cache and other nuisances.
522 * the object should have a mem_obj so the URL will be found there.
523 * minimum_expiry_time seconds delta (defaults to 60 seconds), to
524 * avoid objects which expire almost immediately, and which can't
525 * be refreshed.
526 */
527 int reason = refreshCheck(entry, NULL, Config.minimum_expiry_time);
528 ++ refreshCounts[rcStore].total;
529 ++ refreshCounts[rcStore].status[reason];
530
531 if (reason < STALE_MUST_REVALIDATE)
532 /* Does not need refresh. This is certainly cachable */
533 return true;
534
535 if (entry->lastmod < 0)
536 /* Last modified is needed to do a refresh */
537 return false;
538
539 if (entry->mem_obj == NULL)
540 /* no mem_obj? */
541 return true;
542
543 if (entry->getReply() == NULL)
544 /* no reply? */
545 return true;
546
547 if (entry->getReply()->content_length == 0)
548 /* No use refreshing (caching?) 0 byte objects */
549 return false;
550
551 /* This seems to be refreshable. Cache it */
552 return true;
553 }
554
555 /// whether reply is stale if it is a hit
556 static bool
557 refreshIsStaleIfHit(const int reason)
558 {
559 switch (reason) {
560 case FRESH_MIN_RULE:
561 case FRESH_LMFACTOR_RULE:
562 case FRESH_EXPIRES:
563 return false;
564 default:
565 return true;
566 }
567 }
568
569 /**
570 * Protocol-specific wrapper around refreshCheck() function.
571 *
572 * Note the reason for STALE/FRESH then return true/false respectively.
573 *
574 * \retval 1 if STALE
575 * \retval 0 if FRESH
576 */
577 int
578 refreshCheckHTTP(const StoreEntry * entry, HttpRequest * request)
579 {
580 int reason = refreshCheck(entry, request, 0);
581 ++ refreshCounts[rcHTTP].total;
582 ++ refreshCounts[rcHTTP].status[reason];
583 request->flags.staleIfHit = refreshIsStaleIfHit(reason);
584 return (Config.onoff.offline || reason < 200) ? 0 : 1;
585 }
586
587 /// \see int refreshCheckHTTP(const StoreEntry * entry, HttpRequest * request)
588 int
589 refreshCheckICP(const StoreEntry * entry, HttpRequest * request)
590 {
591 int reason = refreshCheck(entry, request, 30);
592 ++ refreshCounts[rcICP].total;
593 ++ refreshCounts[rcICP].status[reason];
594 return (reason < 200) ? 0 : 1;
595 }
596
597 #if USE_HTCP
598 /// \see int refreshCheckHTTP(const StoreEntry * entry, HttpRequest * request)
599 int
600 refreshCheckHTCP(const StoreEntry * entry, HttpRequest * request)
601 {
602 int reason = refreshCheck(entry, request, 10);
603 ++ refreshCounts[rcHTCP].total;
604 ++ refreshCounts[rcHTCP].status[reason];
605 return (reason < 200) ? 0 : 1;
606 }
607
608 #endif
609
610 #if USE_CACHE_DIGESTS
611 /// \see int refreshCheckHTTP(const StoreEntry * entry, HttpRequest * request)
612 int
613 refreshCheckDigest(const StoreEntry * entry, time_t delta)
614 {
615 int reason = refreshCheck(entry,
616 entry->mem_obj ? entry->mem_obj->request : NULL,
617 delta);
618 ++ refreshCounts[rcCDigest].total;
619 ++ refreshCounts[rcCDigest].status[reason];
620 return (reason < 200) ? 0 : 1;
621 }
622 #endif
623
624 /**
625 * Get the configured maximum caching time for objects with this URL
626 * according to refresh_pattern.
627 *
628 * Used by http.cc when generating a upstream requests to ensure that
629 * responses it is given are fresh enough to be worth caching.
630 *
631 * \retval pattern-max if there is a refresh_pattern matching the URL configured.
632 * \retval REFRESH_DEFAULT_MAX if there are no explicit limits configured
633 */
634 time_t
635 getMaxAge(const char *url)
636 {
637 const RefreshPattern *R;
638 debugs(22, 3, "getMaxAge: '" << url << "'");
639
640 if ((R = refreshLimits(url)))
641 return R->max;
642 else
643 return REFRESH_DEFAULT_MAX;
644 }
645
646 static int
647 refreshCountsStatsEntry(StoreEntry * sentry, struct RefreshCounts &rc, int code, const char *desc)
648 {
649 storeAppendPrintf(sentry, "%6d\t%6.2f\t%s\n", rc.status[code], xpercent(rc.status[code], rc.total), desc);
650 return rc.status[code];
651 }
652
653 static void
654 refreshCountsStats(StoreEntry * sentry, struct RefreshCounts &rc)
655 {
656 if (!rc.total)
657 return;
658
659 storeAppendPrintf(sentry, "\n\n%s histogram:\n", rc.proto);
660 storeAppendPrintf(sentry, "Count\t%%Total\tCategory\n");
661
662 int sum = 0;
663 sum += refreshCountsStatsEntry(sentry, rc, FRESH_REQUEST_MAX_STALE_ALL, "Fresh: request max-stale wildcard");
664 sum += refreshCountsStatsEntry(sentry, rc, FRESH_REQUEST_MAX_STALE_VALUE, "Fresh: request max-stale value");
665 sum += refreshCountsStatsEntry(sentry, rc, FRESH_EXPIRES, "Fresh: expires time not reached");
666 sum += refreshCountsStatsEntry(sentry, rc, FRESH_LMFACTOR_RULE, "Fresh: refresh_pattern last-mod factor percentage");
667 sum += refreshCountsStatsEntry(sentry, rc, FRESH_MIN_RULE, "Fresh: refresh_pattern min value");
668 sum += refreshCountsStatsEntry(sentry, rc, FRESH_OVERRIDE_EXPIRES, "Fresh: refresh_pattern override-expires");
669 sum += refreshCountsStatsEntry(sentry, rc, FRESH_OVERRIDE_LASTMOD, "Fresh: refresh_pattern override-lastmod");
670 sum += refreshCountsStatsEntry(sentry, rc, STALE_MUST_REVALIDATE, "Stale: response has must-revalidate");
671 sum += refreshCountsStatsEntry(sentry, rc, STALE_RELOAD_INTO_IMS, "Stale: changed reload into IMS");
672 sum += refreshCountsStatsEntry(sentry, rc, STALE_FORCED_RELOAD, "Stale: request has no-cache directive");
673 sum += refreshCountsStatsEntry(sentry, rc, STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE, "Stale: age exceeds request max-age value");
674 sum += refreshCountsStatsEntry(sentry, rc, STALE_EXPIRES, "Stale: expires time reached");
675 sum += refreshCountsStatsEntry(sentry, rc, STALE_MAX_RULE, "Stale: refresh_pattern max age rule");
676 sum += refreshCountsStatsEntry(sentry, rc, STALE_LMFACTOR_RULE, "Stale: refresh_pattern last-mod factor percentage");
677 sum += refreshCountsStatsEntry(sentry, rc, STALE_DEFAULT, "Stale: by default");
678 storeAppendPrintf(sentry, "\n");
679 }
680
681 static void
682 refreshStats(StoreEntry * sentry)
683 {
684 // display per-rule counts of usage and tests
685 storeAppendPrintf(sentry, "\nRefresh pattern usage:\n\n");
686 storeAppendPrintf(sentry, " Used \tChecks \t%% Matches\tPattern\n");
687 for (const RefreshPattern *R = Config.Refresh; R; R = R->next) {
688 storeAppendPrintf(sentry, " %10" PRIu64 "\t%10" PRIu64 "\t%6.2f\t%s%s\n",
689 R->stats.matchCount,
690 R->stats.matchTests,
691 xpercent(R->stats.matchCount, R->stats.matchTests),
692 (R->pattern.flags & std::regex::icase ? "-i " : ""),
693 R->pattern.c_str());
694 }
695
696 int i;
697 int total = 0;
698
699 /* get total usage count */
700
701 for (i = 0; i < rcCount; ++i)
702 total += refreshCounts[i].total;
703
704 /* protocol usage histogram */
705 storeAppendPrintf(sentry, "\nRefreshCheck calls per protocol\n\n");
706
707 storeAppendPrintf(sentry, "Protocol\t#Calls\t%%Calls\n");
708
709 for (i = 0; i < rcCount; ++i)
710 storeAppendPrintf(sentry, "%10s\t%6d\t%6.2f\n",
711 refreshCounts[i].proto,
712 refreshCounts[i].total,
713 xpercent(refreshCounts[i].total, total));
714
715 /* per protocol histograms */
716 storeAppendPrintf(sentry, "\n\nRefreshCheck histograms for various protocols\n");
717
718 for (i = 0; i < rcCount; ++i)
719 refreshCountsStats(sentry, refreshCounts[i]);
720 }
721
722 static void
723 refreshRegisterWithCacheManager(void)
724 {
725 Mgr::RegisterAction("refresh", "Refresh Algorithm Statistics", refreshStats, 0, 1);
726 }
727
728 void
729 refreshInit(void)
730 {
731 memset(refreshCounts, 0, sizeof(refreshCounts));
732 refreshCounts[rcHTTP].proto = "HTTP";
733 refreshCounts[rcICP].proto = "ICP";
734 #if USE_HTCP
735
736 refreshCounts[rcHTCP].proto = "HTCP";
737 #endif
738
739 refreshCounts[rcStore].proto = "On Store";
740 #if USE_CACHE_DIGESTS
741
742 refreshCounts[rcCDigest].proto = "Cache Digests";
743 #endif
744
745 refreshRegisterWithCacheManager();
746 }
747