Don't use async-trait

This commit is contained in:
asonix 2020-03-30 00:53:45 -05:00
parent 20cad6bea8
commit a38b6aa1ed
7 changed files with 132 additions and 74 deletions

View file

@ -27,7 +27,6 @@ required-features = ["sha-2"]
[dependencies] [dependencies]
actix-web = "3.0.0-alpha.1" actix-web = "3.0.0-alpha.1"
actix-http = "2.0.0-alpha.2" actix-http = "2.0.0-alpha.2"
async-trait = "0.1.27"
base64 = { version = "0.11", optional = true } base64 = { version = "0.11", optional = true }
bytes = "0.5.4" bytes = "0.5.4"
chrono = "0.4.6" chrono = "0.4.6"

View file

@ -1,19 +1,20 @@
use actix_web::client::Client; use actix_web::{client::Client, error::BlockingError};
use http_signature_normalization_actix::prelude::*; use http_signature_normalization_actix::prelude::*;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::time::SystemTime; use std::time::SystemTime;
async fn request(config: Config) -> Result<(), Box<dyn std::error::Error>> { async fn request(config: Config) -> Result<(), Box<dyn std::error::Error>> {
let mut digest = Sha256::new(); let digest = Sha256::new();
let mut response = Client::default() let mut response = Client::default()
.post("http://127.0.0.1:8010/") .post("http://127.0.0.1:8010/")
.header("User-Agent", "Actix Web") .header("User-Agent", "Actix Web")
.set(actix_web::http::header::Date(SystemTime::now().into())) .set(actix_web::http::header::Date(SystemTime::now().into()))
.signature_with_digest(&config, "my-key-id", &mut digest, "Hewwo-owo", |s| { .signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
println!("Signing String\n{}", s); println!("Signing String\n{}", s);
Ok(base64::encode(s)) as Result<_, MyError> Ok(base64::encode(s)) as Result<_, MyError>
})? })
.await?
.send() .send()
.await .await
.map_err(|e| { .map_err(|e| {
@ -55,4 +56,16 @@ pub enum MyError {
#[error("Failed to retrieve request body")] #[error("Failed to retrieve request body")]
Body, Body,
#[error("Blocking operation was canceled")]
Canceled,
}
impl From<BlockingError<MyError>> for MyError {
fn from(b: BlockingError<MyError>) -> Self {
match b {
BlockingError::Error(e) => e,
_ => MyError::Canceled,
}
}
} }

View file

@ -1,4 +1,7 @@
use actix_web::{http::StatusCode, web, App, HttpRequest, HttpResponse, HttpServer, ResponseError}; use actix_web::{
http::StatusCode, middleware::Logger, web, App, HttpRequest, HttpResponse, HttpServer,
ResponseError,
};
use futures::future::{err, ok, Ready}; use futures::future::{err, ok, Ready};
use http_signature_normalization_actix::prelude::*; use http_signature_normalization_actix::prelude::*;
use log::info; use log::info;
@ -59,6 +62,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
App::new() App::new()
.wrap(VerifyDigest::new(Sha256::new()).optional()) .wrap(VerifyDigest::new(Sha256::new()).optional())
.wrap(VerifySignature::new(MyVerify, config.clone()).optional()) .wrap(VerifySignature::new(MyVerify, config.clone()).optional())
.wrap(Logger::default())
.route("/", web::post().to(index)) .route("/", web::post().to(index))
}) })
.bind("127.0.0.1:8010")? .bind("127.0.0.1:8010")?

View file

