public static CacheFreshnessStatus ComputeFreshness(HttpRequestCacheValidator ctx) {
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_now_time, DateTime.UtcNow.ToString("r", CultureInfo.InvariantCulture)));
/*
apparent_age = max(0, response_time - date_value);
*/
DateTime nowDate = DateTime.UtcNow;
TimeSpan age = TimeSpan.MaxValue;
DateTime date = ctx.CacheDate;
if (date != DateTime.MinValue) {
age = (nowDate - date);
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_age1_date_header, ((int)age.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo), ctx.CacheDate.ToString("r", CultureInfo.InvariantCulture)));
}
else if (ctx.CacheEntry.LastSynchronizedUtc != DateTime.MinValue) {
/*
Another way to compute cache age but only if Date header is absent.
*/
age = nowDate - ctx.CacheEntry.LastSynchronizedUtc;
if (ctx.CacheAge != TimeSpan.MinValue) {
age += ctx.CacheAge;
}
if(Logging.On) {
if (ctx.CacheAge != TimeSpan.MinValue)
Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_age1_last_synchronized_age_header, ((int)age.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo), ctx.CacheEntry.LastSynchronizedUtc.ToString("r", CultureInfo.InvariantCulture), ((int)ctx.CacheAge.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)));
else
Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_age1_last_synchronized, ((int)age.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo), ctx.CacheEntry.LastSynchronizedUtc.ToString("r", CultureInfo.InvariantCulture)));
}
}
/*
corrected_received_age = max(apparent_age, age_value);
*/
if (ctx.CacheAge != TimeSpan.MinValue) {
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_age2, ((int)ctx.CacheAge.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)));
if (ctx.CacheAge > age || age == TimeSpan.MaxValue) {
age = ctx.CacheAge;
}
}
// Updating CacheAge ...
// Note we don't account on response "transit" delay
// Also undefined cache entry Age is reported as TimeSpan.MaxValue (which is impossble to get from HTTP)
// Also a negative age is reset to 0 as per RFC
ctx.CacheAge = (age < TimeSpan.Zero? TimeSpan.Zero: age);
// Now we start checking the server specified requirements
/*
The calculation to determine if a response has expired is quite simple:
response_is_fresh = (freshness_lifetime > current_age)
*/
// If we managed to compute the Cache Age
if (ctx.CacheAge != TimeSpan.MinValue) {
/*
s-maxage
If a response includes an s-maxage directive, then for a shared
cache (but not for a private cache), the maximum age specified by
this directive overrides the maximum age specified by either the
max-age directive or the Expires header.
*/
if (!ctx.CacheEntry.IsPrivateEntry && ctx.CacheCacheControl.SMaxAge != -1) {
ctx.CacheMaxAge = TimeSpan.FromSeconds(ctx.CacheCacheControl.SMaxAge);
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_max_age_cache_s_max_age, ((int)ctx.CacheMaxAge.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)));
if (ctx.CacheAge < ctx.CacheMaxAge) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}
/*
The max-age directive takes priority over Expires, so if max-age is
present in a response, the calculation is simply:
freshness_lifetime = max_age_value
*/
if (ctx.CacheCacheControl.MaxAge != -1) {
ctx.CacheMaxAge = TimeSpan.FromSeconds(ctx.CacheCacheControl.MaxAge);
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_max_age_cache_max_age, ((int)ctx.CacheMaxAge.TotalSeconds).ToString(NumberFormatInfo.InvariantInfo)));
if (ctx.CacheAge < ctx.CacheMaxAge) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}
}
/*
Otherwise, if Expires is present in the response, the calculation is:
freshness_lifetime = expires_value - date_value
*/
if (date == DateTime.MinValue) {
date = ctx.CacheEntry.LastSynchronizedUtc;
}
DateTime expiresDate = ctx.CacheEntry.ExpiresUtc;
if (ctx.CacheExpires != DateTime.MinValue && ctx.CacheExpires < expiresDate) {
expiresDate = ctx.CacheExpires;
}
// If absolute Expires and Response Date and Cache Age can be recovered
if (expiresDate != DateTime.MinValue && date != DateTime.MinValue && ctx.CacheAge != TimeSpan.MinValue) {
ctx.CacheMaxAge = expiresDate - date;
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_max_age_expires_date, ((int)((expiresDate - date).TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo), expiresDate.ToString("r", CultureInfo.InvariantCulture)));
if (ctx.CacheAge < ctx.CacheMaxAge) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}
// If absolute Expires can be recovered
if (expiresDate != DateTime.MinValue) {
ctx.CacheMaxAge = expiresDate - DateTime.UtcNow;
//Take absolute Expires value
if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_max_age_absolute, expiresDate.ToString("r", CultureInfo.InvariantCulture)));
if (expiresDate < DateTime.UtcNow) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}
/*
If none of Expires, Cache-Control: max-age, or Cache-Control: s-
maxage (see section 14.9.3) appears in the response, and the response
does not include other restrictions on caching, the cache MAY compute
a freshness lifetime using a heuristic. The cache MUST attach Warning
113 to any response whose age is more than 24 hours if such warning
has not already been added.
Also, if the response does have a Last-Modified time, the heuristic
expiration value SHOULD be no more than some fraction of the interval
since that time. A typical setting of this fraction might be 10%.
response_is_fresh = (freshness_lifetime > current_age)
*/
ctx.HeuristicExpiration = true;
DateTime lastModifiedDate = ctx.CacheEntry.LastModifiedUtc;
if (ctx.CacheLastModified > lastModifiedDate) {
lastModifiedDate = ctx.CacheLastModified;
} ctx.CacheMaxAge = ctx.UnspecifiedMaxAge;
if (lastModifiedDate != DateTime.MinValue) {
TimeSpan span = (nowDate - lastModifiedDate);
int maxAgeSeconds = (int)(span.TotalSeconds/10);
ctx.CacheMaxAge = TimeSpan.FromSeconds(maxAgeSeconds);
if(Logging.On)Logging.PrintInfo(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_max_age_use_10_percent, maxAgeSeconds.ToString(NumberFormatInfo.InvariantInfo), lastModifiedDate.ToString("r", CultureInfo.InvariantCulture)));
if (ctx.CacheAge.TotalSeconds < maxAgeSeconds) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}
// Else we can only rely on UnspecifiedMaxAge hint
ctx.CacheMaxAge = ctx.UnspecifiedMaxAge;
if(Logging.On)Logging.PrintWarning(Logging.RequestCache, SR.GetString(SR.net_log_cache_no_max_age_use_default, ((int)(ctx.UnspecifiedMaxAge.TotalSeconds)).ToString(NumberFormatInfo.InvariantInfo)));
if (ctx.CacheMaxAge >= ctx.CacheAge) {
return CacheFreshnessStatus.Fresh;
}
return CacheFreshnessStatus.Stale;
}