headers_content_md5/
lib.rs1#![deny(unsafe_code)]
16#![deny(unused_must_use)]
17
18use base64::{engine::general_purpose::STANDARD as base64, Engine};
19use headers::{Header, HeaderValue};
20
21#[derive(Clone, Copy, Debug, PartialEq)]
59pub struct ContentMd5(pub [u8; 16]);
60
61static CONTENT_MD5: http::header::HeaderName = http::header::HeaderName::from_static("content-md5");
62
63impl Header for ContentMd5 {
64 fn name() -> &'static http::header::HeaderName {
65 &CONTENT_MD5
66 }
67
68 fn decode<'i, I: Iterator<Item = &'i HeaderValue>>(
69 values: &mut I,
70 ) -> Result<Self, headers::Error> {
71 let value = values.next().ok_or_else(headers::Error::invalid)?;
72
73 if value.len() < 22 || value.len() > 24 {
75 return Err(headers::Error::invalid());
76 }
77
78 let value = value.to_str().map_err(|_| headers::Error::invalid())?;
79 let mut buffer = [0; 18];
80 base64
81 .decode_slice(value, &mut buffer)
82 .map_err(|_| headers::Error::invalid())?;
83 let mut slice = [0; 16];
84 slice.copy_from_slice(&buffer[..16]);
85 Ok(Self(slice))
86 }
87
88 fn encode<E: Extend<HeaderValue>>(&self, values: &mut E) {
89 let encoded = base64.encode(self.0);
90 if let Ok(value) = HeaderValue::from_str(&encoded) {
91 values.extend(std::iter::once(value));
92 }
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use crate::ContentMd5;
99 use headers::Header;
100 use http::HeaderValue;
101
102 #[test]
103 fn decode_works() {
104 let value = HeaderValue::from_static("Q2hlY2sgSW50ZWdyaXR5IQ==");
105 let md5 = ContentMd5::decode(&mut [&value].into_iter()).unwrap();
106 assert_eq!(md5.0, "Check Integrity!".as_bytes())
107 }
108
109 #[test]
110 fn encode_works() {
111 let md5 = ContentMd5("Check Integrity!".as_bytes().try_into().unwrap());
112 let mut header = Vec::default();
113 md5.encode(&mut header);
114 assert_eq!(header[0], "Q2hlY2sgSW50ZWdyaXR5IQ==");
115 }
116}