20260620 优化delta长尾控制面和PP cache快路径
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user