Run signature creation in spawn_blocking

Also move -reqwest fully to async trait
This commit is contained in:
asonix 2023-07-06 12:11:27 -05:00
parent c7cb3ce602
commit ac25b1d951
3 changed files with 120 additions and 116 deletions

View file

@ -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 <asonix@asonix.dog>"]
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"

View file

@ -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<F, E, K, D, V>(
async fn authorization_signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + Send + 'static,
Self: Sized;
fn signature_with_digest<F, E, K, D, V>(
async fn signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + Send + 'static,
Self: Sized;
}
#[async_trait::async_trait]
impl SignExt for RequestBuilder {
fn authorization_signature_with_digest<F, E, K, D, V>(
async fn authorization_signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
mut digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + 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<F, E, K, D, V>(
async fn signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
mut digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + 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<F, E, K, D, V>(
async fn authorization_signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
mut digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + 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<F, E, K, D, V>(
async fn signature_with_digest<F, E, K, D, V>(
self,
config: Config,
key_id: K,
mut digest: D,
v: V,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Request, E>> + Send>>
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error>,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send + 'static,
D: DigestCreate + Send + 'static,
V: AsRef<[u8]> + Into<Body> + 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)
}
}
}

View file

@ -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<F, E, K>(
async fn authorization_signature<F, E, K>(
self,
config: &Config,
key_id: K,
@ -49,17 +50,17 @@ pub trait Sign {
) -> Result<Request, E>
where
Self: Sized,
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display;
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + Send + 'static,
K: Display + Send;
/// Add a Signature to the request
fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
async fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
where
Self: Sized,
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display;
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + 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<F, E, K>(
async fn authorization_signature<F, E, K>(
self,
config: &Config,
key_id: K,
f: F,
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display,
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + 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<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
async fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display,
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + 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<F, E, K>(req: &mut Request, config: &Config, key_id: K, f: F) -> Result<Signed, E>
async fn prepare<F, E, K>(req: &mut Request, config: &Config, key_id: K, f: F) -> Result<Signed, E>
where
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError>,
K: Display,
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + 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<F, E, K>(
async fn authorization_signature<F, E, K>(
self,
config: &Config,
key_id: K,
f: F,
) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display,
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + 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<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
async fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Request, E>
where
F: FnOnce(&str) -> Result<String, E>,
E: From<SignError> + From<reqwest::Error>,
K: Display,
F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<SignError> + From<reqwest::Error> + 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();