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