http-msgsign-draft 0.3.0

Add functionality for draft-cavage-http-signatures-12 to http.
Documentation
use bytes::Bytes;
use http::Response;
use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full};
use http_content_digest::errors::DigestError;
use http_content_digest::{BodyDigest, ContentHasher};

use crate::digest::header::{self, DIGEST};
use crate::digest::Digest;

impl<B> Digest for Response<B>
where
    B: http_body::Body + Send,
    B::Data: Send,
{
    type Error = DigestError;
    type Content = Response<BoxBody<Bytes, DigestError>>;
    
    async fn digest<H: ContentHasher>(self) -> Result<Self::Content, Self::Error> {
        let (mut parts, body) = self.into_parts();
        let actual = body.digest::<H>().await.map_err(|_e| DigestError::Body)?;
        
        let body = Full::new(actual.body)
            .map_err(|infallible| match infallible {})
            .boxed();
        
        parts.headers.insert(
            DIGEST,
            format!("{}={}", H::DIGEST_ALG, actual.digest.to_base64())
                .parse()
                .unwrap(),
        );
        
        Ok(Response::from_parts(parts, body))
    }
    
    async fn verify_digest<H: ContentHasher>(self) -> Result<Self::Content, Self::Error> {
        let (parts, body) = self.into_parts();
        let expect = header::Digest::from_header(&parts.headers)?;
        
        if expect.alg != H::DIGEST_ALG {
            return Err(DigestError::AlgorithmNotSupported);
        };
        
        let actual = body.digest::<H>().await.map_err(|_e| DigestError::Body)?;
        
        if actual.digest != expect.digest {
            return Err(DigestError::Mismatch);
        }
        
        let body = Full::new(actual.body)
            .map_err(|infallible| match infallible {})
            .boxed();
        
        Ok(Response::from_parts(parts, body))
    }
}