diff --git a/src/lib.rs b/src/lib.rs index 71e0553..3333bf0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -104,9 +104,12 @@ This crate uses Semantic Versioning 2.0.0 as its versioning scheme. You can read #[cfg(all(feature = "rt", feature = "rt_multi_thread"))] compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time"); +use errors::ChorusResult; use serde::{Deserialize, Serialize}; use url::{ParseError, Url}; +use crate::errors::ChorusError; + #[cfg(feature = "client")] pub mod api; pub mod errors; @@ -168,7 +171,7 @@ impl UrlBundle { let url_fmt = format!("http://{}", url); return UrlBundle::parse_url(url_fmt); } - Err(_) => panic!("Invalid URL"), + Err(_) => panic!("Invalid URL"), // TODO: should not panic here }; // if the last character of the string is a slash, remove it. let mut url_string = url.to_string(); @@ -177,6 +180,52 @@ impl UrlBundle { } url_string } + + /// Performs a few HTTP requests to try and retrieve a `UrlBundle` from an instances' root domain. + /// The method tries to retrieve the `UrlBundle` via these three strategies, in order: + /// - GET: `$url/.well-known/spacebar` -> Retrieve UrlBundle via `$wellknownurl/api/policies/instance/domains` + /// - GET: `$url/api/policies/instance/domains` + /// - GET: `$url/policies/instance/domains` + /// + /// The URL stored at `.well-known/spacebar` is the instances' API endpoint. The API + /// stores the CDN and WSS URLs under the `$api/policies/instance/domains` endpoint. If all three + /// of the above approaches fail, it is very likely that the instance is misconfigured, unreachable, or that + /// a wrong URL was provided. + pub async fn from_root_domain(url: &str) -> ChorusResult { + let parsed = UrlBundle::parse_url(url.to_string()); + let client = reqwest::Client::new(); + let request_wellknown = client + .get(format!("{}/.well-known/spacebar", &parsed)) + .header(http::header::ACCEPT, "application/json") + .build()?; + let response_wellknown = client.execute(request_wellknown).await?; + if response_wellknown.status().is_success() { + let body = response_wellknown.json::().await?.api; + UrlBundle::from_api_url(&body).await + } else { + if let Ok(response_slash_api) = + UrlBundle::from_api_url(&format!("{}/api/policies/instance/domains", url)).await + { + return Ok(response_slash_api); + } + if let Ok(response_api) = + UrlBundle::from_api_url(&format!("{}/policies/instance/domains", url)).await + { + Ok(response_api) + } else { + Err(ChorusError::RequestFailed { url: url.to_string(), error: "Could not retrieve UrlBundle from url after trying 3 different approaches. Check the provided Url and if the instance is reachable.".to_string() } ) + } + } + } + + async fn from_api_url(url: &str) -> ChorusResult { + todo!() + } +} + +#[derive(Deserialize)] +struct WellKnownResponse { + pub(crate) api: String, } #[cfg(test)]