///|
/// Whether contacting the origin is currently possible.
pub(all) enum OriginState {
OriginAvailable
OriginUnavailable
} derive(Debug, Eq)
///|
/// Stable terminal reuse decisions.
pub(all) enum ReuseVerdict {
ReuseFresh
Revalidate
ServeStale
FetchFromOrigin
OnlyIfCachedMiss
NotReusable
} derive(Debug, Eq)
///|
pub(all) struct ReuseDecision {
verdict : ReuseVerdict
current_age : Int64
freshness_lifetime : Int64
trace : Array[TraceStep]
diagnostics : Array[Diagnostic]
} derive(Debug, Eq)
///|
/// Decide whether one candidate stored response can satisfy a request.
pub fn evaluate_reuse(
stored : StoredResponse?,
presented : RequestMetadata,
mode : CacheMode,
now : Timestamp,
origin? : OriginState = OriginAvailable,
policy? : CachePolicy = default_policy(),
) -> ReuseDecision {
let request_control = effective_request_control(presented.headers)
let diagnostics = request_control.diagnostics.copy()
guard stored is Some(candidate) else {
return reuse_without_candidate(request_control, diagnostics)
}
let key = evaluate_cache_key(candidate, presented)
append_diagnostics(diagnostics, key.diagnostics)
let trace = key.trace.copy()
if !key.matches {
return reuse_terminal(
if request_control.contains("only-if-cached") {
OnlyIfCachedMiss
} else {
NotReusable
},
"REUSE_CACHE_KEY_MISMATCH",
"candidate does not match the presented cache key",
"RFC 9111 4, 5.2.1.7",
0L,
0L,
trace,
diagnostics,
)
}
let response_control = parse_cache_control(candidate.response.headers)
append_diagnostics(diagnostics, response_control.diagnostics)
let freshness = calculate_freshness(candidate, mode, now, policy~)
append_diagnostics(diagnostics, freshness.diagnostics)
for item in freshness.trace {
trace.push(item)
}
let age = freshness.current_age
let lifetime = constrained_lifetime(
freshness.freshness_lifetime,
request_control,
diagnostics,
)
if request_forces_validation(request_control) ||
response_control.contains("no-cache") {
return network_required(
candidate, request_control, origin, "REUSE_NO_CACHE_REVALIDATION", "request or response requires successful validation before reuse",
"RFC 9111 4, 5.2.1.4, 5.2.2.4", age, lifetime, trace, diagnostics,
)
}
let minimum_fresh = request_delta_or_zero(
request_control, "min-fresh", diagnostics,
)
let fresh_enough = saturated_add(age, minimum_fresh) < lifetime
if fresh_enough {
return reuse_terminal(
ReuseFresh,
"REUSE_FRESH",
"candidate is fresh after applying request constraints",
"RFC 9111 4.2, 5.2.1.3",
age,
lifetime,
trace,
diagnostics,
)
}
let stale_by = if age > lifetime { age - lifetime } else { 0L }
let stale_forbidden = response_control.contains("must-revalidate") ||
(mode == SharedCache && response_control.contains("proxy-revalidate")) ||
request_control.contains("no-cache")
let max_stale = max_stale_allowance(request_control, diagnostics)
let request_allows_stale = match max_stale {
Some(None) => true
Some(Some(limit)) => stale_by <= limit
None => false
}
let disconnected_policy = origin == OriginUnavailable &&
policy.allow_disconnected_stale
if !stale_forbidden && (request_allows_stale || disconnected_policy) {
return reuse_terminal(
ServeStale,
if request_allows_stale {
"REUSE_STALE_REQUEST_ALLOWED"
} else {
"REUSE_STALE_DISCONNECTED_POLICY"
},
"stale candidate is permitted by explicit request or disconnected policy",
"RFC 9111 4.2.4, 5.2.1.2",
age,
lifetime,
trace,
diagnostics,
)
}
network_required(
candidate,
request_control,
origin,
if stale_forbidden {
"REUSE_STALE_FORBIDDEN"
} else {
"REUSE_STALE_REVALIDATE"
},
"stale candidate requires successful validation or a new response",
"RFC 9111 4.2.4, 4.3",
age,
lifetime,
trace,
diagnostics,
)
}
///|
fn reuse_without_candidate(
request_control : CacheControl,
diagnostics : Array[Diagnostic],
) -> ReuseDecision {
let trace : Array[TraceStep] = []
if request_control.contains("only-if-cached") {
reuse_terminal(
OnlyIfCachedMiss,
"REUSE_ONLY_IF_CACHED_MISS",
"only-if-cached forbids contacting the origin and no candidate exists",
"RFC 9111 5.2.1.7",
0L,
0L,
trace,
diagnostics,
)
} else {
reuse_terminal(
FetchFromOrigin,
"REUSE_CACHE_MISS",
"no stored response candidate exists",
"RFC 9111 4",
0L,
0L,
trace,
diagnostics,
)
}
}
///|
fn network_required(
candidate : StoredResponse,
request_control : CacheControl,
origin : OriginState,
code : String,
message : String,
section : String,
age : Int64,
lifetime : Int64,
trace : Array[TraceStep],
diagnostics : Array[Diagnostic],
) -> ReuseDecision {
if request_control.contains("only-if-cached") || origin == OriginUnavailable {
return reuse_terminal(
OnlyIfCachedMiss,
"REUSE_NETWORK_FORBIDDEN",
"validation or retrieval is required but the origin cannot be contacted",
"RFC 9111 5.2.1.7",
age,
lifetime,
trace,
diagnostics,
)
}
reuse_terminal(
if has_validator(candidate.response.headers) {
Revalidate
} else {
FetchFromOrigin
},
code,
message,
section,
age,
lifetime,
trace,
diagnostics,
)
}
///|
fn reuse_terminal(
verdict : ReuseVerdict,
code : String,
message : String,
section : String,
age : Int64,
lifetime : Int64,
trace : Array[TraceStep],
diagnostics : Array[Diagnostic],
) -> ReuseDecision {
trace.push(step(code, message, section))
{
verdict,
current_age: age,
freshness_lifetime: lifetime,
trace,
diagnostics,
}
}
///|
fn effective_request_control(headers : Headers) -> CacheControl {
let parsed = parse_cache_control(headers)
if headers.contains("cache-control") {
return parsed
}
for value in headers.values("pragma") {
if @lex.lower(@lex.trim_ows(value)) == "no-cache" {
let directives = parsed.directives.copy()
directives.push({
name: "no-cache",
value: None,
quoted: false,
raw: "no-cache",
})
let diagnostics = parsed.diagnostics.copy()
diagnostics.push({
level: Info,
code: "REQUEST_PRAGMA_NO_CACHE",
message: "legacy Pragma no-cache was applied because Cache-Control is absent",
field_name: Some("pragma"),
})
return { directives, diagnostics, }
}
}
parsed
}
///|
fn request_forces_validation(control : CacheControl) -> Bool {
if control.contains("no-cache") {
return true
}
match control.delta("max-age") {
DeltaValid(0L) => true
_ => false
}
}
///|
fn constrained_lifetime(
lifetime : Int64,
control : CacheControl,
diagnostics : Array[Diagnostic],
) -> Int64 {
match control.delta("max-age") {
DeltaValid(limit) => if limit < lifetime { limit } else { lifetime }
DeltaInvalid | DeltaRepeated => {
diagnostics.push({
level: Warning,
code: "REQUEST_MAX_AGE_INVALID",
message: "invalid request max-age was ignored",
field_name: Some("cache-control"),
})
lifetime
}
DeltaMissing => lifetime
}
}
///|
fn request_delta_or_zero(
control : CacheControl,
name : String,
diagnostics : Array[Diagnostic],
) -> Int64 {
match control.delta(name) {
DeltaValid(value) => value
DeltaInvalid | DeltaRepeated => {
diagnostics.push({
level: Warning,
code: "REQUEST_DELTA_INVALID",
message: "invalid request delta directive was ignored: " + name,
field_name: Some("cache-control"),
})
0L
}
DeltaMissing => 0L
}
}
///|
/// Outer Some means max-stale is present; inner None means unlimited.
fn max_stale_allowance(
control : CacheControl,
diagnostics : Array[Diagnostic],
) -> Int64?? {
let matches = control.occurrences("max-stale")
if matches.length() == 0 {
return None
}
if matches.length() > 1 {
diagnostics.push({
level: Warning,
code: "REQUEST_MAX_STALE_REPEATED",
message: "repeated max-stale directive was ignored",
field_name: Some("cache-control"),
})
return None
}
match matches[0].value {
None => Some(None)
Some(value) =>
match @lex.parse_saturating_decimal(value, 2147483648L) {
Some(seconds) => Some(Some(seconds))
None => {
diagnostics.push({
level: Warning,
code: "REQUEST_MAX_STALE_INVALID",
message: "invalid max-stale value was ignored",
field_name: Some("cache-control"),
})
None
}
}
}
}
///|
fn has_validator(headers : Headers) -> Bool {
(headers.first("etag") is Some(value) && value.trim().length() > 0) ||
(headers.first("last-modified") is Some(value) && value.trim().length() > 0)
}