use serde::Deserialize; #[derive(Debug, Clone, Deserialize)] pub struct Discovery { pub links: Vec, } #[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, }