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