///|
/// Intermediate values from RFC 9111 section 4.2.3 age calculation.
pub(all) struct AgeBreakdown {
date_value : Timestamp?
age_value : Int64
apparent_age : Int64
response_delay : Int64
corrected_age_value : Int64
corrected_initial_age : Int64
resident_time : Int64
current_age : Int64
} derive(Debug, Eq)
///|
pub(all) struct AgeResult {
breakdown : AgeBreakdown
trace : Array[TraceStep]
diagnostics : Array[Diagnostic]
} derive(Debug, Eq)
///|
/// Calculate current response age with saturating arithmetic. Reversed clocks
/// are clamped to zero-duration intervals and reported diagnostically.
pub fn calculate_current_age(
stored : StoredResponse,
now : Timestamp,
) -> AgeResult {
let diagnostics : Array[Diagnostic] = []
let trace : Array[TraceStep] = []
let date_value = header_date(stored.response.headers, "date", now)
if stored.response.headers.contains("date") && date_value is None {
diagnostics.push({
level: Warning,
code: "AGE_DATE_INVALID",
message: "Date is invalid or repeated and was ignored",
field_name: Some("date"),
})
}
let age_value = parse_age_value(stored.response.headers, diagnostics)
let apparent_age = match date_value {
Some(date) => nonnegative_difference(stored.response_time, date)
None => 0L
}
let response_delay = if stored.response_time >= stored.request_time {
stored.response_time - stored.request_time
} else {
diagnostics.push({
level: Warning,
code: "AGE_RESPONSE_TIME_BEFORE_REQUEST",
message: "response time precedes request time; delay was clamped to zero",
field_name: None,
})
0L
}
let corrected_age_value = saturated_add(age_value, response_delay)
let corrected_initial_age = if apparent_age > corrected_age_value {
apparent_age
} else {
corrected_age_value
}
let resident_time = if now >= stored.response_time {
now - stored.response_time
} else {
diagnostics.push({
level: Warning,
code: "AGE_NOW_BEFORE_RESPONSE",
message: "current time precedes response time; resident time was clamped to zero",
field_name: None,
})
0L
}
let current_age = saturated_add(corrected_initial_age, resident_time)
trace.push(
step(
"AGE_CALCULATED", "current age calculated from apparent age, corrected Age, and resident time",
"RFC 9111 4.2.3",
),
)
{
breakdown: {
date_value,
age_value,
apparent_age,
response_delay,
corrected_age_value,
corrected_initial_age,
resident_time,
current_age,
},
trace,
diagnostics,
}
}
///|
fn parse_age_value(headers : Headers, diagnostics : Array[Diagnostic]) -> Int64 {
let values = headers.values("age")
if values.length() == 0 {
return 0L
}
if values.length() > 1 {
diagnostics.push({
level: Warning,
code: "AGE_REPEATED",
message: "repeated Age fields were ignored",
field_name: Some("age"),
})
return 0L
}
match @lex.parse_saturating_decimal(values[0], 2147483648L) {
Some(value) => value
None => {
diagnostics.push({
level: Warning,
code: "AGE_VALUE_INVALID",
message: "invalid Age field was treated as zero",
field_name: Some("age"),
})
0L
}
}
}
///|
fn nonnegative_difference(later : Int64, earlier : Int64) -> Int64 {
if later <= earlier {
0L
} else if later - earlier > 2147483648L {
2147483648L
} else {
later - earlier
}
}
///|
fn saturated_add(left : Int64, right : Int64) -> Int64 {
if left >= 2147483648L || right >= 2147483648L - left {
2147483648L
} else {
left + right
}
}