diff --git a/src/common/content_md5.rs b/src/common/content_md5.rs new file mode 100644 index 00000000..8cc43ad9 --- /dev/null +++ b/src/common/content_md5.rs @@ -0,0 +1,55 @@ +use std::convert::TryInto; +use {Header, HeaderValue}; + +/// `Content-MD5` header, defined in +/// [RFC1864](https://datatracker.ietf.org/doc/html/rfc1864) +/// +/// ## ABNF +/// +/// ```text +/// Content-Length = 1*DIGIT +/// ``` +/// +/// ## Example values +/// +/// * `Q2hlY2sgSW50ZWdyaXR5IQ==` +/// +/// # Example +/// +/// ``` +/// # extern crate headers; +/// use headers::ContentMd5; +/// +/// let md5 = ContentMd5([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5]); +/// ``` +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct ContentMd5(pub [u8; 16]); + +static CONTENT_MD5: ::http::header::HeaderName = + ::http::header::HeaderName::from_static("content-md5"); + +impl Header for ContentMd5 { + fn name() -> &'static ::http::header::HeaderName { + &CONTENT_MD5 + } + + fn decode<'i, I: Iterator>(values: &mut I) -> Result { + let value = values.next().ok_or_else(::Error::invalid)?; + + // Ensure base64 encoded length fits the expected MD5 digest length. + if value.len() < 22 || value.len() > 24 { + return Err(::Error::invalid()); + } + + let value = value.to_str().map_err(|_| ::Error::invalid())?; + let vec = base64::decode(value).map_err(|_| ::Error::invalid())?; + Ok(Self(vec.try_into().map_err(|_| ::Error::invalid())?)) + } + + fn encode>(&self, values: &mut E) { + let encoded = base64::encode(self.0); + if let Ok(value) = HeaderValue::from_str(&encoded) { + values.extend(std::iter::once(value)); + } + } +} diff --git a/src/common/mod.rs b/src/common/mod.rs index 2237ae8e..a0720701 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -29,6 +29,7 @@ pub use self::content_encoding::ContentEncoding; //pub use self::content_language::ContentLanguage; pub use self::content_length::ContentLength; pub use self::content_location::ContentLocation; +pub use self::content_md5::ContentMd5; pub use self::content_range::ContentRange; pub use self::content_type::ContentType; pub use self::cookie::Cookie; @@ -149,6 +150,7 @@ mod content_encoding; //mod content_language; mod content_length; mod content_location; +mod content_md5; mod content_range; mod content_type; mod cookie;