use bytes::Bytes;
use failure::Fail;
use reqwest::{header::CONTENT_LENGTH, RequestBuilder, Response};
use std::{borrow::Cow, io::Cursor};
use crate::{Digest, ShaSize};
#[derive(Debug, Fail)]
#[fail(display = "Failed to verify digest")]
pub struct VerifyError;
pub struct DigestBody {
inner: Bytes,
}
impl From<Bytes> for DigestBody {
fn from(b: Bytes) -> Self {
DigestBody { inner: b }
}
}
impl From<Vec<u8>> for DigestBody {
fn from(b: Vec<u8>) -> Self {
DigestBody { inner: b.into() }
}
}
impl From<&'static [u8]> for DigestBody {
fn from(b: &'static [u8]) -> Self {
DigestBody { inner: b.into() }
}
}
impl From<Cow<'static, [u8]>> for DigestBody {
fn from(b: Cow<'static, [u8]>) -> Self {
DigestBody {
inner: b.into_owned().into(),
}
}
}
impl From<String> for DigestBody {
fn from(b: String) -> Self {
DigestBody { inner: b.into() }
}
}
impl From<&'static str> for DigestBody {
fn from(b: &'static str) -> Self {
DigestBody { inner: b.into() }
}
}
impl From<Cow<'static, str>> for DigestBody {
fn from(b: Cow<'static, str>) -> Self {
DigestBody {
inner: b.into_owned().into(),
}
}
}
pub trait VerifyDigest {
fn verify_digest(&mut self) -> Result<Vec<u8>, VerifyError>;
}
pub trait WithDigest {
fn with_digest<B: Into<DigestBody>>(self, body: B, sha_size: ShaSize) -> RequestBuilder;
}
impl WithDigest for RequestBuilder {
fn with_digest<B: Into<DigestBody>>(self, body: B, sha_size: ShaSize) -> RequestBuilder {
let body: DigestBody = body.into();
let bytes = body.inner;
let digest = Digest::new(&bytes, sha_size);
self.header("Digest", digest.as_string())
.header(CONTENT_LENGTH, bytes.len())
.body(bytes.to_vec())
}
}
impl VerifyDigest for Response {
fn verify_digest(&mut self) -> Result<Vec<u8>, VerifyError> {
let mut c = Cursor::new(Vec::new());
let digest: Digest = self
.headers()
.get("Digest")
.ok_or(VerifyError)?
.to_str()
.map_err(|_| VerifyError)?
.parse()
.map_err(|_| VerifyError)?;
self.copy_to(&mut c).map_err(|_| VerifyError)?;
let v = c.into_inner();
digest.verify(&v).map(move |_| v).map_err(|_| VerifyError)
}
}
#[cfg(test)]
mod tests {
use reqwest::{header::CONTENT_TYPE, Client};
use crate::{prelude::*, ShaSize};
#[test]
fn add_digest_to_request_256() {
add_digest_to_request(ShaSize::TwoFiftySix);
}
#[test]
fn add_digest_to_request_384() {
add_digest_to_request(ShaSize::ThreeEightyFour);
}
#[test]
fn add_digest_to_request_512() {
add_digest_to_request(ShaSize::FiveTwelve);
}
fn add_digest_to_request(sha_size: ShaSize) {
let uri = "http://example.com";
let json = r#"{"Library":"Hyper"}"#;
Client::new()
.post(uri)
.header(CONTENT_TYPE, "application/json")
.with_digest(json, sha_size)
.build()
.unwrap();
}
}