From ac25b1d951cd30261928cfe3b4a185e72c33aa52 Mon Sep 17 00:00:00 2001 From: asonix Date: Thu, 6 Jul 2023 12:11:27 -0500 Subject: [PATCH] Run signature creation in spawn_blocking Also move -reqwest fully to async trait --- reqwest/Cargo.toml | 3 +- reqwest/src/digest/mod.rs | 161 +++++++++++++++++++------------------- reqwest/src/lib.rs | 72 +++++++++-------- 3 files changed, 120 insertions(+), 116 deletions(-) diff --git a/reqwest/Cargo.toml b/reqwest/Cargo.toml index 94ccd76..75e1521 100644 --- a/reqwest/Cargo.toml +++ b/reqwest/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "http-signature-normalization-reqwest" description = "An HTTP Signatures library that leaves the signing to you" -version = "0.8.0" +version = "0.9.0" authors = ["asonix "] license = "AGPL-3.0" readme = "README.md" @@ -22,6 +22,7 @@ name = "client" required-features = ["sha-2"] [dependencies] +async-trait = "0.1.71" base64 = { version = "0.13", optional = true } http-signature-normalization = { version = "0.7.0", path = ".." } httpdate = "1.0.2" diff --git a/reqwest/src/digest/mod.rs b/reqwest/src/digest/mod.rs index 8fab0f4..0957253 100644 --- a/reqwest/src/digest/mod.rs +++ b/reqwest/src/digest/mod.rs @@ -1,6 +1,6 @@ use crate::{Config, Sign, SignError}; use reqwest::{Body, Request, RequestBuilder}; -use std::{fmt::Display, future::Future, pin::Pin}; +use std::fmt::Display; #[cfg(feature = "sha-2")] mod sha2; @@ -21,107 +21,105 @@ pub trait DigestCreate { /// It generates HTTP Signatures after the Digest header has been added, in order to have /// verification that the body has not been tampered with, or that the request can't be replayed by /// a malicious entity +#[async_trait::async_trait] pub trait SignExt: Sign { - fn authorization_signature_with_digest( + async fn authorization_signature_with_digest( self, config: Config, key_id: K, digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized; - fn signature_with_digest( + async fn signature_with_digest( self, config: Config, key_id: K, digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized; } +#[async_trait::async_trait] impl SignExt for RequestBuilder { - fn authorization_signature_with_digest( + async fn authorization_signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, - Self: Sized, { - Box::pin(async move { - let (v, digest) = tokio::task::spawn_blocking(move || { - let digest = digest.compute(v.as_ref()); - (v, digest) - }) - .await - .map_err(|_| SignError::Canceled)?; - - let mut req = self - .header("Digest", format!("{}={}", D::NAME, digest)) - .authorization_signature(&config, key_id, f)?; - - *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); - - Ok(req) + let (v, digest) = tokio::task::spawn_blocking(move || { + let digest = digest.compute(v.as_ref()); + (v, digest) }) + .await + .map_err(|_| SignError::Canceled)?; + + let mut req = self + .header("Digest", format!("{}={}", D::NAME, digest)) + .authorization_signature(&config, key_id, f) + .await?; + + *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); + + Ok(req) } - fn signature_with_digest( + async fn signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, - Self: Sized, { - Box::pin(async move { - let (v, digest) = tokio::task::spawn_blocking(move || { - let digest = digest.compute(v.as_ref()); - (v, digest) - }) - .await - .map_err(|_| SignError::Canceled)?; - - let mut req = self - .header("Digest", format!("{}={}", D::NAME, digest)) - .signature(&config, key_id, f)?; - - *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); - - Ok(req) + let (v, digest) = tokio::task::spawn_blocking(move || { + let digest = digest.compute(v.as_ref()); + (v, digest) }) + .await + .map_err(|_| SignError::Canceled)?; + + let mut req = self + .header("Digest", format!("{}={}", D::NAME, digest)) + .signature(&config, key_id, f) + .await?; + + *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); + + Ok(req) } } @@ -130,75 +128,74 @@ mod middleware { use super::{Config, DigestCreate, Sign, SignError, SignExt}; use reqwest::{Body, Request}; use reqwest_middleware::RequestBuilder; - use std::{fmt::Display, future::Future, pin::Pin}; + use std::fmt::Display; + #[async_trait::async_trait] impl SignExt for RequestBuilder { - fn authorization_signature_with_digest( + async fn authorization_signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { - Box::pin(async move { - let (v, digest) = tokio::task::spawn_blocking(move || { - let digest = digest.compute(v.as_ref()); - (v, digest) - }) - .await - .map_err(|_| SignError::Canceled)?; - - let mut req = self - .header("Digest", format!("{}={}", D::NAME, digest)) - .authorization_signature(&config, key_id, f)?; - - *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); - - Ok(req) + let (v, digest) = tokio::task::spawn_blocking(move || { + let digest = digest.compute(v.as_ref()); + (v, digest) }) + .await + .map_err(|_| SignError::Canceled)?; + + let mut req = self + .header("Digest", format!("{}={}", D::NAME, digest)) + .authorization_signature(&config, key_id, f) + .await?; + + *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); + + Ok(req) } - fn signature_with_digest( + async fn signature_with_digest( self, config: Config, key_id: K, mut digest: D, v: V, f: F, - ) -> Pin> + Send>> + ) -> Result where F: FnOnce(&str) -> Result + Send + 'static, - E: From + From, + E: From + From + Send + 'static, K: Display + Send + 'static, D: DigestCreate + Send + 'static, V: AsRef<[u8]> + Into + Send + 'static, Self: Sized, { - Box::pin(async move { - let (v, digest) = tokio::task::spawn_blocking(move || { - let digest = digest.compute(v.as_ref()); - (v, digest) - }) - .await - .map_err(|_| SignError::Canceled)?; - - let mut req = self - .header("Digest", format!("{}={}", D::NAME, digest)) - .signature(&config, key_id, f)?; - - *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); - - Ok(req) + let (v, digest) = tokio::task::spawn_blocking(move || { + let digest = digest.compute(v.as_ref()); + (v, digest) }) + .await + .map_err(|_| SignError::Canceled)?; + + let mut req = self + .header("Digest", format!("{}={}", D::NAME, digest)) + .signature(&config, key_id, f) + .await?; + + *req.body_mut() = Some(Body::from(v.as_ref().to_vec())); + + Ok(req) } } } diff --git a/reqwest/src/lib.rs b/reqwest/src/lib.rs index fb892c3..9b23c33 100644 --- a/reqwest/src/lib.rs +++ b/reqwest/src/lib.rs @@ -39,9 +39,10 @@ pub struct Config { } /// A trait implemented by the reqwest RequestBuilder type to add an HTTP Signature to the request +#[async_trait::async_trait] pub trait Sign { /// Add an Authorization Signature to the request - fn authorization_signature( + async fn authorization_signature( self, config: &Config, key_id: K, @@ -49,17 +50,17 @@ pub trait Sign { ) -> Result where Self: Sized, - F: FnOnce(&str) -> Result, - E: From + From, - K: Display; + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send; /// Add a Signature to the request - fn signature(self, config: &Config, key_id: K, f: F) -> Result + async fn signature(self, config: &Config, key_id: K, f: F) -> Result where Self: Sized, - F: FnOnce(&str) -> Result, - E: From + From, - K: Display; + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send; } #[derive(Debug, thiserror::Error)] @@ -156,20 +157,21 @@ impl Config { } } +#[async_trait::async_trait] impl Sign for RequestBuilder { - fn authorization_signature( + async fn authorization_signature( self, config: &Config, key_id: K, f: F, ) -> Result where - F: FnOnce(&str) -> Result, - E: From + From, - K: Display, + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send, { let mut request = self.build()?; - let signed = prepare(&mut request, config, key_id, f)?; + let signed = prepare(&mut request, config, key_id, f).await?; let auth_header = signed.authorization_header(); request.headers_mut().insert( @@ -180,14 +182,14 @@ impl Sign for RequestBuilder { Ok(request) } - fn signature(self, config: &Config, key_id: K, f: F) -> Result + async fn signature(self, config: &Config, key_id: K, f: F) -> Result where - F: FnOnce(&str) -> Result, - E: From + From, - K: Display, + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send, { let mut request = self.build()?; - let signed = prepare(&mut request, config, key_id, f)?; + let signed = prepare(&mut request, config, key_id, f).await?; let sig_header = signed.signature_header(); @@ -200,11 +202,11 @@ impl Sign for RequestBuilder { } } -fn prepare(req: &mut Request, config: &Config, key_id: K, f: F) -> Result +async fn prepare(req: &mut Request, config: &Config, key_id: K, f: F) -> Result where - F: FnOnce(&str) -> Result, - E: From, - K: Display, + F: FnOnce(&str) -> Result + Send + 'static, + E: From + Send + 'static, + K: Display + Send, { if config.set_date && !req.headers().contains_key("date") { req.headers_mut().insert( @@ -246,7 +248,10 @@ where .begin_sign(req.method().as_str(), &path_and_query, bt) .map_err(SignError::from)?; - let signed = unsigned.sign(key_id.to_string(), f)?; + let key_string = key_id.to_string(); + let signed = tokio::task::spawn_blocking(move || unsigned.sign(key_string, f)) + .await + .map_err(|_| SignError::Canceled)??; Ok(signed) } @@ -257,20 +262,21 @@ mod middleware { use reqwest_middleware::RequestBuilder; use std::fmt::Display; + #[async_trait::async_trait] impl Sign for RequestBuilder { - fn authorization_signature( + async fn authorization_signature( self, config: &Config, key_id: K, f: F, ) -> Result where - F: FnOnce(&str) -> Result, - E: From + From, - K: Display, + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send, { let mut request = self.build()?; - let signed = prepare(&mut request, config, key_id, f)?; + let signed = prepare(&mut request, config, key_id, f).await?; let auth_header = signed.authorization_header(); request.headers_mut().insert( @@ -281,14 +287,14 @@ mod middleware { Ok(request) } - fn signature(self, config: &Config, key_id: K, f: F) -> Result + async fn signature(self, config: &Config, key_id: K, f: F) -> Result where - F: FnOnce(&str) -> Result, - E: From + From, - K: Display, + F: FnOnce(&str) -> Result + Send + 'static, + E: From + From + Send + 'static, + K: Display + Send, { let mut request = self.build()?; - let signed = prepare(&mut request, config, key_id, f)?; + let signed = prepare(&mut request, config, key_id, f).await?; let sig_header = signed.signature_header();