http_content_digest/digest/
request.rs

1use bytes::Bytes;
2use http::Request;
3use http_body_util::combinators::BoxBody;
4use http_body_util::{BodyExt, Full};
5
6use super::ContentDigest;
7use crate::digest::header::{self, CONTENT_DIGEST};
8use crate::digest::{BodyDigest, ContentHasher};
9use crate::errors::DigestError;
10
11impl<B> ContentDigest for Request<B>
12where
13    B: http_body::Body + Send,
14    B::Data: Send,
15{
16    type Error = DigestError;
17    type Content = Request<BoxBody<Bytes, DigestError>>;
18
19    async fn digest<H: ContentHasher>(self) -> Result<Self::Content, Self::Error> {
20        let (mut parts, body) = self.into_parts();
21        let actual = body.digest::<H>().await.map_err(|_e| DigestError::Body)?;
22
23        let body = Full::new(actual.body)
24            .map_err(|infallible| match infallible {})
25            .boxed();
26
27        parts.headers.insert(
28            CONTENT_DIGEST,
29            format!("{}={}", H::DIGEST_ALG, actual.digest.to_base64().to_sfv())
30                .parse()
31                .unwrap(),
32        );
33
34        Ok(Request::from_parts(parts, body))
35    }
36
37    async fn verify_digest<H: ContentHasher>(self) -> Result<Self::Content, Self::Error> {
38        let (parts, body) = self.into_parts();
39        let Some(expect) = header::ContentDigest::from_header(&parts.headers)?.find(H::DIGEST_ALG)
40        else {
41            return Err(DigestError::AlgorithmNotSupported);
42        };
43
44        let actual = body.digest::<H>().await.map_err(|_e| DigestError::Body)?;
45
46        if actual.digest != expect {
47            return Err(DigestError::Mismatch);
48        }
49
50        let body = Full::new(actual.body)
51            .map_err(|infallible| match infallible {})
52            .boxed();
53
54        Ok(Request::from_parts(parts, body))
55    }
56}