Commit cb1e1507 authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Don my peril sensitive sun-glasses, and go over the TTL calculation

routine.

Follow RFC2616 more closely, and make relative (ie: max-age) specifications
take precedence over absolute specifications (Expires:)

Add an explicit clock-skew window (parameter: clock_skew) and only
interpret Expires: in absolute terms if the Date: header is inside
this window.

Explicit check for the case where the backend sends an Expires: which
is before the Date:, even if the Date: is out side our window.




git-svn-id: http://www.varnish-cache.org/svn/trunk/varnish-cache@3019 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent 1fc6b15c
...@@ -175,6 +175,9 @@ struct params { ...@@ -175,6 +175,9 @@ struct params {
/* Prefer IPv6 connections to backend*/ /* Prefer IPv6 connections to backend*/
unsigned prefer_ipv6; unsigned prefer_ipv6;
/* Acceptable clockskew with backends */
unsigned clock_skew;
}; };
extern volatile struct params *params; extern volatile struct params *params;
......
...@@ -745,6 +745,11 @@ static const struct parspec parspec[] = { ...@@ -745,6 +745,11 @@ static const struct parspec parspec[] = {
"VCL can override this default value for each backend.", "VCL can override this default value for each backend.",
0, 0,
"400", "ms" }, "400", "ms" },
{ "clock_skew", tweak_uint, &master.clock_skew, 0, UINT_MAX,
"How much clockskew we are willing to accept between the "
"backend and our own clock.",
0,
"10", "s" },
{ "prefer_ipv6", tweak_bool, &master.prefer_ipv6, 0, 0, { "prefer_ipv6", tweak_bool, &master.prefer_ipv6, 0, 0,
"Prefer IPv6 address when connecting to backends which " "Prefer IPv6 address when connecting to backends which "
"have both IPv4 and IPv6 addresses.", "have both IPv4 and IPv6 addresses.",
......
...@@ -60,106 +60,101 @@ ...@@ -60,106 +60,101 @@
* choice. * choice.
* *
* Varnish implements a policy which is RFC2616 compliant when there * Varnish implements a policy which is RFC2616 compliant when there
* is no clockskew, and falls back to a new "clockless cache" mode otherwise. * is no clockskew, and falls as gracefully as possible otherwise.
* Our "clockless cache" model is syntehsized from the bits of RFC2616 * Our "clockless cache" model is syntehsized from the bits of RFC2616
* that talks about how a cache should react to a clockless origin server, * that talks about how a cache should react to a clockless origin server,
* and more or uses the inverse logic for the opposite relationship. * and more or less uses the inverse logic for the opposite relationship.
* *
*/ */
#if PSEUDO_CODE static double
/* Marker for no retirement age determined */ RFC2616_Ttl(const struct sess *sp, const struct http *hp, struct object *obj)
retirement_age = INT_MAX {
int ttl;
/* If we have a max-age directive, respect it */ unsigned max_age, age;
if (max-age) double h_date, h_expires, ttd;
retirement_age = max(0,min(retirement_age, max-age - Age:)) char *p;
/* If Date: is not in future and Expires: looks sensible, use it */ /* If all else fails, cache using default ttl */
if ((!date || date < our_clock) && expires > our_clock) { ttl = params->default_ttl;
ttd = min(our_clock + retirement_age, Expires:)
/* Otherwise we have clock-skew */ max_age = age = 0;
} else { ttd = 0;
/* If we have both date and expires, infer max-age */ h_expires = 0;
if (date && expires) h_date = 0;
retirement_age =
max(0, min(retirement_age, Expires: - Date:)
/* Apply default_ttl if nothing better found */ do { /* Allows us to break when we want out */
if (retirement_age == INT_MAX)
retirement_age = default_ttl
/* Apply the max-age we can up with */ /*
ttd = our_clock + retirement_age * First find any relative specification from the backend
} * These take precedence according to RFC2616, 13.2.4
*/
/* Apply hard limits */ if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) ||
ttd = max(ttd, our_clock + hard_lower_ttl) http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) &&
ttd = min(ttd, our_clock + hard_upper_ttl) p != NULL) {
#endif
static double max_age = strtoul(p, NULL, 0);
RFC2616_Ttl(const struct sess *sp, const struct http *hp, struct object *obj) if (http_GetHdr(hp, H_Age, &p)) {
{ age = strtoul(p, NULL, 0);
int retirement_age; obj->age = age;
unsigned u1, u2; }
double h_date, h_expires, ttd;
char *p;
retirement_age = INT_MAX; if (age > max_age)
ttl = 0;
u1 = u2 = 0; else
if ((http_GetHdrField(hp, H_Cache_Control, "s-maxage", &p) || ttl = max_age - age;
http_GetHdrField(hp, H_Cache_Control, "max-age", &p)) && break;
p != NULL) {
u1 = strtoul(p, NULL, 0);
u2 = 0;
if (http_GetHdr(hp, H_Age, &p)) {
u2 = strtoul(p, NULL, 0);
obj->age = u2;
} }
if (u2 <= u1)
retirement_age = u1 - u2;
}
/* /* Next look for absolute specifications from backend */
* XXX: if the backends time is too skewed relative to our own
* XXX: we should blacklist the backend, to avoid getting totally
* XXX: bogus results further down. Exactly what "too skewed" means
* XXX: in this context is a good question. It could be determined
* XXX: out according to the backends headers, but a simple fixed
* XXX: tolerance of a minute either way would be more predictable.
*/
h_date = 0;
if (http_GetHdr(hp, H_Date, &p))
h_date = TIM_parse(p);
h_expires = 0; if (http_GetHdr(hp, H_Expires, &p))
if (http_GetHdr(hp, H_Expires, &p)) h_expires = TIM_parse(p);
h_expires = TIM_parse(p); if (h_expires == 0)
break;
if (h_date < obj->entered && h_expires > obj->entered) {
ttd = h_expires; if (http_GetHdr(hp, H_Date, &p))
if (retirement_age != INT_MAX && h_date = TIM_parse(p);
obj->entered + retirement_age < ttd)
ttd = obj->entered + retirement_age; /* If backend told us it is expired already, don't cache. */
} else { if (h_expires < h_date) {
if (h_date != 0 && h_expires != 0) { ttl = 0;
if (h_date < h_expires && break;
h_expires - h_date < retirement_age)
retirement_age = h_expires - h_date;
} }
if (retirement_age == INT_MAX)
retirement_age = params->default_ttl;
ttd = obj->entered + retirement_age; if (h_date == 0 ||
} (h_date < obj->entered + params->clock_skew &&
h_date + params->clock_skew > obj->entered)) {
/*
* If we have no Date: header or if it is
* sufficiently close to our clock we will
* trust Expires: relative to our own clock.
*/
if (h_expires < obj->entered)
ttl = 0;
else
ttd = h_expires;
break;
}
/*
* But even if the clocks are out of whack we can still
* derive a relative time from the two headers.
* (the negative ttl case is caught above)
*/
ttl = (h_expires - h_date);
} while (0);
if (ttl > 0 && ttd == 0)
ttd = obj->entered + ttl;
/* calculated TTL, Our time, Date, Expires, max-age, age */ /* calculated TTL, Our time, Date, Expires, max-age, age */
WSP(sp, SLT_TTL, "%u RFC %d %d %d %d %d %d", sp->xid, WSP(sp, SLT_TTL, "%u RFC %d %d %d %d %u %u", sp->xid,
(int)(ttd - obj->entered), (int)obj->entered, (int)h_date, ttd ? (int)(ttd - obj->entered) : 0,
(int)h_expires, (int)u1, (int)u2); (int)obj->entered, (int)h_date,
(int)h_expires, max_age, age);
return (ttd); return (ttd);
} }
...@@ -194,9 +189,8 @@ RFC2616_cache_policy(const struct sess *sp, const struct http *hp) ...@@ -194,9 +189,8 @@ RFC2616_cache_policy(const struct sess *sp, const struct http *hp)
} }
sp->obj->ttl = RFC2616_Ttl(sp, hp, sp->obj); sp->obj->ttl = RFC2616_Ttl(sp, hp, sp->obj);
if (sp->obj->ttl == 0) { if (sp->obj->ttl == 0)
sp->obj->cacheable = 0; sp->obj->cacheable = 0;
}
return (body); return (body);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment