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( mut self, config: Config, key_id: K, f: F, ) -> Pin>>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From + From + From + 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( mut self, config: Config, key_id: K, f: F, ) -> Pin>>> where F: FnOnce(&str) -> Result + Send + 'static, E: From + From + From + From + 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( request: &mut ClientRequest, config: &Config, key_id: K, f: F, ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, E: From + From + From + 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) }