20260620 优化delta长尾控制面和PP cache快路径

This commit is contained in:
2026-06-20 15:31:20 +08:00
parent bd266ef2a5
commit 184d3cb95b
33 changed files with 1951 additions and 109 deletions

View File

@@ -72,6 +72,44 @@ pub fn discover_root_ca_instance_from_tal_url_with_strict_name(
)
}
pub fn discover_root_ca_instance_from_tal_url_with_fetchers(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal_url: &str,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
let tal_bytes = http_fetcher
.fetch(tal_url)
.map_err(FromTalError::TalFetch)?;
let tal = Tal::decode_bytes(&tal_bytes)?;
discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher,
rsync_fetcher,
tal,
Some(tal_url.to_string()),
false,
TaUriOrder::HttpFirst,
)
}
pub fn discover_root_ca_instance_from_tal_url_with_fetchers_strict_name(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal_url: &str,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
let tal_bytes = http_fetcher
.fetch(tal_url)
.map_err(FromTalError::TalFetch)?;
let tal = Tal::decode_bytes(&tal_bytes)?;
discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher,
rsync_fetcher,
tal,
Some(tal_url.to_string()),
true,
TaUriOrder::HttpFirst,
)
}
pub fn discover_root_ca_instance_from_tal(
http_fetcher: &dyn Fetcher,
tal: Tal,
@@ -150,6 +188,7 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers(
tal,
tal_url,
false,
TaUriOrder::RsyncFirst,
)
}
@@ -165,15 +204,23 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers_strict_name(
tal,
tal_url,
true,
TaUriOrder::RsyncFirst,
)
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum TaUriOrder {
HttpFirst,
RsyncFirst,
}
fn discover_root_ca_instance_from_tal_with_fetchers_impl(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
tal: Tal,
tal_url: Option<String>,
strict_name: bool,
ta_uri_order: TaUriOrder,
) -> Result<DiscoveredRootCaInstance, FromTalError> {
if tal.ta_uris.is_empty() {
return Err(FromTalError::NoTaUris);
@@ -181,7 +228,7 @@ fn discover_root_ca_instance_from_tal_with_fetchers_impl(
let mut last_err: Option<String> = None;
let mut ta_uris = tal.ta_uris.clone();
ta_uris.sort_by_key(|uri| if uri.scheme() == "rsync" { 0 } else { 1 });
ta_uris.sort_by_key(|uri| ta_uri_priority(uri, ta_uri_order));
for ta_uri in ta_uris.iter() {
let ta_der = match fetch_ta_der(http_fetcher, rsync_fetcher, ta_uri) {
Ok(b) => b,
@@ -221,6 +268,16 @@ fn discover_root_ca_instance_from_tal_with_fetchers_impl(
})))
}
fn ta_uri_priority(uri: &Url, order: TaUriOrder) -> u8 {
match (order, uri.scheme()) {
(TaUriOrder::HttpFirst, "https" | "http") => 0,
(TaUriOrder::HttpFirst, "rsync") => 1,
(TaUriOrder::RsyncFirst, "rsync") => 0,
(TaUriOrder::RsyncFirst, "https" | "http") => 1,
_ => 2,
}
}
fn fetch_ta_der(
http_fetcher: &dyn Fetcher,
rsync_fetcher: &dyn RsyncFetcher,
@@ -250,41 +307,9 @@ fn fetch_ta_der_via_rsync(
rsync_fetcher: &dyn RsyncFetcher,
ta_rsync_uri: &str,
) -> Result<Vec<u8>, String> {
let base = rsync_parent_uri(ta_rsync_uri)?;
let objects = rsync_fetcher
.fetch_objects(&base)
.map_err(|e| e.to_string())?;
objects
.into_iter()
.find(|(uri, _)| uri == ta_rsync_uri)
.map(|(_, bytes)| bytes)
.ok_or_else(|| format!("TA rsync object not found in fetched subtree: {ta_rsync_uri}"))
}
fn rsync_parent_uri(ta_rsync_uri: &str) -> Result<String, String> {
let url = Url::parse(ta_rsync_uri).map_err(|e| e.to_string())?;
if url.scheme() != "rsync" {
return Err(format!("not an rsync URI: {ta_rsync_uri}"));
}
let host = url
.host_str()
.ok_or_else(|| format!("missing host in rsync URI: {ta_rsync_uri}"))?;
let segments = url
.path_segments()
.ok_or_else(|| format!("missing path in rsync URI: {ta_rsync_uri}"))?
.collect::<Vec<_>>();
if segments.is_empty() || segments.last().copied().unwrap_or_default().is_empty() {
return Err(format!(
"rsync URI must reference a file object: {ta_rsync_uri}"
));
}
let parent_segments = &segments[..segments.len() - 1];
let mut parent = format!("rsync://{host}/");
if !parent_segments.is_empty() {
parent.push_str(&parent_segments.join("/"));
parent.push('/');
}
Ok(parent)
rsync_fetcher
.fetch_object(ta_rsync_uri)
.map_err(|e| e.to_string())
}
pub fn discover_root_ca_instance_from_tal_and_ta_der(
@@ -334,6 +359,20 @@ pub fn canonical_tal_rsync_uri_from_bytes(tal_bytes: &[u8]) -> Result<Url, FromT
mod tests {
use super::*;
use crate::fetch::rsync::LocalDirRsyncFetcher;
use std::collections::HashMap;
struct MapHttpFetcher {
map: HashMap<String, Vec<u8>>,
}
impl Fetcher for MapHttpFetcher {
fn fetch(&self, uri: &str) -> Result<Vec<u8>, String> {
self.map
.get(uri)
.cloned()
.ok_or_else(|| format!("no fixture mapped for {uri}"))
}
}
struct FailingHttpFetcher;
@@ -392,6 +431,84 @@ mod tests {
.starts_with("rsync://")
);
}
struct PanicRsyncFetcher;
impl RsyncFetcher for PanicRsyncFetcher {
fn fetch_objects(
&self,
rsync_base_uri: &str,
) -> crate::fetch::rsync::RsyncFetchResult<Vec<(String, Vec<u8>)>> {
panic!("rsync should not be used when HTTPS TA URI is available: {rsync_base_uri}")
}
}
#[test]
fn discover_root_ca_instance_from_tal_url_with_fetchers_prefers_https_ta_uri() {
let tal_url = "https://example.test/apnic.tal";
let tal_bytes = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/tal/apnic-rfc7730-https.tal"),
)
.unwrap();
let ta_der = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/ta/apnic-ta.cer"),
)
.unwrap();
let https_uri = "https://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer";
let mut map = HashMap::new();
map.insert(tal_url.to_string(), tal_bytes);
map.insert(https_uri.to_string(), ta_der);
let discovery = discover_root_ca_instance_from_tal_url_with_fetchers(
&MapHttpFetcher { map },
&PanicRsyncFetcher,
tal_url,
)
.expect("discover via HTTPS TA URI");
assert_eq!(
discovery.trust_anchor.resolved_ta_uri.unwrap().as_str(),
https_uri
);
}
#[test]
fn discover_root_ca_instance_from_tal_url_with_fetchers_supports_rsync_only_tal() {
let tal_url = "https://example.test/apnic-rsync-only.tal";
let tal_bytes = std::fs::read_to_string(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/tal/apnic-rfc7730-https.tal"),
)
.unwrap();
let rsync_uri = "rsync://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer";
let key_material = tal_bytes
.split_once("\n\n")
.map(|(_, key)| key)
.expect("fixture contains TAL key material");
let rsync_only_tal = format!("{rsync_uri}\n\n{key_material}");
let ta_der = std::fs::read(
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/ta/apnic-ta.cer"),
)
.unwrap();
let td = tempfile::tempdir().unwrap();
let mirror_root = td.path().join("repository");
std::fs::create_dir_all(&mirror_root).unwrap();
std::fs::write(mirror_root.join("apnic-rpki-root-iana-origin.cer"), ta_der).unwrap();
let mut map = HashMap::new();
map.insert(tal_url.to_string(), rsync_only_tal.into_bytes());
let discovery = discover_root_ca_instance_from_tal_url_with_fetchers(
&MapHttpFetcher { map },
&LocalDirRsyncFetcher::new(mirror_root),
tal_url,
)
.expect("discover via rsync-only TAL");
assert_eq!(
discovery.trust_anchor.resolved_ta_uri.unwrap().as_str(),
rsync_uri
);
}
}
pub fn run_root_from_tal_url_once(
@@ -402,7 +519,8 @@ pub fn run_root_from_tal_url_once(
rsync_fetcher: &dyn crate::fetch::rsync::RsyncFetcher,
validation_time: time::OffsetDateTime,
) -> Result<RunFromTalOutput, FromTalError> {
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let discovery =
discover_root_ca_instance_from_tal_url_with_fetchers(http_fetcher, rsync_fetcher, tal_url)?;
let run = run_publication_point_once(
store,