]>
Commit | Line | Data |
---|---|---|
e4e6a8db | 1 | |
2 | /* | |
e4e6a8db | 3 | * DEBUG: section 22 Refresh Calculation |
4 | * AUTHOR: Harvest Derived | |
5 | * | |
2b6662ba | 6 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 7 | * ---------------------------------------------------------- |
e4e6a8db | 8 | * |
2b6662ba | 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. | |
e4e6a8db | 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. | |
26ac0430 | 22 | * |
e4e6a8db | 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. | |
26ac0430 | 27 | * |
e4e6a8db | 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 | |
cbdec147 | 30 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
e25c139f | 31 | * |
e4e6a8db | 32 | */ |
33 | ||
34 | #ifndef USE_POSIX_REGEX | |
35 | #define USE_POSIX_REGEX /* put before includes; always use POSIX */ | |
36 | #endif | |
37 | ||
582c2af2 | 38 | #include "squid.h" |
8822ebee | 39 | #include "mgr/Registration.h" |
582c2af2 | 40 | #include "HttpHdrCc.h" |
a2ac85d9 | 41 | #include "HttpRequest.h" |
924f73bc | 42 | #include "HttpReply.h" |
582c2af2 | 43 | #include "MemObject.h" |
8d9a8184 | 44 | #include "RefreshPattern.h" |
985c86bc | 45 | #include "SquidTime.h" |
4d5904f7 | 46 | #include "SquidConfig.h" |
582c2af2 | 47 | #include "Store.h" |
b1bd952a | 48 | #include "URL.h" |
e4e6a8db | 49 | |
7d47d8e6 | 50 | typedef enum { |
65fa5c61 | 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 | |
7d47d8e6 | 61 | } refreshCountsEnum; |
829a9357 | 62 | |
26ac0430 | 63 | typedef struct { |
1f848b2c | 64 | bool expires; |
65 | bool min; | |
66 | bool lmfactor; | |
67 | bool max; | |
2fadd50d | 68 | } stale_flags; |
65fa5c61 | 69 | |
70 | /* | |
71 | * This enumerated list assigns specific values, ala HTTP/FTP status | |
72 | * codes. All Fresh codes are in the range 100-199 and all stale | |
73 | * codes are 200-299. We might want to use these codes in logging, | |
74 | * so best to keep them consistent over time. | |
75 | */ | |
76 | enum { | |
77 | FRESH_REQUEST_MAX_STALE_ALL = 100, | |
78 | FRESH_REQUEST_MAX_STALE_VALUE, | |
79 | FRESH_EXPIRES, | |
80 | FRESH_LMFACTOR_RULE, | |
81 | FRESH_MIN_RULE, | |
82 | FRESH_OVERRIDE_EXPIRES, | |
83 | FRESH_OVERRIDE_LASTMOD, | |
84 | STALE_MUST_REVALIDATE = 200, | |
85 | STALE_RELOAD_INTO_IMS, | |
86 | STALE_FORCED_RELOAD, | |
87 | STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE, | |
88 | STALE_EXPIRES, | |
89 | STALE_MAX_RULE, | |
90 | STALE_LMFACTOR_RULE, | |
570d3f75 | 91 | STALE_MAX_STALE, |
65fa5c61 | 92 | STALE_DEFAULT = 299 |
93 | }; | |
94 | ||
26ac0430 | 95 | static struct RefreshCounts { |
829a9357 | 96 | const char *proto; |
1c3e77cd | 97 | int total; |
65fa5c61 | 98 | int status[STALE_DEFAULT + 1]; |
62e76326 | 99 | } |
100 | ||
101 | refreshCounts[rcCount]; | |
1c3e77cd | 102 | |
e4e6a8db | 103 | /* |
104 | * Defaults: | |
105 | * MIN NONE | |
106 | * PCT 20% | |
107 | * MAX 3 days | |
108 | */ | |
48f44632 | 109 | #define REFRESH_DEFAULT_MIN (time_t)0 |
c3f6d204 | 110 | #define REFRESH_DEFAULT_PCT 0.20 |
48f44632 | 111 | #define REFRESH_DEFAULT_MAX (time_t)259200 |
e4e6a8db | 112 | |
8d9a8184 | 113 | static const RefreshPattern *refreshUncompiledPattern(const char *); |
1c3e77cd | 114 | static OBJH refreshStats; |
8d9a8184 | 115 | static int refreshStaleness(const StoreEntry * entry, time_t check_time, const time_t age, const RefreshPattern * R, stale_flags * sf); |
65fa5c61 | 116 | |
8d9a8184 | 117 | static RefreshPattern DefaultRefresh; |
2b5133db | 118 | |
8d9a8184 | 119 | const RefreshPattern * |
6018f0de | 120 | refreshLimits(const char *url) |
121 | { | |
8d9a8184 | 122 | const RefreshPattern *R; |
62e76326 | 123 | |
6018f0de | 124 | for (R = Config.Refresh; R; R = R->next) { |
62e76326 | 125 | if (!regexec(&(R->compiled_pattern), url, 0, 0, 0)) |
126 | return R; | |
6018f0de | 127 | } |
62e76326 | 128 | |
6018f0de | 129 | return NULL; |
130 | } | |
131 | ||
8d9a8184 | 132 | static const RefreshPattern * |
2b5133db | 133 | refreshUncompiledPattern(const char *pat) |
134 | { | |
8d9a8184 | 135 | const RefreshPattern *R; |
62e76326 | 136 | |
2b5133db | 137 | for (R = Config.Refresh; R; R = R->next) { |
62e76326 | 138 | if (0 == strcmp(R->pattern, pat)) |
139 | return R; | |
2b5133db | 140 | } |
62e76326 | 141 | |
2b5133db | 142 | return NULL; |
143 | } | |
144 | ||
b2f01ec3 | 145 | /** |
65fa5c61 | 146 | * Calculate how stale the response is (or will be at the check_time). |
147 | * Staleness calculation is based on the following: (1) response | |
148 | * expiration time, (2) age greater than configured maximum, (3) | |
149 | * last-modified factor, and (4) age less than configured minimum. | |
150 | * | |
b2f01ec3 AJ |
151 | * \retval -1 If the response is fresh. |
152 | * \retval >0 Otherwise return it's staleness. | |
153 | * \retval 0 NOTE return value of 0 means the response is stale. | |
65fa5c61 | 154 | * |
155 | * The 'stale_flags' structure is used to tell the calling function | |
156 | * _why_ this response is fresh or stale. Its used, for example, | |
157 | * when the admin wants to override expiration and last-modified | |
158 | * times. | |
159 | */ | |
160 | static int | |
8d9a8184 | 161 | refreshStaleness(const StoreEntry * entry, time_t check_time, const time_t age, const RefreshPattern * R, stale_flags * sf) |
65fa5c61 | 162 | { |
b2f01ec3 AJ |
163 | /** \par |
164 | * Check for an explicit expiration time (Expires: header). | |
65fa5c61 | 165 | */ |
166 | if (entry->expires > -1) { | |
1f848b2c | 167 | sf->expires = true; |
62e76326 | 168 | |
169 | if (entry->expires > check_time) { | |
4a7a3d56 | 170 | debugs(22, 3, "FRESH: expires " << entry->expires << |
171 | " >= check_time " << check_time << " "); | |
bf8fe701 | 172 | |
62e76326 | 173 | return -1; |
174 | } else { | |
4a7a3d56 | 175 | debugs(22, 3, "STALE: expires " << entry->expires << |
176 | " < check_time " << check_time << " "); | |
bf8fe701 | 177 | |
62e76326 | 178 | return (check_time - entry->expires); |
179 | } | |
65fa5c61 | 180 | } |
62e76326 | 181 | |
b2f01ec3 | 182 | /** \par |
65fa5c61 | 183 | * Use local heuristics to determine staleness. Start with the |
184 | * max age from the refresh_pattern rule. | |
185 | */ | |
186 | if (age > R->max) { | |
4a7a3d56 | 187 | debugs(22, 3, "STALE: age " << age << " > max " << R->max << " "); |
1f848b2c | 188 | sf->max = true; |
62e76326 | 189 | return (age - R->max); |
65fa5c61 | 190 | } |
62e76326 | 191 | |
b2f01ec3 AJ |
192 | /** \par |
193 | * Try the last-modified factor algorithm: refresh_pattern n% percentage of Last-Modified: age. | |
65fa5c61 | 194 | */ |
195 | if (entry->lastmod > -1 && entry->timestamp > entry->lastmod) { | |
62e76326 | 196 | /* |
197 | * stale_age is the Age of the response when it became/becomes | |
198 | * stale according to the last-modified factor algorithm. | |
199 | */ | |
200 | time_t stale_age = static_cast<time_t>((entry->timestamp - entry->lastmod) * R->pct); | |
1f848b2c | 201 | sf->lmfactor = true; |
62e76326 | 202 | |
203 | if (age >= stale_age) { | |
4a7a3d56 | 204 | debugs(22, 3, "STALE: age " << age << " > stale_age " << stale_age); |
62e76326 | 205 | return (age - stale_age); |
206 | } else { | |
4a7a3d56 | 207 | debugs(22, 3, "FRESH: age " << age << " <= stale_age " << stale_age); |
62e76326 | 208 | return -1; |
209 | } | |
65fa5c61 | 210 | } |
62e76326 | 211 | |
b2f01ec3 AJ |
212 | /** \par |
213 | * Finally, if all else fails; staleness is determined by the refresh_pattern | |
65fa5c61 | 214 | * configured minimum age. |
215 | */ | |
9e4b5932 | 216 | if (age < R->min) { |
217 | debugs(22, 3, "FRESH: age " << age << " < min " << R->min); | |
1f848b2c | 218 | sf->min = true; |
62e76326 | 219 | return -1; |
65fa5c61 | 220 | } |
62e76326 | 221 | |
9e4b5932 | 222 | debugs(22, 3, "STALE: age " << age << " >= min " << R->min); |
65fa5c61 | 223 | return (age - R->min); |
224 | } | |
225 | ||
b2f01ec3 AJ |
226 | /** |
227 | * \retval 1 if the entry must be revalidated within delta seconds | |
228 | * \retval 0 otherwise | |
829a9357 | 229 | * |
230 | * note: request maybe null (e.g. for cache digests build) | |
e4e6a8db | 231 | */ |
829a9357 | 232 | static int |
190154cf | 233 | refreshCheck(const StoreEntry * entry, HttpRequest * request, time_t delta) |
e4e6a8db | 234 | { |
8d9a8184 | 235 | const RefreshPattern *R; |
829a9357 | 236 | const char *uri = NULL; |
65fa5c61 | 237 | time_t age = 0; |
a207429f | 238 | time_t check_time = squid_curtime + delta; |
65fa5c61 | 239 | int staleness; |
240 | stale_flags sf; | |
62e76326 | 241 | |
9b5d1d21 | 242 | if (entry->mem_obj) |
62e76326 | 243 | uri = entry->mem_obj->url; |
7d47d8e6 | 244 | else if (request) |
62e76326 | 245 | uri = urlCanonical(request); |
7d47d8e6 | 246 | |
bf8fe701 | 247 | debugs(22, 3, "refreshCheck: '" << (uri ? uri : "<none>") << "'"); |
65fa5c61 | 248 | |
249 | if (check_time > entry->timestamp) | |
62e76326 | 250 | age = check_time - entry->timestamp; |
251 | ||
efd62b86 | 252 | // FIXME: what to do when age < 0 or counter overflow? |
b2f01ec3 | 253 | assert(age >= 0); |
efd62b86 | 254 | |
65fa5c61 | 255 | R = uri ? refreshLimits(uri) : refreshUncompiledPattern("."); |
62e76326 | 256 | |
65fa5c61 | 257 | if (NULL == R) |
62e76326 | 258 | R = &DefaultRefresh; |
259 | ||
bf8fe701 | 260 | debugs(22, 3, "refreshCheck: Matched '" << R->pattern << " " << |
261 | (int) R->min << " " << (int) (100.0 * R->pct) << "%% " << | |
262 | (int) R->max << "'"); | |
65fa5c61 | 263 | |
64f8c2cb | 264 | debugs(22, 3, "\tage:\t" << age); |
62e76326 | 265 | |
bf8fe701 | 266 | debugs(22, 3, "\tcheck_time:\t" << mkrfc1123(check_time)); |
62e76326 | 267 | |
bf8fe701 | 268 | debugs(22, 3, "\tentry->timestamp:\t" << mkrfc1123(entry->timestamp)); |
65fa5c61 | 269 | |
45e5102d | 270 | if (request && !request->flags.ignore_cc) { |
64f8c2cb | 271 | const HttpHdrCc *const cc = request->cache_control; |
d74ad83f | 272 | if (cc && cc->hasMinFresh()) { |
4ce6e3b5 | 273 | const int32_t minFresh=cc->minFresh(); |
64f8c2cb | 274 | debugs(22, 3, "\tage + min-fresh:\t" << age << " + " << |
77da1817 | 275 | minFresh << " = " << age + minFresh); |
64f8c2cb | 276 | debugs(22, 3, "\tcheck_time + min-fresh:\t" << check_time << " + " |
422acb7f FC |
277 | << minFresh << " = " << |
278 | mkrfc1123(check_time + minFresh)); | |
279 | age += minFresh; | |
280 | check_time += minFresh; | |
64f8c2cb AR |
281 | } |
282 | } | |
283 | ||
284 | memset(&sf, '\0', sizeof(sf)); | |
285 | ||
286 | staleness = refreshStaleness(entry, check_time, age, R, &sf); | |
287 | ||
288 | debugs(22, 3, "Staleness = " << staleness); | |
289 | ||
65fd3895 AJ |
290 | // stale-if-error requires any failure be passed thru when its period is over. |
291 | if (request && entry->mem_obj && entry->mem_obj->getReply() && entry->mem_obj->getReply()->cache_control && | |
77da1817 | 292 | entry->mem_obj->getReply()->cache_control->hasStaleIfError() && |
4ce6e3b5 | 293 | entry->mem_obj->getReply()->cache_control->staleIfError() < staleness) { |
65fd3895 AJ |
294 | |
295 | debugs(22, 3, "refreshCheck: stale-if-error period expired."); | |
45e5102d | 296 | request->flags.fail_on_validation_err = 1; |
65fd3895 AJ |
297 | } |
298 | ||
4ca08219 | 299 | if (EBIT_TEST(entry->flags, ENTRY_REVALIDATE) && staleness > -1 |
626096be | 300 | #if USE_HTTP_VIOLATIONS |
04f7fd38 | 301 | && !R->flags.ignore_must_revalidate |
4ca08219 | 302 | #endif |
04f7fd38 | 303 | ) { |
bf8fe701 | 304 | debugs(22, 3, "refreshCheck: YES: Must revalidate stale response"); |
5b1d04af | 305 | if (request) |
45e5102d | 306 | request->flags.fail_on_validation_err = 1; |
62e76326 | 307 | return STALE_MUST_REVALIDATE; |
65fa5c61 | 308 | } |
62e76326 | 309 | |
829a9357 | 310 | /* request-specific checks */ |
45e5102d | 311 | if (request && !request->flags.ignore_cc) { |
62e76326 | 312 | HttpHdrCc *cc = request->cache_control; |
4c3ef9b2 | 313 | |
45e5102d | 314 | if (request->flags.ims && (R->flags.refresh_ims || Config.onoff.refresh_all_ims)) { |
4c3ef9b2 | 315 | /* The clients no-cache header is changed into a IMS query */ |
bf8fe701 | 316 | debugs(22, 3, "refreshCheck: YES: refresh-ims"); |
4c3ef9b2 | 317 | return STALE_FORCED_RELOAD; |
318 | } | |
319 | ||
626096be | 320 | #if USE_HTTP_VIOLATIONS |
62e76326 | 321 | |
45e5102d | 322 | if (!request->flags.nocache_hack) { |
62e76326 | 323 | (void) 0; |
324 | } else if (R->flags.ignore_reload) { | |
325 | /* The clients no-cache header is ignored */ | |
bf8fe701 | 326 | debugs(22, 3, "refreshCheck: MAYBE: ignore-reload"); |
62e76326 | 327 | } else if (R->flags.reload_into_ims || Config.onoff.reload_into_ims) { |
328 | /* The clients no-cache header is changed into a IMS query */ | |
bf8fe701 | 329 | debugs(22, 3, "refreshCheck: YES: reload-into-ims"); |
62e76326 | 330 | return STALE_RELOAD_INTO_IMS; |
331 | } else { | |
332 | /* The clients no-cache header is not overridden on this request */ | |
bf8fe701 | 333 | debugs(22, 3, "refreshCheck: YES: client reload"); |
45e5102d | 334 | request->flags.nocache = 1; |
62e76326 | 335 | return STALE_FORCED_RELOAD; |
336 | } | |
337 | ||
9f60cfdf | 338 | #endif |
62e76326 | 339 | if (NULL != cc) { |
d74ad83f | 340 | if (cc->hasMaxAge()) { |
626096be | 341 | #if USE_HTTP_VIOLATIONS |
cf7c2e94 | 342 | if (R->flags.ignore_reload && cc->maxAge() == 0) { |
7792a5ae AR |
343 | debugs(22, 3, "refreshCheck: MAYBE: client-max-age = 0 and ignore-reload"); |
344 | } else | |
528b2c61 | 345 | #endif |
62e76326 | 346 | { |
cf7c2e94 | 347 | if (cc->maxAge() == 0) { |
bf8fe701 | 348 | debugs(22, 3, "refreshCheck: YES: client-max-age = 0"); |
62e76326 | 349 | return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE; |
350 | } | |
351 | ||
cf7c2e94 | 352 | if (age > cc->maxAge()) { |
bf8fe701 | 353 | debugs(22, 3, "refreshCheck: YES: age > client-max-age"); |
62e76326 | 354 | return STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE; |
355 | } | |
356 | } | |
357 | } | |
358 | ||
d74ad83f FC |
359 | if (cc->hasMaxStale() && staleness > -1) { |
360 | if (cc->maxStale()==HttpHdrCc::MAX_STALE_ANY) { | |
62e76326 | 361 | /* max-stale directive without a value */ |
bf8fe701 | 362 | debugs(22, 3, "refreshCheck: NO: max-stale wildcard"); |
62e76326 | 363 | return FRESH_REQUEST_MAX_STALE_ALL; |
4ce6e3b5 | 364 | } else if (staleness < cc->maxStale()) { |
bf8fe701 | 365 | debugs(22, 3, "refreshCheck: NO: staleness < max-stale"); |
62e76326 | 366 | return FRESH_REQUEST_MAX_STALE_VALUE; |
367 | } | |
368 | } | |
369 | } | |
48f44632 | 370 | } |
62e76326 | 371 | |
65fa5c61 | 372 | if (-1 == staleness) { |
bf8fe701 | 373 | debugs(22, 3, "refreshCheck: object isn't stale.."); |
ed1de692 | 374 | if (sf.expires) { |
bf8fe701 | 375 | debugs(22, 3, "refreshCheck: returning FRESH_EXPIRES"); |
62e76326 | 376 | return FRESH_EXPIRES; |
26ac0430 | 377 | } |
62e76326 | 378 | |
379 | assert(!sf.max); | |
380 | ||
ed1de692 | 381 | if (sf.lmfactor) { |
bf8fe701 | 382 | debugs(22, 3, "refreshCheck: returning FRESH_LMFACTOR_RULE"); |
62e76326 | 383 | return FRESH_LMFACTOR_RULE; |
26ac0430 | 384 | } |
62e76326 | 385 | |
386 | assert(sf.min); | |
387 | ||
bf8fe701 | 388 | debugs(22, 3, "refreshCheck: returning FRESH_MIN_RULE"); |
62e76326 | 389 | return FRESH_MIN_RULE; |
1dfa1d81 | 390 | } |
62e76326 | 391 | |
65fa5c61 | 392 | /* |
393 | * At this point the response is stale, unless one of | |
542c4d60 | 394 | * the override options kicks in. |
570d3f75 | 395 | * NOTE: max-stale config blocks the overrides. |
65fa5c61 | 396 | */ |
570d3f75 | 397 | int max_stale = (R->max_stale >= 0 ? R->max_stale : Config.maxStale); |
60c3d5b7 | 398 | if ( max_stale >= 0 && staleness > max_stale) { |
570d3f75 AJ |
399 | debugs(22, 3, "refreshCheck: YES: max-stale limit"); |
400 | if (request) | |
45e5102d | 401 | request->flags.fail_on_validation_err = 1; |
570d3f75 AJ |
402 | return STALE_MAX_STALE; |
403 | } | |
404 | ||
65fa5c61 | 405 | if (sf.expires) { |
626096be | 406 | #if USE_HTTP_VIOLATIONS |
62e76326 | 407 | |
408 | if (R->flags.override_expire && age < R->min) { | |
bf8fe701 | 409 | debugs(22, 3, "refreshCheck: NO: age < min && override-expire"); |
62e76326 | 410 | return FRESH_OVERRIDE_EXPIRES; |
411 | } | |
412 | ||
65fa5c61 | 413 | #endif |
62e76326 | 414 | return STALE_EXPIRES; |
e4e6a8db | 415 | } |
62e76326 | 416 | |
65fa5c61 | 417 | if (sf.max) |
62e76326 | 418 | return STALE_MAX_RULE; |
419 | ||
65fa5c61 | 420 | if (sf.lmfactor) { |
626096be | 421 | #if USE_HTTP_VIOLATIONS |
62e76326 | 422 | |
423 | if (R->flags.override_lastmod && age < R->min) { | |
bf8fe701 | 424 | debugs(22, 3, "refreshCheck: NO: age < min && override-lastmod"); |
62e76326 | 425 | return FRESH_OVERRIDE_LASTMOD; |
426 | } | |
427 | ||
65fa5c61 | 428 | #endif |
62e76326 | 429 | return STALE_LMFACTOR_RULE; |
e4e6a8db | 430 | } |
62e76326 | 431 | |
bf8fe701 | 432 | debugs(22, 3, "refreshCheck: returning STALE_DEFAULT"); |
65fa5c61 | 433 | return STALE_DEFAULT; |
e4e6a8db | 434 | } |
48f44632 | 435 | |
cfa9f1cb | 436 | int |
437 | refreshIsCachable(const StoreEntry * entry) | |
438 | { | |
439 | /* | |
440 | * Don't look at the request to avoid no-cache and other nuisances. | |
441 | * the object should have a mem_obj so the URL will be found there. | |
26ac0430 AJ |
442 | * minimum_expiry_time seconds delta (defaults to 60 seconds), to |
443 | * avoid objects which expire almost immediately, and which can't | |
6a2f3fcf | 444 | * be refreshed. |
cfa9f1cb | 445 | */ |
6a2f3fcf | 446 | int reason = refreshCheck(entry, NULL, Config.minimum_expiry_time); |
5db6bf73 FC |
447 | ++ refreshCounts[rcStore].total; |
448 | ++ refreshCounts[rcStore].status[reason]; | |
62e76326 | 449 | |
451c8350 | 450 | if (reason < STALE_MUST_REVALIDATE) |
62e76326 | 451 | /* Does not need refresh. This is certainly cachable */ |
452 | return 1; | |
453 | ||
cfa9f1cb | 454 | if (entry->lastmod < 0) |
62e76326 | 455 | /* Last modified is needed to do a refresh */ |
456 | return 0; | |
457 | ||
cfa9f1cb | 458 | if (entry->mem_obj == NULL) |
62e76326 | 459 | /* no mem_obj? */ |
460 | return 1; | |
461 | ||
528b2c61 | 462 | if (entry->getReply() == NULL) |
62e76326 | 463 | /* no reply? */ |
464 | return 1; | |
465 | ||
528b2c61 | 466 | if (entry->getReply()->content_length == 0) |
62e76326 | 467 | /* No use refreshing (caching?) 0 byte objects */ |
468 | return 0; | |
469 | ||
cfa9f1cb | 470 | /* This seems to be refreshable. Cache it */ |
471 | return 1; | |
472 | } | |
473 | ||
bcfba8bd AR |
474 | /// whether reply is stale if it is a hit |
475 | static bool | |
476 | refreshIsStaleIfHit(const int reason) | |
477 | { | |
478 | switch (reason) { | |
479 | case FRESH_MIN_RULE: | |
480 | case FRESH_LMFACTOR_RULE: | |
481 | case FRESH_EXPIRES: | |
482 | return false; | |
483 | default: | |
484 | return true; | |
485 | } | |
486 | } | |
487 | ||
829a9357 | 488 | /* refreshCheck... functions below are protocol-specific wrappers around |
489 | * refreshCheck() function above */ | |
490 | ||
491 | int | |
190154cf | 492 | refreshCheckHTTP(const StoreEntry * entry, HttpRequest * request) |
7d47d8e6 | 493 | { |
65fa5c61 | 494 | int reason = refreshCheck(entry, request, 0); |
5db6bf73 FC |
495 | ++ refreshCounts[rcHTTP].total; |
496 | ++ refreshCounts[rcHTTP].status[reason]; | |
45e5102d | 497 | request->flags.stale_if_hit = refreshIsStaleIfHit(reason); |
bcfba8bd | 498 | return (Config.onoff.offline || reason < 200) ? 0 : 1; |
829a9357 | 499 | } |
500 | ||
501 | int | |
190154cf | 502 | refreshCheckICP(const StoreEntry * entry, HttpRequest * request) |
7d47d8e6 | 503 | { |
65fa5c61 | 504 | int reason = refreshCheck(entry, request, 30); |
5db6bf73 FC |
505 | ++ refreshCounts[rcICP].total; |
506 | ++ refreshCounts[rcICP].status[reason]; | |
65fa5c61 | 507 | return (reason < 200) ? 0 : 1; |
829a9357 | 508 | } |
509 | ||
65fa5c61 | 510 | #if USE_HTCP |
32b3cf93 | 511 | int |
190154cf | 512 | refreshCheckHTCP(const StoreEntry * entry, HttpRequest * request) |
32b3cf93 | 513 | { |
65fa5c61 | 514 | int reason = refreshCheck(entry, request, 10); |
5db6bf73 FC |
515 | ++ refreshCounts[rcHTCP].total; |
516 | ++ refreshCounts[rcHTCP].status[reason]; | |
65fa5c61 | 517 | return (reason < 200) ? 0 : 1; |
32b3cf93 | 518 | } |
62e76326 | 519 | |
65fa5c61 | 520 | #endif |
32b3cf93 | 521 | |
65fa5c61 | 522 | #if USE_CACHE_DIGESTS |
829a9357 | 523 | int |
7d47d8e6 | 524 | refreshCheckDigest(const StoreEntry * entry, time_t delta) |
525 | { | |
65fa5c61 | 526 | int reason = refreshCheck(entry, |
62e76326 | 527 | entry->mem_obj ? entry->mem_obj->request : NULL, |
528 | delta); | |
5db6bf73 FC |
529 | ++ refreshCounts[rcCDigest].total; |
530 | ++ refreshCounts[rcCDigest].status[reason]; | |
65fa5c61 | 531 | return (reason < 200) ? 0 : 1; |
6018f0de | 532 | } |
62e76326 | 533 | |
65fa5c61 | 534 | #endif |
6018f0de | 535 | |
48f44632 | 536 | time_t |
537 | getMaxAge(const char *url) | |
538 | { | |
8d9a8184 | 539 | const RefreshPattern *R; |
bf8fe701 | 540 | debugs(22, 3, "getMaxAge: '" << url << "'"); |
62e76326 | 541 | |
6018f0de | 542 | if ((R = refreshLimits(url))) |
62e76326 | 543 | return R->max; |
6018f0de | 544 | else |
62e76326 | 545 | return REFRESH_DEFAULT_MAX; |
48f44632 | 546 | } |
1c3e77cd | 547 | |
829a9357 | 548 | static void |
62e76326 | 549 | |
829a9357 | 550 | refreshCountsStats(StoreEntry * sentry, struct RefreshCounts *rc) |
551 | { | |
cc7cfa8e | 552 | int sum = 0; |
553 | int tot = rc->total; | |
554 | ||
829a9357 | 555 | storeAppendPrintf(sentry, "\n\n%s histogram:\n", rc->proto); |
65fa5c61 | 556 | storeAppendPrintf(sentry, "Count\t%%Total\tCategory\n"); |
829a9357 | 557 | |
65fa5c61 | 558 | #define refreshCountsStatsEntry(code,desc) { \ |
559 | storeAppendPrintf(sentry, "%6d\t%6.2f\t%s\n", \ | |
560 | rc->status[code], xpercent(rc->status[code], tot), desc); \ | |
561 | sum += rc->status[code]; \ | |
cc7cfa8e | 562 | } |
65fa5c61 | 563 | |
564 | refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_ALL, | |
62e76326 | 565 | "Fresh: request max-stale wildcard"); |
65fa5c61 | 566 | refreshCountsStatsEntry(FRESH_REQUEST_MAX_STALE_VALUE, |
62e76326 | 567 | "Fresh: request max-stale value"); |
65fa5c61 | 568 | refreshCountsStatsEntry(FRESH_EXPIRES, |
62e76326 | 569 | "Fresh: expires time not reached"); |
65fa5c61 | 570 | refreshCountsStatsEntry(FRESH_LMFACTOR_RULE, |
62e76326 | 571 | "Fresh: refresh_pattern last-mod factor percentage"); |
65fa5c61 | 572 | refreshCountsStatsEntry(FRESH_MIN_RULE, |
62e76326 | 573 | "Fresh: refresh_pattern min value"); |
65fa5c61 | 574 | refreshCountsStatsEntry(FRESH_OVERRIDE_EXPIRES, |
62e76326 | 575 | "Fresh: refresh_pattern override expires"); |
65fa5c61 | 576 | refreshCountsStatsEntry(FRESH_OVERRIDE_LASTMOD, |
62e76326 | 577 | "Fresh: refresh_pattern override lastmod"); |
65fa5c61 | 578 | refreshCountsStatsEntry(STALE_MUST_REVALIDATE, |
62e76326 | 579 | "Stale: response has must-revalidate"); |
65fa5c61 | 580 | refreshCountsStatsEntry(STALE_RELOAD_INTO_IMS, |
62e76326 | 581 | "Stale: changed reload into IMS"); |
65fa5c61 | 582 | refreshCountsStatsEntry(STALE_FORCED_RELOAD, |
62e76326 | 583 | "Stale: request has no-cache directive"); |
65fa5c61 | 584 | refreshCountsStatsEntry(STALE_EXCEEDS_REQUEST_MAX_AGE_VALUE, |
62e76326 | 585 | "Stale: age exceeds request max-age value"); |
65fa5c61 | 586 | refreshCountsStatsEntry(STALE_EXPIRES, |
62e76326 | 587 | "Stale: expires time reached"); |
65fa5c61 | 588 | refreshCountsStatsEntry(STALE_MAX_RULE, |
62e76326 | 589 | "Stale: refresh_pattern max age rule"); |
65fa5c61 | 590 | refreshCountsStatsEntry(STALE_LMFACTOR_RULE, |
62e76326 | 591 | "Stale: refresh_pattern last-mod factor percentage"); |
65fa5c61 | 592 | refreshCountsStatsEntry(STALE_DEFAULT, |
62e76326 | 593 | "Stale: by default"); |
65fa5c61 | 594 | |
7d47d8e6 | 595 | tot = sum; /* paranoid: "total" line shows 100% if we forgot nothing */ |
65fa5c61 | 596 | storeAppendPrintf(sentry, "%6d\t%6.2f\tTOTAL\n", |
62e76326 | 597 | rc->total, xpercent(rc->total, tot)); |
65fa5c61 | 598 | \ |
62e76326 | 599 | storeAppendPrintf(sentry, "\n"); |
829a9357 | 600 | } |
601 | ||
1c3e77cd | 602 | static void |
603 | refreshStats(StoreEntry * sentry) | |
604 | { | |
829a9357 | 605 | int i; |
606 | int total = 0; | |
607 | ||
608 | /* get total usage count */ | |
62e76326 | 609 | |
829a9357 | 610 | for (i = 0; i < rcCount; ++i) |
62e76326 | 611 | total += refreshCounts[i].total; |
829a9357 | 612 | |
613 | /* protocol usage histogram */ | |
614 | storeAppendPrintf(sentry, "\nRefreshCheck calls per protocol\n\n"); | |
62e76326 | 615 | |
829a9357 | 616 | storeAppendPrintf(sentry, "Protocol\t#Calls\t%%Calls\n"); |
62e76326 | 617 | |
829a9357 | 618 | for (i = 0; i < rcCount; ++i) |
62e76326 | 619 | storeAppendPrintf(sentry, "%10s\t%6d\t%6.2f\n", |
620 | refreshCounts[i].proto, | |
621 | refreshCounts[i].total, | |
622 | xpercent(refreshCounts[i].total, total)); | |
829a9357 | 623 | |
624 | /* per protocol histograms */ | |
625 | storeAppendPrintf(sentry, "\n\nRefreshCheck histograms for various protocols\n"); | |
62e76326 | 626 | |
829a9357 | 627 | for (i = 0; i < rcCount; ++i) |
62e76326 | 628 | refreshCountsStats(sentry, &refreshCounts[i]); |
1c3e77cd | 629 | } |
630 | ||
5f5e883f FC |
631 | static void |
632 | refreshRegisterWithCacheManager(void) | |
633 | { | |
8822ebee | 634 | Mgr::RegisterAction("refresh", "Refresh Algorithm Statistics", refreshStats, 0, 1); |
5f5e883f FC |
635 | } |
636 | ||
1c3e77cd | 637 | void |
9bc73deb | 638 | refreshInit(void) |
1c3e77cd | 639 | { |
829a9357 | 640 | memset(refreshCounts, 0, sizeof(refreshCounts)); |
641 | refreshCounts[rcHTTP].proto = "HTTP"; | |
642 | refreshCounts[rcICP].proto = "ICP"; | |
65fa5c61 | 643 | #if USE_HTCP |
62e76326 | 644 | |
32b3cf93 | 645 | refreshCounts[rcHTCP].proto = "HTCP"; |
65fa5c61 | 646 | #endif |
62e76326 | 647 | |
cfa9f1cb | 648 | refreshCounts[rcStore].proto = "On Store"; |
65fa5c61 | 649 | #if USE_CACHE_DIGESTS |
62e76326 | 650 | |
829a9357 | 651 | refreshCounts[rcCDigest].proto = "Cache Digests"; |
65fa5c61 | 652 | #endif |
62e76326 | 653 | |
65fa5c61 | 654 | memset(&DefaultRefresh, '\0', sizeof(DefaultRefresh)); |
655 | DefaultRefresh.pattern = "<none>"; | |
656 | DefaultRefresh.min = REFRESH_DEFAULT_MIN; | |
657 | DefaultRefresh.pct = REFRESH_DEFAULT_PCT; | |
658 | DefaultRefresh.max = REFRESH_DEFAULT_MAX; | |
d120ed12 FC |
659 | |
660 | refreshRegisterWithCacheManager(); | |
1c3e77cd | 661 | } |