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