use crate::errors::data_sources;
use crate::{BgpkitCommonsError, Result};
use super::RpkiTrie;
use super::rpki_client::RpkiClientData;
const CLOUDFLARE_RPKI_URL: &str = "https://rpki.cloudflare.com/rpki.json";
#[derive(Clone)]
pub struct RpkiLoad {
pub trie: RpkiTrie,
pub etag: Option<String>,
pub last_modified: Option<String>,
}
impl RpkiTrie {
pub fn from_cloudflare() -> Result<Self> {
match Self::from_cloudflare_conditional(None, None)? {
Some(load) => Ok(load.trie),
None => Err(BgpkitCommonsError::data_source_error(
data_sources::CLOUDFLARE,
"server returned 304 Not Modified for an unconditional request",
)),
}
}
pub fn from_cloudflare_conditional(
etag: Option<&str>,
last_modified: Option<&str>,
) -> Result<Option<RpkiLoad>> {
let Some(fetch) =
RpkiClientData::from_url_conditional(CLOUDFLARE_RPKI_URL, etag, last_modified)?
else {
return Ok(None);
};
let trie = Self::from_rpki_client_data(fetch.data, None)?;
Ok(Some(RpkiLoad {
trie,
etag: fetch.etag,
last_modified: fetch.last_modified,
}))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_from_cloudflare() {
let trie = RpkiTrie::from_cloudflare().expect("Failed to load Cloudflare RPKI data");
let total_roas: usize = trie.trie.iter().map(|(_, roas)| roas.len()).sum();
println!("Loaded {} ROAs from Cloudflare", total_roas);
println!("Loaded {} ASPAs", trie.aspas.len());
assert!(total_roas > 0, "Should have loaded some ROAs");
}
#[test]
#[ignore] fn test_from_cloudflare_conditional() {
let load = RpkiTrie::from_cloudflare_conditional(None, None)
.expect("Failed to load Cloudflare RPKI data")
.expect("Unconditional load should return data");
assert!(load.etag.is_some(), "Cloudflare should return an ETag");
assert!(load.last_modified.is_some());
let unchanged = RpkiTrie::from_cloudflare_conditional(
load.etag.as_deref(),
load.last_modified.as_deref(),
)
.expect("Conditional request failed");
assert!(
unchanged.is_none(),
"Same validators should yield 304 Not Modified"
);
let changed = RpkiTrie::from_cloudflare_conditional(Some("\"stale-etag\""), None)
.expect("Conditional request failed");
assert!(changed.is_some(), "Stale ETag should yield fresh data");
}
}