use crate::{Client, ClientError, CtClient};
use luct_core::{
tiling::{Checkpoint, Tile, TileId, TilingError},
v1::SignedTreeHead,
};
use url::Url;
impl<C: Client> CtClient<C> {
#[tracing::instrument(level = "trace")]
pub async fn get_checkpoint(&self) -> Result<SignedTreeHead, ClientError> {
self.assert_v1()?;
let url = self.get_url("checkpoint")?;
let (status, response) = self.client.get(&url, &[]).await?;
self.check_status(&url, status, &response)?;
let checkpoint = Checkpoint::parse_checkpoint(&response)?;
let sth = self
.log
.validate_checkpoint(&checkpoint)
.map_err(|err| ClientError::SignatureValidationFailed("checkpoint STH", err))?;
tracing::debug!(
"fetched and validated checkpoint: {:?} from url {}",
sth,
url
);
Ok(sth)
}
#[tracing::instrument(level = "trace")]
pub async fn get_tile(&self, mut tile_id: TileId) -> Result<Tile, ClientError> {
self.assert_v1()?;
let url = self.get_url(&tile_id.as_url())?;
let (mut status, mut response) = self.client.get_bin(&url, &[]).await?;
if status == 404 && tile_id.is_partial() {
tile_id = tile_id.into_unpartial();
let url = self.get_url(&tile_id.as_url())?;
(status, response) = self.client.get_bin(&url, &[]).await?;
};
self.check_status_binary(&url, status, &response)?;
tracing::trace!("fetched tile {:?}, from url: {}", tile_id, url);
Ok(tile_id.with_data(response)?)
}
fn get_url(&self, path: &str) -> Result<Url, ClientError> {
let url = self
.log
.config()
.tile_url()
.as_ref()
.ok_or(TilingError::NonTilingLog)?;
Ok(url.join(path).map_err(|_| TilingError::NonTilingLog)?)
}
}
#[cfg(all(test, feature = "reqwest"))]
mod tests {
use super::*;
use crate::reqwest::ReqwestClient;
use luct_core::{CtLogConfig, tree::NodeKey};
const ARCHE2026H1: &str = "{
\"description\": \"Google 'Arche2026h1' log\",
\"key\": \"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZ+3YKoZTMruov4cmlImbk4MckBNzEdCyMuHlwGgJ8BUrzFLlR5U0619xDDXIXespkpBgCNVQAkhMTTXakM6KMg==\",
\"url\": \"https://arche2026h1.staging.ct.transparency.dev/\",
\"tile_url\": \"https://storage.googleapis.com/static-ct-staging-arche2026h1-bucket/\",
\"mmd\": 60
}";
#[test]
fn get_url() {
let client = get_client();
let url = client
.get_url(
&TileId::from_node_key(&NodeKey::leaf(1), 1000)
.unwrap()
.as_url(),
)
.unwrap();
assert_eq!(
url.to_string(),
"https://storage.googleapis.com/static-ct-staging-arche2026h1-bucket/tile/0/000"
)
}
#[tokio::test]
#[ignore = "Makes an HTTP call, for manual testing only"]
async fn get_checkpoint() {
let client = get_client();
let _ = client.get_checkpoint().await.unwrap();
}
#[tokio::test]
#[ignore = "Makes an HTTP call, for manual testing only"]
async fn get_tile() {
let client = get_client();
let _ = client
.get_tile(TileId::from_node_key(&NodeKey::leaf(1), 1000).unwrap())
.await
.unwrap();
}
fn get_client() -> CtClient<ReqwestClient> {
let config: CtLogConfig = serde_json::from_str(ARCHE2026H1).unwrap();
let client = ReqwestClient::new("luct-test");
CtClient::new(config, client)
}
}