http-signature-normalization-actix 0.12.0

An HTTP Signatures library that leaves the signing to you
Documentation
use crate::{create::Signed, Config, PrepareSignError, Sign, Spawn};
use actix_rt::task::JoinError;
use awc::{
    http::header::{HttpDate, InvalidHeaderValue, TryIntoHeaderValue},
    ClientRequest,
};
use std::{fmt::Display, future::Future, pin::Pin, time::SystemTime};

impl Sign for ClientRequest {
    fn authorization_signature<F, E, K, S>(
        mut self,
        config: Config<S>,
        key_id: K,
        f: F,
    ) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
    where
        F: FnOnce(&str) -> Result<String, E> + Send + 'static,
        E: From<JoinError>
            + From<PrepareSignError>
            + From<crate::Canceled>
            + From<InvalidHeaderValue>
            + std::fmt::Debug
            + Send
            + 'static,
        K: Display + 'static,
        S: Spawn + 'static,
        Self: Sized,
    {
        Box::pin(async move {
            let signed = prepare(&mut self, &config, key_id, f).await?;
            signed.authorization_header(self.headers_mut())?;
            Ok(self)
        })
    }

    fn signature<F, E, K, S>(
        mut self,
        config: Config<S>,
        key_id: K,
        f: F,
    ) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
    where
        F: FnOnce(&str) -> Result<String, E> + Send + 'static,
        E: From<JoinError>
            + From<PrepareSignError>
            + From<InvalidHeaderValue>
            + From<crate::Canceled>
            + std::fmt::Debug
            + Send
            + 'static,
        K: Display + 'static,
        S: Spawn + 'static,
        Self: Sized,
    {
        Box::pin(async move {
            let signed = prepare(&mut self, &config, key_id, f).await?;
            signed.signature_header(self.headers_mut())?;
            Ok(self)
        })
    }
}

async fn prepare<F, E, K, S>(
    request: &mut ClientRequest,
    config: &Config<S>,
    key_id: K,
    f: F,
) -> Result<Signed, E>
where
    F: FnOnce(&str) -> Result<String, E> + Send + 'static,
    E: From<JoinError>
        + From<PrepareSignError>
        + From<crate::Canceled>
        + std::fmt::Debug
        + Send
        + 'static,
    K: Display,
    S: Spawn + 'static,
{
    if config.set_date && !request.headers().contains_key("date") {
        request.headers_mut().insert(
            actix_http::header::DATE,
            HttpDate::from(SystemTime::now())
                .try_into_value()
                .expect("Date is valid"),
        );
    }

    let mut headers = request.headers().clone();
    if config.set_host {
        let header_string = request
            .get_uri()
            .host()
            .ok_or_else(|| PrepareSignError::Host(request.get_uri().to_string()))?
            .to_string();

        let header_string = match request.get_uri().port().map(|p| p.as_u16()) {
            None | Some(443) | Some(80) => header_string,
            Some(port) => format!("{}:{}", header_string, port),
        };
        headers.insert(
            "Host".parse().unwrap(),
            header_string
                .parse()
                .map_err(|_| PrepareSignError::Host(request.get_uri().to_string()))?,
        );
    }
    let unsigned = config.begin_sign(
        request.get_method(),
        request.get_uri().path_and_query(),
        headers,
    )?;

    let key_id = key_id.to_string();

    let signed = config
        .spawner
        .spawn_blocking(move || unsigned.sign(key_id, f))
        .await??;

    Ok(signed)
}