From c5e3fcfc99f7a1810bc1fddcafa1af97500eea96 Mon Sep 17 00:00:00 2001 From: Stefano Pigozzi Date: Sat, 9 Nov 2024 11:01:26 +0100 Subject: [PATCH] Create crate --- acrate-nodeinfo/src/lib.rs | 146 ++++++++++++++++++++++++++++++++++--- 1 file changed, 135 insertions(+), 11 deletions(-) diff --git a/acrate-nodeinfo/src/lib.rs b/acrate-nodeinfo/src/lib.rs index b93cf3f..c43fef7 100644 --- a/acrate-nodeinfo/src/lib.rs +++ b/acrate-nodeinfo/src/lib.rs @@ -1,14 +1,138 @@ -pub fn add(left: u64, right: u64) -> u64 { - left + right +use serde::Deserialize; + + +#[derive(Debug, Clone, Deserialize)] +pub struct Discovery { + pub links: Vec, } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +#[derive(Debug, Clone, Deserialize)] +pub struct DiscoveryDocument { + pub rel: String, + pub href: String, +} + +impl Discovery { + pub async fn get(url: reqwest::Url) -> Result { + use DiscoveryGetError::*; + + log::debug!("Getting nodeinfo discovery at: {url}"); + + log::trace!("Sending GET request to: {url}"); + let response = reqwest::get(url) + .await + .map_err(Request)?; + + log::trace!("Checking headers of the response..."); + response + .headers() + .get("Content-Type") + .ok_or(ContentTypeMissing)? + .eq("application/json") + .then_some(()) + .ok_or(ContentTypeInvalid)?; + + log::trace!("Attempting to parse nodeinfo discovery as JSON..."); + let data = response.json::() + .await + .map_err(Parse)?; + + Ok(data) + } + + const WELLKNOWN_DISCOVERY_PATH: &str = "/.well-known/nodeinfo"; + + pub async fn discover(base: &reqwest::Url) -> Result { + use DiscoveryDiscoverError::*; + + log::debug!("Discovering nodeinfo at base: {base}"); + + let mut url = base.clone(); + + let path = Self::WELLKNOWN_DISCOVERY_PATH; + log::trace!("Setting URL path to `{path}`..."); + url.set_path(path); + + log::trace!("Unsetting URL query..."); + url.set_query(None); + + log::trace!("Unsetting URL fragment..."); + url.set_fragment(None); + + log::trace!("Setting URL scheme to HTTPS..."); + url.set_scheme("https") + .map_err(UrlManipulation)?; + + log::trace!("Attempting to retrieve nodeinfo via HTTPS..."); + let https = Self::get(url.clone()) + .await; + + let https = match https { + Ok(data) => { + log::trace!("HTTPS retrieval was successful, returning..."); + return Ok(data) + } + Err(err) => { + log::warn!("HTTPS retrieval failed."); + err + } + }; + + log::trace!("Setting URL scheme to HTTP..."); + url.set_scheme("http") + .map_err(UrlManipulation)?; + + log::trace!("Attempting to retrieve nodeinfo via HTTP..."); + let http = Self::get(url.clone()) + .await; + + let http = match http { + Ok(data) => { + log::trace!("HTTP retrieval was successful, returning..."); + return Ok(data) + } + Err(err) => { + log::warn!("HTTP retrieval failed."); + err + } + }; + + Err( + DiscoveryDiscoverError::Fetch( + DiscoveryDiscoverAttemptsErrors { + https, + http, + } + ) + ) + } +} + +pub enum DiscoveryGetError { + /// The HTTP request failed. + Request(reqwest::Error), + /// The `Content-Type` header of the response is missing. + ContentTypeMissing, + /// The `Content-Type` header of the response is invalid. + ContentTypeInvalid, + /// The JSON document failed to be parsed. + Parse(reqwest::Error), +} + +pub enum DiscoveryDiscoverError { + /// Manipulation of the URL scheme of the given base failed. + /// + /// See [reqwest::Url::set_scheme] for possible causes. + UrlManipulation(()), + + /// All attempts of fetching the discovery metadata failed. + Fetch(DiscoveryDiscoverAttemptsErrors), +} + +pub struct DiscoveryDiscoverAttemptsErrors { + /// The error occurred during the HTTPS request. + pub https: DiscoveryGetError, + + /// The error occurred during the HTTP request. + pub http: DiscoveryGetError, }