pub use http::Extensions;
use log::warn;
use once_cell::sync::{Lazy, OnceCell};
use pingora_error::{Error, ErrorType::*, OrErr, Result};
use pingora_header_serde::HeaderSerde;
use pingora_http::{HMap, ResponseHeader};
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::time::{Duration, SystemTime};
use crate::key::HashBinary;
pub(crate) type InternalMeta = internal_meta::InternalMetaLatest;
mod internal_meta {
use super::*;
pub(crate) type InternalMetaLatest = InternalMetaV2;
#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct InternalMetaV0 {
pub(crate) fresh_until: SystemTime,
pub(crate) created: SystemTime,
pub(crate) stale_while_revalidate_sec: u32,
pub(crate) stale_if_error_sec: u32,
}
impl InternalMetaV0 {
#[allow(dead_code)]
fn serialize(&self) -> Result<Vec<u8>> {
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v0")
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct InternalMetaV1 {
pub(crate) version: u8,
pub(crate) fresh_until: SystemTime,
pub(crate) created: SystemTime,
pub(crate) stale_while_revalidate_sec: u32,
pub(crate) stale_if_error_sec: u32,
}
impl InternalMetaV1 {
#[allow(dead_code)]
pub const VERSION: u8 = 1;
#[allow(dead_code)]
pub fn serialize(&self) -> Result<Vec<u8>> {
assert_eq!(self.version, 1);
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v1")
}
}
#[derive(Debug, Deserialize, Serialize, Clone)]
pub(crate) struct InternalMetaV2 {
pub(crate) version: u8,
pub(crate) fresh_until: SystemTime,
pub(crate) created: SystemTime,
pub(crate) updated: SystemTime,
pub(crate) stale_while_revalidate_sec: u32,
pub(crate) stale_if_error_sec: u32,
#[serde(default)]
pub(crate) variance: Option<HashBinary>,
#[serde(default)]
pub(crate) epoch_override: Option<SystemTime>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub(crate) provenance: Option<SystemTime>,
}
impl Default for InternalMetaV2 {
fn default() -> Self {
let epoch = SystemTime::UNIX_EPOCH;
InternalMetaV2 {
version: InternalMetaV2::VERSION,
fresh_until: epoch,
created: epoch,
updated: epoch,
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
variance: None,
epoch_override: None,
provenance: None,
}
}
}
impl InternalMetaV2 {
pub const VERSION: u8 = 2;
pub fn serialize(&self) -> Result<Vec<u8>> {
assert_eq!(self.version, Self::VERSION);
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v2")
}
}
impl From<InternalMetaV0> for InternalMetaV2 {
fn from(v0: InternalMetaV0) -> Self {
InternalMetaV2 {
version: InternalMetaV2::VERSION,
fresh_until: v0.fresh_until,
created: v0.created,
updated: v0.created,
stale_while_revalidate_sec: v0.stale_while_revalidate_sec,
stale_if_error_sec: v0.stale_if_error_sec,
..Default::default()
}
}
}
impl From<InternalMetaV1> for InternalMetaV2 {
fn from(v1: InternalMetaV1) -> Self {
InternalMetaV2 {
version: InternalMetaV2::VERSION,
fresh_until: v1.fresh_until,
created: v1.created,
updated: v1.created,
stale_while_revalidate_sec: v1.stale_while_revalidate_sec,
stale_if_error_sec: v1.stale_if_error_sec,
..Default::default()
}
}
}
pub(crate) fn deserialize(buf: &[u8]) -> Result<InternalMetaLatest> {
const MIN_SIZE: usize = 10; if buf.len() < MIN_SIZE {
return Error::e_explain(
InternalError,
format!("Buf too short ({}) to be InternalMeta", buf.len()),
);
}
let preread_buf = &mut &buf[..MIN_SIZE];
match rmp::decode::read_array_len(preread_buf)
.or_err(InternalError, "failed to decode cache meta array size")?
{
4 => Ok(InternalMetaV0::deserialize(buf)?.into()),
_ => {
let version = rmp::decode::read_pfix(preread_buf)
.or_err(InternalError, "failed to decode meta version")?;
match version {
1 => Ok(InternalMetaV1::deserialize(buf)?.into()),
2 => InternalMetaV2::deserialize(buf),
_ => Error::e_explain(
InternalError,
format!("Unknown InternalMeta version {version}"),
),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_internal_meta_serde_v0() {
let meta = InternalMetaV0 {
fresh_until: SystemTime::now(),
created: SystemTime::now(),
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
};
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV0::deserialize(&binary).unwrap();
assert_eq!(meta.fresh_until, meta2.fresh_until);
}
#[test]
fn test_internal_meta_serde_v1() {
let meta = InternalMetaV1 {
version: InternalMetaV1::VERSION,
fresh_until: SystemTime::now(),
created: SystemTime::now(),
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
};
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV1::deserialize(&binary).unwrap();
assert_eq!(meta.fresh_until, meta2.fresh_until);
}
#[test]
fn test_internal_meta_serde_v2() {
let meta = InternalMetaV2::default();
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta.created, meta2.created);
assert_eq!(meta.updated, meta2.updated);
}
#[test]
fn test_internal_meta_serde_across_versions() {
let meta = InternalMetaV0 {
fresh_until: SystemTime::now(),
created: SystemTime::now(),
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
};
let binary = meta.serialize().unwrap();
let meta2 = deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
let meta = InternalMetaV1 {
version: 1,
fresh_until: SystemTime::now(),
created: SystemTime::now(),
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
};
let binary = meta.serialize().unwrap();
let meta2 = deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta2.created, meta2.updated);
}
#[derive(Deserialize, Serialize)]
struct InternalMetaV2Base {
version: u8,
fresh_until: SystemTime,
created: SystemTime,
updated: SystemTime,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
}
impl InternalMetaV2Base {
pub const VERSION: u8 = 2;
pub fn serialize(&self) -> Result<Vec<u8>> {
assert!(self.version >= Self::VERSION);
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v2")
}
}
#[derive(Deserialize, Serialize)]
struct InternalMetaV2BaseWithVariance {
version: u8,
fresh_until: SystemTime,
created: SystemTime,
updated: SystemTime,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
variance: Option<HashBinary>,
}
impl Default for InternalMetaV2BaseWithVariance {
fn default() -> Self {
let epoch = SystemTime::UNIX_EPOCH;
InternalMetaV2BaseWithVariance {
version: InternalMetaV2::VERSION,
fresh_until: epoch,
created: epoch,
updated: epoch,
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
variance: None,
}
}
}
impl InternalMetaV2BaseWithVariance {
pub const VERSION: u8 = 2;
pub fn serialize(&self) -> Result<Vec<u8>> {
assert!(self.version >= Self::VERSION);
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v2")
}
}
#[derive(Deserialize, Serialize)]
struct InternalMetaV2BeforeProvenance {
version: u8,
fresh_until: SystemTime,
created: SystemTime,
updated: SystemTime,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
#[serde(default)]
variance: Option<HashBinary>,
#[serde(default)]
epoch_override: Option<SystemTime>,
}
impl Default for InternalMetaV2BeforeProvenance {
fn default() -> Self {
let epoch = SystemTime::UNIX_EPOCH;
InternalMetaV2BeforeProvenance {
version: InternalMetaV2::VERSION,
fresh_until: epoch,
created: epoch,
updated: epoch,
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
variance: None,
epoch_override: None,
}
}
}
impl InternalMetaV2BeforeProvenance {
pub fn serialize(&self) -> Result<Vec<u8>> {
rmp_serde::encode::to_vec(self).or_err(InternalError, "failed to encode cache meta")
}
fn deserialize(buf: &[u8]) -> Result<Self> {
rmp_serde::decode::from_slice(buf)
.or_err(InternalError, "failed to decode cache meta v2")
}
}
#[test]
fn test_internal_meta_serde_v2_extend_fields_variance() {
let meta = InternalMetaV2BaseWithVariance::default();
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV2Base::deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta.created, meta2.created);
assert_eq!(meta.updated, meta2.updated);
let now = SystemTime::now();
let meta = InternalMetaV2Base {
version: InternalMetaV2::VERSION,
fresh_until: now,
created: now,
updated: now,
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
};
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV2BaseWithVariance::deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta.created, meta2.created);
assert_eq!(meta.updated, meta2.updated);
}
#[test]
fn test_internal_meta_serde_v2_extend_fields_epoch_override() {
let now = SystemTime::now();
let mut meta = InternalMetaV2BaseWithVariance {
version: InternalMetaV2::VERSION,
fresh_until: now,
created: now,
updated: now,
stale_while_revalidate_sec: 0,
stale_if_error_sec: 0,
variance: None,
};
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta.created, meta2.created);
assert_eq!(meta.updated, meta2.updated);
assert!(meta2.variance.is_none());
assert!(meta2.epoch_override.is_none());
meta.variance = Some(*b"variance_testing");
let binary = meta.serialize().unwrap();
let meta2 = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(meta2.version, 2);
assert_eq!(meta.fresh_until, meta2.fresh_until);
assert_eq!(meta.created, meta2.created);
assert_eq!(meta.updated, meta2.updated);
assert_eq!(meta.variance, meta2.variance);
assert!(meta2.epoch_override.is_none());
}
#[test]
fn test_internal_meta_serde_v2_epoch_override_always_serialized() {
let now = SystemTime::now();
let meta_none = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
epoch_override: None,
..Default::default()
};
let meta_some = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
epoch_override: Some(now),
..Default::default()
};
let bin_none = meta_none.serialize().unwrap();
let bin_some = meta_some.serialize().unwrap();
let len_none =
rmp::decode::read_array_len(&mut &bin_none[..]).expect("decode array len");
let len_some =
rmp::decode::read_array_len(&mut &bin_some[..]).expect("decode array len");
assert_eq!(len_none, len_some);
let decoded_none = InternalMetaV2::deserialize(&bin_none).unwrap();
let decoded_some = InternalMetaV2::deserialize(&bin_some).unwrap();
assert!(decoded_none.epoch_override.is_none());
assert_eq!(decoded_some.epoch_override, Some(now));
let meta_none_with_variance = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
variance: Some(*b"variance_testing"),
epoch_override: None,
..Default::default()
};
let meta_some_with_variance = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
variance: Some(*b"variance_testing"),
epoch_override: Some(now),
..Default::default()
};
let bin_none = meta_none_with_variance.serialize().unwrap();
let bin_some = meta_some_with_variance.serialize().unwrap();
let len_none =
rmp::decode::read_array_len(&mut &bin_none[..]).expect("decode array len");
let len_some =
rmp::decode::read_array_len(&mut &bin_some[..]).expect("decode array len");
assert_eq!(len_none, len_some);
}
#[test]
fn test_internal_meta_serde_v2_extend_fields_provenance_backward_compat() {
let now = SystemTime::now();
let old = InternalMetaV2BeforeProvenance {
fresh_until: now,
created: now,
updated: now,
variance: Some(*b"variance_testing"),
epoch_override: Some(now),
..Default::default()
};
let binary = old.serialize().unwrap();
let decoded = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(decoded.version, 2);
assert_eq!(decoded.fresh_until, now);
assert_eq!(decoded.created, now);
assert_eq!(decoded.variance, Some(*b"variance_testing"));
assert_eq!(decoded.epoch_override, Some(now));
assert!(decoded.provenance.is_none());
}
#[test]
fn test_internal_meta_serde_v2_extend_fields_provenance_forward_compat_none() {
let now = SystemTime::now();
let current = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
variance: Some(*b"variance_testing"),
epoch_override: Some(now),
provenance: None,
..Default::default()
};
let binary = current.serialize().unwrap();
let decoded = InternalMetaV2BeforeProvenance::deserialize(&binary).unwrap();
assert_eq!(decoded.fresh_until, now);
assert_eq!(decoded.created, now);
assert_eq!(decoded.variance, Some(*b"variance_testing"));
assert_eq!(decoded.epoch_override, Some(now));
}
#[test]
fn test_internal_meta_serde_v2_extend_fields_provenance_some_needs_field_support() {
let now = SystemTime::now();
let current = InternalMetaV2 {
fresh_until: now,
created: now,
updated: now,
variance: Some(*b"variance_testing"),
epoch_override: Some(now),
provenance: Some(now),
..Default::default()
};
let binary = current.serialize().unwrap();
assert!(InternalMetaV2BeforeProvenance::deserialize(&binary).is_err());
let decoded = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(decoded.provenance, Some(now));
}
#[test]
fn test_internal_meta_serde_v2_provenance_round_trip() {
let admission = SystemTime::now();
let updated = admission + Duration::from_secs(300);
let meta = InternalMetaV2 {
fresh_until: updated,
created: updated, updated,
provenance: Some(admission), ..Default::default()
};
let binary = meta.serialize().unwrap();
let decoded = InternalMetaV2::deserialize(&binary).unwrap();
assert_eq!(decoded.created, updated);
assert_eq!(decoded.provenance, Some(admission));
}
}
}
#[derive(Debug)]
pub(crate) struct CacheMetaInner {
pub(crate) internal: InternalMeta,
pub(crate) header: ResponseHeader,
pub extensions: Extensions,
}
#[derive(Debug)]
pub struct CacheMeta(pub(crate) Box<CacheMetaInner>);
impl CacheMeta {
pub fn new(
fresh_until: SystemTime,
created: SystemTime,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
header: ResponseHeader,
) -> CacheMeta {
CacheMeta(Box::new(CacheMetaInner {
internal: InternalMeta {
version: InternalMeta::VERSION,
fresh_until,
created,
updated: created, stale_while_revalidate_sec,
stale_if_error_sec,
provenance: Some(created),
..Default::default()
},
header,
extensions: Extensions::new(),
}))
}
pub fn created(&self) -> SystemTime {
self.0.internal.created
}
pub fn updated(&self) -> SystemTime {
self.0.internal.updated
}
pub fn provenance(&self) -> SystemTime {
self.0
.internal
.provenance
.unwrap_or(self.0.internal.created)
}
pub(crate) fn set_provenance(&mut self, provenance: SystemTime) {
self.0.internal.provenance = Some(provenance);
}
pub(crate) fn reset_provenance_to_created(&mut self) {
self.0.internal.provenance = Some(self.0.internal.created);
}
#[cfg(test)]
pub(crate) fn provenance_raw(&self) -> Option<SystemTime> {
self.0.internal.provenance
}
pub fn epoch(&self) -> SystemTime {
self.0.internal.epoch_override.unwrap_or(self.updated())
}
pub fn epoch_override(&self) -> Option<SystemTime> {
self.0.internal.epoch_override
}
pub fn set_epoch_override(&mut self, epoch: SystemTime) {
self.0.internal.epoch_override = Some(epoch);
}
pub fn remove_epoch_override(&mut self) {
self.0.internal.epoch_override = None;
}
pub fn is_fresh(&self, time: SystemTime) -> bool {
self.0.internal.fresh_until >= time
}
pub fn fresh_sec(&self) -> u64 {
let reference = self.epoch();
self.0
.internal
.fresh_until
.duration_since(reference)
.map_or(0, |duration| duration.as_secs())
}
pub fn fresh_until(&self) -> SystemTime {
self.0.internal.fresh_until
}
pub fn age(&self) -> Duration {
let reference = self.epoch();
SystemTime::now()
.duration_since(reference)
.unwrap_or_default()
}
pub fn stale_while_revalidate_sec(&self) -> u32 {
self.0.internal.stale_while_revalidate_sec
}
pub fn stale_if_error_sec(&self) -> u32 {
self.0.internal.stale_if_error_sec
}
pub fn serve_stale_while_revalidate(&self, time: SystemTime) -> bool {
self.can_serve_stale(self.0.internal.stale_while_revalidate_sec, time)
}
pub fn serve_stale_if_error(&self, time: SystemTime) -> bool {
self.can_serve_stale(self.0.internal.stale_if_error_sec, time)
}
pub fn disable_serve_stale(&mut self) {
self.0.internal.stale_if_error_sec = 0;
self.0.internal.stale_while_revalidate_sec = 0;
}
pub fn expire_at(&mut self, instant: SystemTime) {
let fresh_until = &mut self.0.internal.fresh_until;
*fresh_until = (*fresh_until).min(instant);
}
pub fn update_freshness(
&mut self,
fresh_until: SystemTime,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
) {
self.0.internal.fresh_until = fresh_until;
self.0.internal.stale_while_revalidate_sec = stale_while_revalidate_sec;
self.0.internal.stale_if_error_sec = stale_if_error_sec;
self.0.internal.updated = SystemTime::now();
}
pub fn variance(&self) -> Option<HashBinary> {
self.0.internal.variance
}
pub fn set_variance_key(&mut self, variance_key: HashBinary) {
self.0.internal.variance = Some(variance_key);
}
pub fn set_variance(&mut self, variance: HashBinary) {
self.0.internal.variance = Some(variance)
}
pub fn remove_variance(&mut self) {
self.0.internal.variance = None
}
pub fn response_header(&self) -> &ResponseHeader {
&self.0.header
}
pub fn response_header_mut(&mut self) -> &mut ResponseHeader {
&mut self.0.header
}
pub fn extensions(&self) -> &Extensions {
&self.0.extensions
}
pub fn extensions_mut(&mut self) -> &mut Extensions {
&mut self.0.extensions
}
pub fn response_header_copy(&self) -> ResponseHeader {
self.0.header.clone()
}
pub fn headers(&self) -> &HMap {
&self.0.header.headers
}
fn can_serve_stale(&self, serve_stale_sec: u32, time: SystemTime) -> bool {
if serve_stale_sec == 0 {
return false;
}
if let Some(stale_until) = self
.0
.internal
.fresh_until
.checked_add(Duration::from_secs(serve_stale_sec.into()))
{
stale_until >= time
} else {
true
}
}
pub fn serialize(&self) -> Result<(Vec<u8>, Vec<u8>)> {
let internal = self.0.internal.serialize()?;
let header = header_serialize(&self.0.header)?;
log::debug!("header to serialize: {:?}", self.0.header);
Ok((internal, header))
}
pub fn deserialize(internal: &[u8], header: &[u8]) -> Result<Self> {
let internal = internal_meta::deserialize(internal)?;
let header = header_deserialize(header)?;
Ok(CacheMeta(Box::new(CacheMetaInner {
internal,
header,
extensions: Extensions::new(),
})))
}
}
use http::StatusCode;
pub type FreshDurationByStatusFn = fn(StatusCode) -> Option<Duration>;
pub struct CacheMetaDefaults {
fresh_sec_fn: FreshDurationByStatusFn,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
}
impl CacheMetaDefaults {
pub const fn new(
fresh_sec_fn: FreshDurationByStatusFn,
stale_while_revalidate_sec: u32,
stale_if_error_sec: u32,
) -> Self {
CacheMetaDefaults {
fresh_sec_fn,
stale_while_revalidate_sec,
stale_if_error_sec,
}
}
pub fn fresh_sec(&self, resp_status: StatusCode) -> Option<Duration> {
if resp_status == StatusCode::NOT_MODIFIED {
(self.fresh_sec_fn)(StatusCode::OK)
} else {
(self.fresh_sec_fn)(resp_status)
}
}
pub fn serve_stale_while_revalidate_sec(&self) -> u32 {
self.stale_while_revalidate_sec
}
pub fn serve_stale_if_error_sec(&self) -> u32 {
self.stale_if_error_sec
}
}
static COMPRESSION_DICT_CONTENT: OnceCell<Cow<'static, [u8]>> = OnceCell::new();
static HEADER_SERDE: Lazy<HeaderSerde> = Lazy::new(|| {
let dict_opt = if let Some(dict_content) = COMPRESSION_DICT_CONTENT.get() {
Some(dict_content.to_vec())
} else {
warn!("no header compression dictionary loaded - use set_compression_dict_content() or set_compression_dict_path() to set one");
None
};
HeaderSerde::new(dict_opt)
});
pub(crate) fn header_serialize(header: &ResponseHeader) -> Result<Vec<u8>> {
HEADER_SERDE.serialize(header)
}
pub(crate) fn header_deserialize<T: AsRef<[u8]>>(buf: T) -> Result<ResponseHeader> {
HEADER_SERDE.deserialize(buf.as_ref())
}
pub fn set_compression_dict_path(path: &str) -> bool {
match std::fs::read(path) {
Ok(dict) => COMPRESSION_DICT_CONTENT.set(dict.into()).is_ok(),
Err(e) => {
warn!(
"failed to read header compress dictionary file at {}, {:?}",
path, e
);
false
}
}
}
pub fn set_compression_dict_content(content: Cow<'static, [u8]>) -> bool {
COMPRESSION_DICT_CONTENT.set(content).is_ok()
}
#[cfg(test)]
mod tests {
use super::*;
use std::time::Duration;
#[test]
fn test_cache_meta_age_without_override() {
let now = SystemTime::now();
let header = ResponseHeader::build_no_case(200, None).unwrap();
let meta = CacheMeta::new(now + Duration::from_secs(300), now, 0, 0, header);
std::thread::sleep(Duration::from_millis(100));
let age = meta.age();
assert!(age.as_secs() < 1, "age should be close to 0");
assert_eq!(meta.epoch(), meta.updated());
}
#[test]
fn test_cache_meta_age_with_epoch_override_past() {
let now = SystemTime::now();
let header = ResponseHeader::build(200, None).unwrap();
let mut meta = CacheMeta::new(now + Duration::from_secs(300), now, 0, 0, header);
let epoch_override = now - Duration::from_secs(10);
meta.set_epoch_override(epoch_override);
let age = meta.age();
assert!(age.as_secs() >= 10);
assert!(age.as_secs() < 12);
assert_eq!(meta.epoch(), epoch_override);
assert_eq!(meta.epoch_override(), Some(epoch_override));
}
#[test]
fn test_cache_meta_age_with_epoch_override_future() {
let now = SystemTime::now();
let header = ResponseHeader::build(200, None).unwrap();
let mut meta = CacheMeta::new(now + Duration::from_secs(100), now, 0, 0, header);
let future_epoch = now + Duration::from_secs(10);
meta.set_epoch_override(future_epoch);
let age_with_epoch = meta.age();
assert_eq!(age_with_epoch, Duration::ZERO);
}
#[test]
fn test_cache_meta_fresh_sec() {
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
SystemTime::now() + Duration::from_secs(100),
SystemTime::now() - Duration::from_secs(100),
0,
0,
header,
);
meta.0.internal.updated = SystemTime::UNIX_EPOCH + Duration::from_secs(1000);
meta.0.internal.fresh_until = SystemTime::UNIX_EPOCH + Duration::from_secs(1100);
let fresh_sec_without_override = meta.fresh_sec();
assert_eq!(fresh_sec_without_override, 100);
let epoch_override = SystemTime::UNIX_EPOCH + Duration::from_secs(1050);
meta.set_epoch_override(epoch_override);
assert_eq!(meta.epoch_override(), Some(epoch_override));
assert_eq!(meta.epoch(), epoch_override);
let fresh_sec_with_override = meta.fresh_sec();
assert_eq!(fresh_sec_with_override, 50);
meta.remove_epoch_override();
assert_eq!(meta.epoch_override(), None);
assert_eq!(meta.epoch(), meta.updated());
assert_eq!(meta.fresh_sec(), 100); }
#[test]
fn test_cache_meta_new_stamps_provenance() {
let now = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let meta = CacheMeta::new(now + Duration::from_secs(60), now, 0, 0, header);
assert_eq!(meta.created(), now);
assert_eq!(meta.provenance(), now);
assert_eq!(meta.provenance_raw(), Some(now));
}
#[test]
fn test_cache_meta_provenance_fallback_for_absent_field() {
let admission = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(admission + Duration::from_secs(60), admission, 0, 0, header);
meta.0.internal.provenance = None;
assert_eq!(meta.created(), admission);
assert!(meta.provenance_raw().is_none());
assert_eq!(meta.provenance(), admission);
}
#[test]
fn test_cache_meta_set_provenance() {
let admission = SystemTime::now();
let provenance = admission - Duration::from_secs(30);
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(admission + Duration::from_secs(60), admission, 0, 0, header);
meta.set_provenance(provenance);
assert_eq!(meta.created(), admission);
assert_eq!(meta.provenance(), provenance);
assert_eq!(meta.provenance_raw(), Some(provenance));
}
#[test]
fn expiring_at_the_current_instant_leaves_the_serve_stale_windows_open() {
let admission = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
admission + Duration::from_secs(300),
admission,
30,
30,
header,
);
assert!(meta.is_fresh(admission));
let expired_at = SystemTime::now();
meta.expire_at(expired_at);
assert_eq!(meta.fresh_until(), expired_at);
assert!(!meta.is_fresh(expired_at + Duration::from_secs(1)));
assert!(meta.serve_stale_while_revalidate(expired_at));
assert!(meta.serve_stale_if_error(expired_at));
}
#[test]
fn expiring_at_an_instant_measures_the_serve_stale_windows_from_that_instant() {
let admission = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
admission + Duration::from_secs(3600),
admission,
600,
600,
header,
);
let expired_at = admission + Duration::from_secs(300);
meta.expire_at(expired_at);
assert_eq!(meta.fresh_until(), expired_at);
assert!(!meta.is_fresh(expired_at + Duration::from_secs(1)));
assert!(meta.serve_stale_while_revalidate(expired_at + Duration::from_secs(599)));
assert!(meta.serve_stale_if_error(expired_at + Duration::from_secs(599)));
assert!(!meta.serve_stale_while_revalidate(expired_at + Duration::from_secs(601)));
assert!(!meta.serve_stale_if_error(expired_at + Duration::from_secs(601)));
}
#[test]
fn expiring_at_an_instant_is_idempotent() {
let admission = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
admission + Duration::from_secs(3600),
admission,
600,
600,
header,
);
let expired_at = admission + Duration::from_secs(300);
meta.expire_at(expired_at);
meta.expire_at(expired_at);
meta.expire_at(expired_at);
assert_eq!(meta.fresh_until(), expired_at);
assert!(!meta.serve_stale_while_revalidate(expired_at + Duration::from_secs(601)));
}
#[test]
fn expiring_at_a_later_instant_leaves_a_closed_window_closed() {
let admission = SystemTime::now() - Duration::from_secs(3600);
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let fresh_until = admission + Duration::from_secs(60);
let mut meta = CacheMeta::new(fresh_until, admission, 60, 60, header);
let now = SystemTime::now();
assert!(!meta.serve_stale_while_revalidate(now));
meta.expire_at(now);
assert_eq!(
meta.fresh_until(),
fresh_until,
"expiring later than the asset's own deadline must not move it"
);
assert!(!meta.serve_stale_while_revalidate(now));
assert!(!meta.serve_stale_if_error(now));
}
#[test]
fn expiring_and_disabling_serve_stale_leaves_nothing_to_serve() {
let admission = SystemTime::now();
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
admission + Duration::from_secs(300),
admission,
30,
30,
header,
);
let expired_at = SystemTime::now();
meta.expire_at(expired_at);
meta.disable_serve_stale();
assert!(!meta.is_fresh(expired_at + Duration::from_secs(1)));
assert!(!meta.serve_stale_while_revalidate(expired_at));
assert!(!meta.serve_stale_if_error(expired_at));
}
#[test]
fn expiring_a_meta_leaves_its_admission_history_alone() {
let admission = SystemTime::now() - Duration::from_secs(120);
let provenance = admission - Duration::from_secs(30);
let header = ResponseHeader::build(StatusCode::OK, None).unwrap();
let mut meta = CacheMeta::new(
admission + Duration::from_secs(300),
admission,
0,
0,
header,
);
meta.set_provenance(provenance);
meta.expire_at(SystemTime::now());
assert_eq!(meta.created(), admission);
assert_eq!(meta.updated(), admission);
assert_eq!(meta.provenance(), provenance);
}
}