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