]> git.ipfire.org Git - thirdparty/squid.git/blame - src/refresh.cc
Import of fix-ranges branch
[thirdparty/squid.git] / src / refresh.cc
CommitLineData
e4e6a8db 1
2/*
528b2c61 3 * $Id: refresh.cc,v 1.59 2003/01/23 00:37:25 robertc Exp $
e4e6a8db 4 *
5 * DEBUG: section 22 Refresh Calculation
6 * AUTHOR: Harvest Derived
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
e4e6a8db 10 *
2b6662ba 11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
e4e6a8db 19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
e4e6a8db 34 */
35
36#ifndef USE_POSIX_REGEX
37#define USE_POSIX_REGEX /* put before includes; always use POSIX */
38#endif
39
40#include "squid.h"
e6ccf245 41#include "Store.h"
528b2c61 42#include "MemObject.h"
e4e6a8db 43
7d47d8e6 44typedef enum {
65fa5c61 45 rcHTTP,
46 rcICP,
47#if USE_HTCP
48 rcHTCP,
49#endif
50#if USE_CACHE_DIGESTS
51 rcCDigest,
52#endif
53 rcStore,
54 rcCount
7d47d8e6 55} refreshCountsEnum;
829a9357 56
65fa5c61 57typedef struct {
58 unsigned int expires:1;
59 unsigned int min:1;
60 unsigned int lmfactor:1;
61 unsigned int max;
62} stale_flags;
63
64/*
65 * This enumerated list assigns specific values, ala HTTP/FTP status
66 * codes. All Fresh codes are in the range 100-199 and all stale
67 * codes are 200-299. We might want to use these codes in logging,
68 * so best to keep them consistent over time.
69 */
70enum {
71 FRESH_REQUEST_MAX_STALE_ALL = 100,
72 FRESH_REQUEST_MAX_STALE_VALUE,
73 FRESH_EXPIRES,
74 FRESH_LMFACTOR_RULE,
75 FRESH_MIN_RULE,
76 FRESH_OVERRIDE_EXPIRES,
77 FRESH_OVERRIDE_LASTMOD,
78 STALE_MUST_REVALIDATE = 200,
79 STALE_RELOAD_INTO_IMS,
80 STALE_FORCED_RELOAD,
81 STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
82 STALE_EXPIRES,
83 STALE_MAX_RULE,
84 STALE_LMFACTOR_RULE,
85 STALE_DEFAULT = 299
86};
87
829a9357 88static struct RefreshCounts {
89 const char *proto;
1c3e77cd 90 int total;
65fa5c61 91 int status[STALE_DEFAULT + 1];
829a9357 92} refreshCounts[rcCount];
1c3e77cd 93
e4e6a8db 94/*
95 * Defaults:
96 * MIN NONE
97 * PCT 20%
98 * MAX 3 days
99 */
48f44632 100#define REFRESH_DEFAULT_MIN (time_t)0
c3f6d204 101#define REFRESH_DEFAULT_PCT 0.20
48f44632 102#define REFRESH_DEFAULT_MAX (time_t)259200
e4e6a8db 103
0cdcddb9 104static const refresh_t *refreshLimits(const char *);
2b5133db 105static const refresh_t *refreshUncompiledPattern(const char *);
1c3e77cd 106static OBJH refreshStats;
65fa5c61 107static int refreshStaleness(const StoreEntry *, time_t, time_t, const refresh_t *, stale_flags *);
108
109static refresh_t DefaultRefresh;
2b5133db 110
6018f0de 111static const refresh_t *
112refreshLimits(const char *url)
113{
114 const refresh_t *R;
115 for (R = Config.Refresh; R; R = R->next) {
116 if (!regexec(&(R->compiled_pattern), url, 0, 0, 0))
117 return R;
118 }
119 return NULL;
120}
121
2b5133db 122static const refresh_t *
123refreshUncompiledPattern(const char *pat)
124{
125 const refresh_t *R;
126 for (R = Config.Refresh; R; R = R->next) {
127 if (0 == strcmp(R->pattern, pat))
128 return R;
129 }
130 return NULL;
131}
132
65fa5c61 133/*
134 * Calculate how stale the response is (or will be at the check_time).
135 * Staleness calculation is based on the following: (1) response
136 * expiration time, (2) age greater than configured maximum, (3)
137 * last-modified factor, and (4) age less than configured minimum.
138 *
139 * If the response is fresh, return -1. Otherwise return its
140 * staleness. NOTE return value of 0 means the response is stale.
141 *
142 * The 'stale_flags' structure is used to tell the calling function
143 * _why_ this response is fresh or stale. Its used, for example,
144 * when the admin wants to override expiration and last-modified
145 * times.
146 */
147static int
148refreshStaleness(const StoreEntry * entry, time_t check_time, time_t age, const refresh_t * R, stale_flags * sf)
149{
150 /*
151 * Check for an explicit expiration time.
152 */
153 if (entry->expires > -1) {
154 sf->expires = 1;
155 if (entry->expires > check_time) {
156 debug(22, 3) ("FRESH: expires %d >= check_time %d \n",
157 (int) entry->expires, (int) check_time);
158 return -1;
159 } else {
160 debug(22, 3) ("STALE: expires %d < check_time %d \n",
161 (int) entry->expires, (int) check_time);
162 return (check_time - entry->expires);
163 }
164 }
165 assert(age >= 0);
166 /*
167 * Use local heuristics to determine staleness. Start with the
168 * max age from the refresh_pattern rule.
169 */
170 if (age > R->max) {
171 debug(22, 3) ("STALE: age %d > max %d \n", (int) age, (int) R->max);
172 sf->max = 1;
173 return (age - R->max);
174 }
175 /*
176 * Try the last-modified factor algorithm.
177 */
178 if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) {
179 /*
180 * stale_age is the Age of the response when it became/becomes
181 * stale according to the last-modified factor algorithm.
182 */
e6ccf245 183 time_t stale_age = static_cast<time_t>((entry->timestamp - entry->lastmod) * R->pct);
65fa5c61 184 sf->lmfactor = 1;
185 if (age >= stale_age) {
186 debug(22, 3) ("STALE: age %d > stale_age %d\n",
187 (int) age, (int) stale_age);
188 return (age - stale_age);
189 } else {
190 debug(22, 3) ("FRESH: age %d <= stale_age %d\n",
191 (int) age, (int) stale_age);
192 return -1;
193 }
194 }
195 /*
196 * If we are here, staleness is determined by the refresh_pattern
197 * configured minimum age.
198 */
199 if (age <= R->min) {
200 debug(22, 3) ("FRESH: age %d <= min %d\n", (int) age, (int) R->min);
201 sf->min = 1;
202 return -1;
203 }
204 debug(22, 3) ("STALE: age %d > min %d\n", (int) age, (int) R->min);
205 return (age - R->min);
206}
207
829a9357 208/* return 1 if the entry must be revalidated within delta seconds
209 * 0 otherwise
210 *
211 * note: request maybe null (e.g. for cache digests build)
e4e6a8db 212 */
829a9357 213static int
65fa5c61 214refreshCheck(const StoreEntry * entry, request_t * request, time_t delta)
e4e6a8db 215{
6018f0de 216 const refresh_t *R;
829a9357 217 const char *uri = NULL;
65fa5c61 218 time_t age = 0;
a207429f 219 time_t check_time = squid_curtime + delta;
65fa5c61 220 int staleness;
221 stale_flags sf;
9b5d1d21 222 if (entry->mem_obj)
223 uri = entry->mem_obj->url;
7d47d8e6 224 else if (request)
9b5d1d21 225 uri = urlCanonical(request);
7d47d8e6 226
65fa5c61 227 debug(22, 3) ("refreshCheck: '%s'\n", uri ? uri : "<none>");
228
229 if (check_time > entry->timestamp)
230 age = check_time - entry->timestamp;
231 R = uri ? refreshLimits(uri) : refreshUncompiledPattern(".");
232 if (NULL == R)
233 R = &DefaultRefresh;
234 memset(&sf, '\0', sizeof(sf));
235 staleness = refreshStaleness(entry, check_time, age, R, &sf);
236 debug(22, 3) ("Staleness = %d\n", staleness);
237
a3d5953d 238 debug(22, 3) ("refreshCheck: Matched '%s %d %d%% %d'\n",
65fa5c61 239 R->pattern, (int) R->min, (int) (100.0 * R->pct), (int) R->max);
a3d5953d 240 debug(22, 3) ("refreshCheck: age = %d\n", (int) age);
49d3fcb0 241 debug(22, 3) ("\tcheck_time:\t%s\n", mkrfc1123(check_time));
242 debug(22, 3) ("\tentry->timestamp:\t%s\n", mkrfc1123(entry->timestamp));
65fa5c61 243
244 if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1) {
245 debug(22, 3) ("refreshCheck: YES: Must revalidate stale response\n");
246 return STALE_MUST_REVALIDATE;
247 }
829a9357 248 /* request-specific checks */
249 if (request) {
65fa5c61 250 HttpHdrCc *cc = request->cache_control;
9f60cfdf 251#if HTTP_VIOLATIONS
65fa5c61 252 if (!request->flags.nocache_hack) {
253 (void) 0;
254 } else if (R->flags.ignore_reload) {
255 /* The clients no-cache header is ignored */
256 debug(22, 3) ("refreshCheck: MAYBE: ignore-reload\n");
257 } else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) {
258 /* The clients no-cache header is changed into a IMS query */
259 debug(22, 3) ("refreshCheck: YES: reload-into-ims\n");
260 return STALE_RELOAD_INTO_IMS;
261 } else {
262 /* The clients no-cache header is not overridden on this request */
263 debug(22, 3) ("refreshCheck: YES: client reload\n");
264 request->flags.nocache = 1;
265 return STALE_FORCED_RELOAD;
cbe3a719 266 }
9f60cfdf 267#endif
65fa5c61 268 if (NULL != cc) {
269 if (cc->max_age > -1) {
085008f9 270#if HTTP_VIOLATIONS
271 if (R->flags.ignore_reload && cc->max_age == 0) {
272 } else
528b2c61 273#endif
274 {
275#if 0
276 if (cc->max_age == 0) {
277 debug (22,3) ("refreshCheck: YES: client-max-age = 0\n");
278 return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
279 }
085008f9 280#endif
65fa5c61 281 if (age > cc->max_age) {
282 debug(22, 3) ("refreshCheck: YES: age > client-max-age\n");
283 return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE;
284 }
528b2c61 285 }
65fa5c61 286 }
287 if (EBIT_TEST(cc->mask, CC_MAX_STALE) && staleness > -1) {
288 if (cc->max_stale < 0) {
289 /* max-stale directive without a value */
290 debug(22, 3) ("refreshCheck: NO: max-stale wildcard\n");
291 return FRESH_REQUEST_MAX_STALE_ALL;
292 } else if (staleness < cc->max_stale) {
293 debug(22, 3) ("refreshCheck: NO: staleness < max-stale\n");
294 return FRESH_REQUEST_MAX_STALE_VALUE;
295 }
829a9357 296 }
48f44632 297 }
298 }
65fa5c61 299 if (-1 == staleness) {
300 if (sf.expires)
301 return FRESH_EXPIRES;
302 assert(!sf.max);
303 if (sf.lmfactor)
304 return FRESH_LMFACTOR_RULE;
305 assert(sf.min);
306 return FRESH_MIN_RULE;
1dfa1d81 307 }
65fa5c61 308 /*
309 * At this point the response is stale, unless one of
542c4d60 310 * the override options kicks in.
65fa5c61 311 */
312 if (sf.expires) {
313#if HTTP_VIOLATIONS
314 if (R->flags.override_expire && age < R->min) {
315 debug(22, 3) ("refreshCheck: NO: age < min && override-expire\n");
316 return FRESH_OVERRIDE_EXPIRES;
34308e0f 317 }
65fa5c61 318#endif
319 return STALE_EXPIRES;
e4e6a8db 320 }
65fa5c61 321 if (sf.max)
322 return STALE_MAX_RULE;
323 if (sf.lmfactor) {
9f60cfdf 324#if HTTP_VIOLATIONS
65fa5c61 325 if (R->flags.override_lastmod && age < R->min) {
326 debug(22, 3) ("refreshCheck: NO: age < min && override-lastmod\n");
327 return FRESH_OVERRIDE_LASTMOD;
0d508d5b 328 }
65fa5c61 329#endif
330 return STALE_LMFACTOR_RULE;
e4e6a8db 331 }
65fa5c61 332 return STALE_DEFAULT;
e4e6a8db 333}
48f44632 334
cfa9f1cb 335int
336refreshIsCachable(const StoreEntry * entry)
337{
338 /*
339 * Don't look at the request to avoid no-cache and other nuisances.
340 * the object should have a mem_obj so the URL will be found there.
341 * 60 seconds delta, to avoid objects which expire almost
342 * immediately, and which can't be refreshed.
343 */
65fa5c61 344 int reason = refreshCheck(entry, NULL, 60);
345 refreshCounts[rcStore].total++;
346 refreshCounts[rcStore].status[reason]++;
347 if (reason < 200)
cfa9f1cb 348 /* Does not need refresh. This is certainly cachable */
349 return 1;
350 if (entry->lastmod < 0)
351 /* Last modified is needed to do a refresh */
352 return 0;
353 if (entry->mem_obj == NULL)
354 /* no mem_obj? */
355 return 1;
528b2c61 356 if (entry->getReply() == NULL)
cfa9f1cb 357 /* no reply? */
358 return 1;
528b2c61 359 if (entry->getReply()->content_length == 0)
cfa9f1cb 360 /* No use refreshing (caching?) 0 byte objects */
361 return 0;
362 /* This seems to be refreshable. Cache it */
363 return 1;
364}
365
829a9357 366/* refreshCheck... functions below are protocol-specific wrappers around
367 * refreshCheck() function above */
368
369int
7d47d8e6 370refreshCheckHTTP(const StoreEntry * entry, request_t * request)
371{
65fa5c61 372 int reason = refreshCheck(entry, request, 0);
373 refreshCounts[rcHTTP].total++;
374 refreshCounts[rcHTTP].status[reason]++;
375 return (reason < 200) ? 0 : 1;
829a9357 376}
377
378int
7d47d8e6 379refreshCheckICP(const StoreEntry * entry, request_t * request)
380{
65fa5c61 381 int reason = refreshCheck(entry, request, 30);
382 refreshCounts[rcICP].total++;
383 refreshCounts[rcICP].status[reason]++;
384 return (reason < 200) ? 0 : 1;
829a9357 385}
386
65fa5c61 387#if USE_HTCP
32b3cf93 388int
389refreshCheckHTCP(const StoreEntry * entry, request_t * request)
390{
65fa5c61 391 int reason = refreshCheck(entry, request, 10);
392 refreshCounts[rcHTCP].total++;
393 refreshCounts[rcHTCP].status[reason]++;
394 return (reason < 200) ? 0 : 1;
32b3cf93 395}
65fa5c61 396#endif
32b3cf93 397
65fa5c61 398#if USE_CACHE_DIGESTS
829a9357 399int
7d47d8e6 400refreshCheckDigest(const StoreEntry * entry, time_t delta)
401{
65fa5c61 402 int reason = refreshCheck(entry,
c68e9c6b 403 entry->mem_obj ? entry->mem_obj->request : NULL,
65fa5c61 404 delta);
405 refreshCounts[rcCDigest].total++;
406 refreshCounts[rcCDigest].status[reason]++;
407 return (reason < 200) ? 0 : 1;
6018f0de 408}
65fa5c61 409#endif
6018f0de 410
48f44632 411time_t
412getMaxAge(const char *url)
413{
6018f0de 414 const refresh_t *R;
a3d5953d 415 debug(22, 3) ("getMaxAge: '%s'\n", url);
6018f0de 416 if ((R = refreshLimits(url)))
417 return R->max;
418 else
419 return REFRESH_DEFAULT_MAX;
48f44632 420}
1c3e77cd 421
829a9357 422static void
423refreshCountsStats(StoreEntry * sentry, struct RefreshCounts *rc)
424{
cc7cfa8e 425 int sum = 0;
426 int tot = rc->total;
427
829a9357 428 storeAppendPrintf(sentry, "\n\n%s histogram:\n", rc->proto);
65fa5c61 429 storeAppendPrintf(sentry, "Count\t%%Total\tCategory\n");
829a9357 430
65fa5c61 431#define refreshCountsStatsEntry(code,desc) { \
432 storeAppendPrintf(sentry, "%6d\t%6.2f\t%s\n", \
433 rc->status[code], xpercent(rc->status[code], tot), desc); \
434 sum += rc->status[code]; \
cc7cfa8e 435}
65fa5c61 436
437 refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_ALL,
438 "Fresh: request max-stale wildcard");
439 refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_VALUE,
440 "Fresh: request max-stale value");
441 refreshCountsStatsEntry(FRESH_EXPIRES,
442 "Fresh: expires time not reached");
443 refreshCountsStatsEntry(FRESH_LMFACTOR_RULE,
444 "Fresh: refresh_pattern last-mod factor percentage");
445 refreshCountsStatsEntry(FRESH_MIN_RULE,
446 "Fresh: refresh_pattern min value");
447 refreshCountsStatsEntry(FRESH_OVERRIDE_EXPIRES,
448 "Fresh: refresh_pattern override expires");
449 refreshCountsStatsEntry(FRESH_OVERRIDE_LASTMOD,
450 "Fresh: refresh_pattern override lastmod");
451 refreshCountsStatsEntry(STALE_MUST_REVALIDATE,
452 "Stale: response has must-revalidate");
453 refreshCountsStatsEntry(STALE_RELOAD_INTO_IMS,
454 "Stale: changed reload into IMS");
455 refreshCountsStatsEntry(STALE_FORCED_RELOAD,
456 "Stale: request has no-cache directive");
457 refreshCountsStatsEntry(STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE,
458 "Stale: age exceeds request max-age value");
459 refreshCountsStatsEntry(STALE_EXPIRES,
460 "Stale: expires time reached");
461 refreshCountsStatsEntry(STALE_MAX_RULE,
462 "Stale: refresh_pattern max age rule");
463 refreshCountsStatsEntry(STALE_LMFACTOR_RULE,
464 "Stale: refresh_pattern last-mod factor percentage");
465 refreshCountsStatsEntry(STALE_DEFAULT,
466 "Stale: by default");
467
7d47d8e6 468 tot = sum; /* paranoid: "total" line shows 100% if we forgot nothing */
65fa5c61 469 storeAppendPrintf(sentry, "%6d\t%6.2f\tTOTAL\n",
470 rc->total, xpercent(rc->total, tot));
471 \
472 storeAppendPrintf(sentry, "\n");
829a9357 473}
474
1c3e77cd 475static void
476refreshStats(StoreEntry * sentry)
477{
829a9357 478 int i;
479 int total = 0;
480
481 /* get total usage count */
482 for (i = 0; i < rcCount; ++i)
483 total += refreshCounts[i].total;
484
485 /* protocol usage histogram */
486 storeAppendPrintf(sentry, "\nRefreshCheck calls per protocol\n\n");
487 storeAppendPrintf(sentry, "Protocol\t#Calls\t%%Calls\n");
488 for (i = 0; i < rcCount; ++i)
489 storeAppendPrintf(sentry, "%10s\t%6d\t%6.2f\n",
490 refreshCounts[i].proto,
7d47d8e6 491 refreshCounts[i].total,
829a9357 492 xpercent(refreshCounts[i].total, total));
493
494 /* per protocol histograms */
495 storeAppendPrintf(sentry, "\n\nRefreshCheck histograms for various protocols\n");
496 for (i = 0; i < rcCount; ++i)
42125db6 497 refreshCountsStats(sentry, &refreshCounts[i]);
1c3e77cd 498}
499
500void
9bc73deb 501refreshInit(void)
1c3e77cd 502{
829a9357 503 memset(refreshCounts, 0, sizeof(refreshCounts));
504 refreshCounts[rcHTTP].proto = "HTTP";
505 refreshCounts[rcICP].proto = "ICP";
65fa5c61 506#if USE_HTCP
32b3cf93 507 refreshCounts[rcHTCP].proto = "HTCP";
65fa5c61 508#endif
cfa9f1cb 509 refreshCounts[rcStore].proto = "On Store";
65fa5c61 510#if USE_CACHE_DIGESTS
829a9357 511 refreshCounts[rcCDigest].proto = "Cache Digests";
65fa5c61 512#endif
1c3e77cd 513 cachemgrRegister("refresh",
514 "Refresh Algorithm Statistics",
515 refreshStats,
516 0,
517 1);
65fa5c61 518 memset(&DefaultRefresh, '\0', sizeof(DefaultRefresh));
519 DefaultRefresh.pattern = "<none>";
520 DefaultRefresh.min = REFRESH_DEFAULT_MIN;
521 DefaultRefresh.pct = REFRESH_DEFAULT_PCT;
522 DefaultRefresh.max = REFRESH_DEFAULT_MAX;
1c3e77cd 523}