@ -10,7 +10,7 @@ use actix_web::{
error::BlockingError, error::BlockingError,
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
}; };
use std::{fmt::Display, future::Future}; use std::{fmt::Display, future::Future, pin::Pin};
use crate::{Config, Sign}; use crate::{Config, Sign};
@ -44,17 +44,16 @@ pub trait DigestVerify {
/// It generates HTTP Signatures after the Digest header has been added, in order to have /// 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 /// verification that the body has not been tampered with, or that the request can't be replayed by
/// a malicious entity /// a malicious entity
#[async_trait::async_trait(?Send)]
pub trait SignExt: Sign { pub trait SignExt: Sign {
/// Set the Digest and Authorization headers on the request /// Set the Digest and Authorization headers on the request
async fn authorization_signature_with_digest<F, E, K, D, V>( fn authorization_signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: Config,
key_id: K, key_id: K,
digest: &mut D, digest: D,
v: V, v: V,
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -63,20 +62,20 @@ pub trait SignExt: Sign {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
D: DigestCreate, D: DigestCreate + Send + 'static,
V: AsRef<[u8]>, V: AsRef<[u8]> + Send + 'static,
Self: Sized; Self: Sized;
/// Set the Digest and Signature headers on the request /// Set the Digest and Signature headers on the request
async fn signature_with_digest<F, E, K, D, V>( fn signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: Config,
key_id: K, key_id: K,
digest: &mut D, digest: D,
v: V, v: V,
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -85,9 +84,9 @@ pub trait SignExt: Sign {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
D: DigestCreate, D: DigestCreate + Send + 'static,
V: AsRef<[u8]>, V: AsRef<[u8]> + Send + 'static,
Self: Sized; Self: Sized;
} }

View file

@ -2,24 +2,24 @@ use actix_web::{
client::ClientRequest, client::ClientRequest,
error::BlockingError, error::BlockingError,
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
web,
}; };
use std::fmt::Display; use std::{fmt::Display, future::Future, pin::Pin};
use crate::{ use crate::{
digest::{DigestClient, DigestCreate, SignExt}, digest::{DigestClient, DigestCreate, SignExt},
Config, Sign, Config, Sign,
}; };
#[async_trait::async_trait(?Send)]
impl SignExt for ClientRequest { impl SignExt for ClientRequest {
async fn authorization_signature_with_digest<F, E, K, D, V>( fn authorization_signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: Config,
key_id: K, key_id: K,
digest: &mut D, mut digest: D,
v: V, v: V,
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -28,27 +28,35 @@ impl SignExt for ClientRequest {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
D: DigestCreate, D: DigestCreate + Send + 'static,
V: AsRef<[u8]>, V: AsRef<[u8]> + Send + 'static,
Self: Sized, Self: Sized,
{ {
let digest = digest.compute(v.as_ref()); Box::pin(async move {
let (d, v) = web::block(move || {
let d = digest.compute(v.as_ref());
Ok((d, v)) as Result<(String, V), E>
})
.await?;
self.set_header("Digest", format!("{}={}", D::NAME, digest)) let c = self
.authorization_signature(config, key_id, f) .set_header("Digest", format!("{}={}", D::NAME, d))
.await .authorization_signature(config, key_id, f)
.map(|c| DigestClient::new(c, v)) .await?;
Ok(DigestClient::new(c, v))
})
} }
async fn signature_with_digest<F, E, K, D, V>( fn signature_with_digest<F, E, K, D, V>(
self, self,
config: &Config, config: Config,
key_id: K, key_id: K,
digest: &mut D, mut digest: D,
v: V, v: V,
f: F, f: F,
) -> Result<DigestClient<V>, E> ) -> Pin<Box<dyn Future<Output = Result<DigestClient<V>, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -57,16 +65,24 @@ impl SignExt for ClientRequest {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
D: DigestCreate, D: DigestCreate + Send + 'static,
V: AsRef<[u8]>, V: AsRef<[u8]> + Send + 'static,
Self: Sized, Self: Sized,
{ {
let digest = digest.compute(v.as_ref()); Box::pin(async move {
let (d, v) = web::block(move || {
let d = digest.compute(v.as_ref());
Ok((d, v)) as Result<(String, V), E>
})
.await?;
self.set_header("Digest", format!("{}={}", D::NAME, digest)) let c = self
.signature(config, key_id, f) .set_header("Digest", format!("{}={}", D::NAME, d))
.await .signature(config, key_id, f)
.map(|c| DigestClient::new(c, v)) .await?;
Ok(DigestClient::new(c, v))
})
} }
} }

View file

@ -99,21 +99,24 @@
//! //!
//! ### Use it in a client //! ### Use it in a client
//! ```rust,ignore //! ```rust,ignore
//! use actix_web::client::Client; //! use actix_web::{client::Client, error::BlockingError};
//! use http_signature_normalization_actix::prelude::*; //! use http_signature_normalization_actix::prelude::*;
//! use sha2::{Digest, Sha256}; //! use sha2::{Digest, Sha256};
//! //!
//! #[actix_rt::main] //! #[actix_rt::main]
//! async fn main() -> Result<(), Box<dyn std::error::Error>> { //! async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let config = Config::default(); //! let config = Config::default();
//! let mut digest = Sha256::new(); //! let digest = Sha256::new();
//! //!
//! let mut response = Client::default() //! let mut response = Client::default()
//! .post("http://127.0.0.1:8010/") //! .post("http://127.0.0.1:8010/")
//! .header("User-Agent", "Actix Web") //! .header("User-Agent", "Actix Web")
//! .authorization_signature_with_digest(&config, "my-key-id", &mut digest, "Hewwo-owo", |s| { //! .set(actix_web::http::header::Date(SystemTime::now().into()))
//! .signature_with_digest(config, "my-key-id", digest, "Hewwo-owo", |s| {
//! println!("Signing String\n{}", s);
//! Ok(base64::encode(s)) as Result<_, MyError> //! Ok(base64::encode(s)) as Result<_, MyError>
//! })? //! })
//! .await?
//! .send() //! .send()
//! .await //! .await
//! .map_err(|e| { //! .map_err(|e| {
@ -143,6 +146,18 @@
//! //!
//! #[error("Failed to retrieve request body")] //! #[error("Failed to retrieve request body")]
//! Body, //! Body,
//!
//! #[error("Blocking operation was canceled")]
//! Canceled,
//! }
//!
//! impl From<BlockingError<MyError>> for MyError {
//! fn from(b: BlockingError<MyError>) -> Self {
//! match b {
//! BlockingError::Error(e) => e,
//! _ => MyError::Canceled,
//! }
//! }
//! } //! }
//! ``` //! ```
@ -155,7 +170,7 @@ use actix_web::{
}, },
}; };
use chrono::Duration; use chrono::Duration;
use std::{collections::BTreeMap, fmt::Display, future::Future}; use std::{collections::BTreeMap, fmt::Display, future::Future, pin::Pin};
mod sign; mod sign;
@ -216,15 +231,14 @@ pub trait SignatureVerify {
} }
/// A trait implemented by the Actix Web ClientRequest type to add an HTTP signature to the request /// A trait implemented by the Actix Web ClientRequest type to add an HTTP signature to the request
#[async_trait::async_trait(?Send)]
pub trait Sign { pub trait Sign {
/// Add an Authorization Signature to the request /// Add an Authorization Signature to the request
async fn authorization_signature<F, E, K>( fn authorization_signature<F, E, K>(
self, self,
config: &Config, config: Config,
key_id: K, key_id: K,
f: F, f: F,
) -> Result<Self, E> ) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -233,11 +247,16 @@ pub trait Sign {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
Self: Sized; Self: Sized;
/// Add a Signature to the request /// Add a Signature to the request
async fn signature<F, E, K>(self, config: &Config, key_id: K, f: F) -> Result<Self, E> fn signature<F, E, K>(
self,
config: Config,
key_id: K,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -246,7 +265,7 @@ pub trait Sign {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
Self: Sized; Self: Sized;
} }

View file

@ -4,18 +4,17 @@ use actix_web::{
http::header::{InvalidHeaderValue, ToStrError}, http::header::{InvalidHeaderValue, ToStrError},
web, web,
}; };
use std::fmt::Display; use std::{fmt::Display, future::Future, pin::Pin};
use crate::{create::Signed, Config, Sign}; use crate::{create::Signed, Config, Sign};
#[async_trait::async_trait(?Send)]
impl Sign for ClientRequest { impl Sign for ClientRequest {
async fn authorization_signature<F, E, K>( fn authorization_signature<F, E, K>(
mut self, mut self,
config: &Config, config: Config,
key_id: K, key_id: K,
f: F, f: F,
) -> Result<Self, E> ) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -24,15 +23,22 @@ impl Sign for ClientRequest {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
Self: Sized, Self: Sized,
{ {
let signed = prepare(&self, config, key_id, f).await?; Box::pin(async move {
signed.authorization_header(self.headers_mut())?; let signed = prepare(&self, &config, key_id, f).await?;
Ok(self) signed.authorization_header(self.headers_mut())?;
Ok(self)
})
} }
async fn signature<F, E, K>(mut self, config: &Config, key_id: K, f: F) -> Result<Self, E> fn signature<F, E, K>(
mut self,
config: Config,
key_id: K,
f: F,
) -> Pin<Box<dyn Future<Output = Result<Self, E>>>>
where where
F: FnOnce(&str) -> Result<String, E> + Send + 'static, F: FnOnce(&str) -> Result<String, E> + Send + 'static,
E: From<BlockingError<E>> E: From<BlockingError<E>>
@ -41,12 +47,14 @@ impl Sign for ClientRequest {
+ std::fmt::Debug + std::fmt::Debug
+ Send + Send
+ 'static, + 'static,
K: Display, K: Display + 'static,
Self: Sized, Self: Sized,
{ {
let signed = prepare(&self, config, key_id, f).await?; Box::pin(async move {
signed.signature_header(self.headers_mut())?; let signed = prepare(&self, &config, key_id, f).await?;
Ok(self) signed.signature_header(self.headers_mut())?;
Ok(self)
})
} }
} }