diff --git a/src/util/http.rs b/src/util/http.rs new file mode 100644 index 0000000..0ce46af --- /dev/null +++ b/src/util/http.rs @@ -0,0 +1,88 @@ +use reqwest::header::{HeaderName, HeaderValue, AUTHORIZATION, USER_AGENT}; +use reqwest::{header::HeaderMap, RequestBuilder, Response}; +use std::collections::HashMap; + +use crate::core::*; +use crate::middle::AuthData; +use crate::state::AppState; +use crate::util::bear::Bearcap; + +/// Perform an HTTP GET request to the specified URL (supports bearcaps). +/// Use the [`headers!`] macro for the `headers` parameter. +/// +/// ## Example +/// +/// ``` +/// let response = get( +/// &state, +/// "https://www.example.com", +/// headers! { +/// ACCEPT => "application/activity+json", +/// "X-Custom-Header" => "x-custom-value", +/// } +/// ) +/// .await?; +/// +/// // automatically injects an "Authorization: Bearer b4dc0ffee" header +/// let response = get(&state, "bear:?t=b4dc0ffee&u=https://www.example.com", headers! {}).await?; +/// ``` +pub async fn get( + state: &AppState, + url: &str, + headers: &[(GenericHeaderName, String)], +) -> Result { + let client = reqwest::Client::new(); + let builder = if url.starts_with("bear:") { + let bearcap = Bearcap::try_from(url)?; + client + .get(bearcap.url.as_str()) + .header(AUTHORIZATION, format!("Bearer {}", bearcap.token)) + } else { + client.get(url) + }; + perform_request(state, builder, headers).await +} + +// the state reference will be used for caching +async fn perform_request( + _state: &AppState, + mut builder: RequestBuilder, + headers: &[(GenericHeaderName, String)], +) -> Result { + builder = headers.into_iter().fold(builder, |b, (k, v)| match k { + GenericHeaderName::Standard(hdr) => b.header(hdr, v), + GenericHeaderName::Custom(hdr) => b.header(hdr, v), + }); + builder = builder.header(USER_AGENT, "nyano"); + Ok(builder.send().await?) +} + +#[macro_export] +macro_rules! headers { + ($($k:expr => $v:expr),* $(,)?) => {{ + &[$((crate::util::http::GenericHeaderName::from($k), ::std::string::String::from($v)),)*] + }} +} + +pub enum GenericHeaderName { + Custom(String), + Standard(HeaderName), +} + +impl From for GenericHeaderName { + fn from(val: String) -> GenericHeaderName { + GenericHeaderName::Custom(val) + } +} + +impl From<&str> for GenericHeaderName { + fn from(val: &str) -> GenericHeaderName { + GenericHeaderName::Custom(String::from(val)) + } +} + +impl From for GenericHeaderName { + fn from(val: HeaderName) -> GenericHeaderName { + GenericHeaderName::Standard(val) + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 2f8eef5..69adab2 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,4 +1,5 @@ pub mod bear; +pub mod http; pub mod password; pub mod token; pub mod validate;