hyper_req_exts 0.18.0

extension helpers for hyper handlers
Documentation
use base64::{prelude::BASE64_STANDARD as b64, Engine};
use hyper::{body::HttpBody, Body};
use tap::Pipe;

use crate::BoxFuture;

pub trait ReqExt<'a> {
    #[cfg(feature = "urlencoded")]
    fn body_urlencoded<T: serde::de::DeserializeOwned>(
        &'a mut self,
    ) -> BoxFuture<'a, crate::Result<T>>;
    #[cfg(feature = "json")]
    fn body_json<T: serde::de::DeserializeOwned>(&'a mut self) -> BoxFuture<'a, crate::Result<T>>;
    fn body_raw_bytes(&'a mut self) -> BoxFuture<'a, crate::Result<Vec<u8>>>;
    fn body_raw_bytes_with_max_size(
        &'a mut self,
        size: u64,
    ) -> BoxFuture<'a, crate::Result<Vec<u8>>>;
    fn basic_auth(&'a self) -> Option<(String, String)>;
    fn bearer_auth(&'a self) -> Option<String>;
    fn body_text(&'a mut self) -> BoxFuture<'a, crate::Result<String>>;
}
impl<'a> ReqExt<'a> for hyper::Request<Body> {
    #[cfg(feature = "urlencoded")]
    fn body_urlencoded<T: serde::de::DeserializeOwned>(
        &'a mut self,
    ) -> BoxFuture<'a, crate::Result<T>> {
        Box::pin(async move {
            serde_urlencoded::from_bytes(&self.body_raw_bytes().await?).map_err(Into::into)
        })
    }
    #[cfg(feature = "json")]
    fn body_json<T: serde::de::DeserializeOwned>(&'a mut self) -> BoxFuture<'a, crate::Result<T>> {
        Box::pin(async move {
            serde_json::from_slice(&self.body_raw_bytes().await?).map_err(Into::into)
        })
    }
    fn body_raw_bytes(&'a mut self) -> BoxFuture<'a, crate::Result<Vec<u8>>> {
        self.body_raw_bytes_with_max_size(1024)
    }
    fn body_raw_bytes_with_max_size(
        &'a mut self,
        max_size: u64,
    ) -> BoxFuture<'a, crate::Result<Vec<u8>>> {
        Box::pin(async move {
            let size = self.size_hint();
            match size.upper() {
                Some(upper) if upper > max_size => {
                    Err(std::io::Error::new(
                        std::io::ErrorKind::Other,
                        "body too large",
                    ))?;
                }
                None => {
                    Err(std::io::Error::new(
                        std::io::ErrorKind::Other,
                        "body too large",
                    ))?;
                }
                _ => {}
            }
            hyper::body::to_bytes(self.body_mut())
                .await?
                .to_vec()
                .pipe(Ok)
        })
    }

    fn basic_auth(&'a self) -> Option<(String, String)> {
        self.headers().get("Authorization").and_then(|h| {
            let s = h.to_str().ok()?;
            b64.decode(s.strip_prefix("Basic ")?)
                .ok()?
                .as_slice()
                .pipe(String::from_utf8_lossy)
                .split(':')
                .pipe(|mut s| {
                    let uname = s.next()?;
                    let pwd = s.next()?;
                    Some((uname.to_string(), pwd.to_string()))
                })
        })
    }
    fn bearer_auth(&'a self) -> Option<String> {
        self.headers().get("Authorization").and_then(|h| {
            let s = h.to_str().ok()?;
            s.strip_prefix("Bearer ")?;
            Some(s.to_string())
        })
    }
    fn body_text(&'a mut self) -> BoxFuture<'a, crate::Result<String>> {
        Box::pin(async move {
            let body = self.body_raw_bytes().await?;
            Ok(String::from_utf8_lossy(&body).to_string())
        })
    }
}