diff --git a/src/util/http.rs b/src/util/http.rs index 0ce46af..f13f031 100644 --- a/src/util/http.rs +++ b/src/util/http.rs @@ -1,18 +1,30 @@ -use reqwest::header::{HeaderName, HeaderValue, AUTHORIZATION, USER_AGENT}; -use reqwest::{header::HeaderMap, RequestBuilder, Response}; -use std::collections::HashMap; +use reqwest::header::{HeaderName, AUTHORIZATION, USER_AGENT}; +use reqwest::{RequestBuilder, Response}; 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. +/// Use the [`crate::headers`] macro for the `headers` parameter. +/// +/// If `url` starts with `"bear:"`, it will be parsed as a bearcap URI +/// and automatically append an `Authorization` header to the request, +/// overwriting an authorization header that was passed explicitly as an +/// argument. The request will fail if parsing the bearcap failed. +/// +/// ## Default Headers +/// +/// By default, the API injects the following headers: +/// +/// - `User-Agent` +/// - `Authorization` (only when passing a bearcap URI) /// /// ## Example /// /// ``` +/// use reqwest::header::ACCEPT; +/// /// let response = get( /// &state, /// "https://www.example.com", @@ -24,12 +36,12 @@ use crate::util::bear::Bearcap; /// .await?; /// /// // automatically injects an "Authorization: Bearer b4dc0ffee" header -/// let response = get(&state, "bear:?t=b4dc0ffee&u=https://www.example.com", headers! {}).await?; +/// let response = get(&state, "bear:?t=b4dc0ffee&u=https://www.example.com", None).await?; /// ``` pub async fn get( state: &AppState, url: &str, - headers: &[(GenericHeaderName, String)], + headers: Option<&[(GenericHeaderName<'_>, String)]>, ) -> Result { let client = reqwest::Client::new(); let builder = if url.starts_with("bear:") { @@ -40,49 +52,57 @@ pub async fn get( } else { client.get(url) }; - perform_request(state, builder, headers).await + perform_request(state, builder, headers.unwrap_or(&[])).await } // the state reference will be used for caching async fn perform_request( _state: &AppState, mut builder: RequestBuilder, - headers: &[(GenericHeaderName, String)], + headers: &[(GenericHeaderName<'_>, String)], ) -> Result { - builder = headers.into_iter().fold(builder, |b, (k, v)| match k { + builder = headers.iter().fold(builder, |b, (k, v)| match k { GenericHeaderName::Standard(hdr) => b.header(hdr, v), - GenericHeaderName::Custom(hdr) => b.header(hdr, v), + GenericHeaderName::CustomOwned(hdr) => b.header(hdr, v), + GenericHeaderName::CustomBorrow(hdr) => b.header(*hdr, v), }); builder = builder.header(USER_AGENT, "nyano"); Ok(builder.send().await?) } +/// Generate a list of HTTP request headers for passing to [`get`]. #[macro_export] macro_rules! headers { ($($k:expr => $v:expr),* $(,)?) => {{ - &[$((crate::util::http::GenericHeaderName::from($k), ::std::string::String::from($v)),)*] + // The [..] is required because of a bug in the IntelliJ Rust plugin: + // https://github.com/intellij-rust/intellij-rust/issues/10005 + ::core::option::Option::Some(&[ + $(($crate::util::http::GenericHeaderName::from($k), ::std::string::String::from($v)),)* + ][..]) }} } -pub enum GenericHeaderName { - Custom(String), +/// Only used within this module, use [`crate::headers`] to generate headers. +pub enum GenericHeaderName<'a> { + CustomOwned(String), + CustomBorrow(&'a str), Standard(HeaderName), } -impl From for GenericHeaderName { - fn from(val: String) -> GenericHeaderName { - GenericHeaderName::Custom(val) +impl<'a> From for GenericHeaderName<'a> { + fn from(val: String) -> GenericHeaderName<'a> { + GenericHeaderName::CustomOwned(val) } } -impl From<&str> for GenericHeaderName { - fn from(val: &str) -> GenericHeaderName { - GenericHeaderName::Custom(String::from(val)) +impl<'a> From<&'a str> for GenericHeaderName<'a> { + fn from(val: &'a str) -> GenericHeaderName<'a> { + GenericHeaderName::CustomBorrow(val) } } -impl From for GenericHeaderName { - fn from(val: HeaderName) -> GenericHeaderName { +impl<'a> From for GenericHeaderName<'a> { + fn from(val: HeaderName) -> GenericHeaderName<'a> { GenericHeaderName::Standard(val) } }