Files
NLnetLabs-krill/src/api/pubd.rs
T
Martin HoffmannandGitHub 35cd5d0bb2 Redesign the HTTP service. (#1264)
Initially, this PR was intended to more clearly separate code between Krill
“business logic” – now bundled in a KrillManager –, and the HTTP server code
that serves the API. The former now lives in the server module, the latter
in the daemon module together with all the code to spin up a Krill daemon
driving the HTTP server.

However, along the way it turned into a complete redesign of how the HTTP
server code works. Request handling has been split into three stages that
forces implementers to check for permissions (or actively choose to not
check), and read the body (or check that there isn’t one). Dispatching of
the request has been restructured which should make it easier to follow what
goes on where.

This PR increases the minimum Rust version to 1.81.
2025-04-14 11:17:08 +02:00

131 lines
3.9 KiB
Rust

use std::fmt;
use std::collections::HashMap;
use rpki::uri;
use rpki::ca::idexchange::PublisherHandle;
use rpki::repository::{x509::Time, Manifest};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
//------------ RepoStats -----------------------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RepoStats {
pub publishers: HashMap<PublisherHandle, PublisherStats>,
pub session: Uuid,
pub serial: u64,
pub last_update: Option<Time>,
pub rsync_base: uri::Rsync,
pub rrdp_base: uri::Https,
}
impl RepoStats {
pub fn stale_publishers(
self, seconds: i64
) -> impl Iterator<Item = PublisherHandle> {
self.publishers.into_iter().filter_map(move |(publisher, stats)| {
if let Some(update_time) = stats.last_update() {
if Time::now().timestamp() - update_time.timestamp()
>= seconds
{
Some(publisher)
}
else {
None
}
} else {
Some(publisher)
}
})
}
}
impl fmt::Display for RepoStats {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "Server URIs:")?;
writeln!(f, " rrdp: {}", self.rrdp_base)?;
writeln!(f, " rsync: {}", self.rsync_base)?;
writeln!(f)?;
if let Some(update) = self.last_update {
writeln!(f, "RRDP updated: {}", update.to_rfc3339())?;
}
writeln!(f, "RRDP session: {}", self.session)?;
writeln!(f, "RRDP serial: {}", self.serial)?;
writeln!(f)?;
writeln!(f, "Publisher, Objects, Size, Last Updated")?;
for (publisher, stats) in &self.publishers {
let update_str = match stats.last_update() {
None => "never".to_string(),
Some(update) => update.to_rfc3339(),
};
writeln!(
f,
"{}, {}, {}, {}",
publisher,
stats.objects,
stats.size,
update_str
)?;
}
Ok(())
}
}
//------------ PublisherStats ------------------------------------------------
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherStats {
pub objects: usize,
pub size: usize,
pub manifests: Vec<PublisherManifestStats>,
}
impl PublisherStats {
/// Returns the most recent "this_update" time
/// from all manifest(s) published by this publisher,
/// if any.. i.e. there may be 0, 1 or many manifests
pub fn last_update(&self) -> Option<Time> {
let mut last_update = None;
for mft in &self.manifests {
if let Some(last_update_until_now) = last_update {
let this_manifest_this_update = mft.this_update;
if this_manifest_this_update > last_update_until_now {
last_update = Some(this_manifest_this_update)
}
} else {
last_update = Some(mft.this_update);
}
}
last_update
}
}
//------------ PublisherManifestStats ----------------------------------------
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct PublisherManifestStats {
pub uri: uri::Rsync,
pub this_update: Time,
pub next_update: Time,
}
impl TryFrom<&Manifest> for PublisherManifestStats {
type Error = ();
// This will fail for syntactically incorrect manifests, which do
// not include the signed object URI in their SIA.
fn try_from(mft: &Manifest) -> Result<Self, Self::Error> {
let uri = mft.cert().signed_object().cloned().ok_or(())?;
Ok(PublisherManifestStats {
uri,
this_update: mft.this_update(),
next_update: mft.next_update(),
})
}
}