1use aws_smithy_types::base64;
9use http::header::{HeaderMap, HeaderValue};
10
11use crate::Crc64Nvme;
12use crate::{
13    Checksum, Crc32, Crc32c, Md5, Sha1, Sha256, CRC_32_C_NAME, CRC_32_NAME, CRC_64_NVME_NAME,
14    SHA_1_NAME, SHA_256_NAME,
15};
16
17pub const CRC_32_HEADER_NAME: &str = "x-amz-checksum-crc32";
18pub const CRC_32_C_HEADER_NAME: &str = "x-amz-checksum-crc32c";
19pub const SHA_1_HEADER_NAME: &str = "x-amz-checksum-sha1";
20pub const SHA_256_HEADER_NAME: &str = "x-amz-checksum-sha256";
21pub const CRC_64_NVME_HEADER_NAME: &str = "x-amz-checksum-crc64nvme";
22
23#[warn(dead_code)]
25pub(crate) static MD5_HEADER_NAME: &str = "content-md5";
26
27pub const CHECKSUM_ALGORITHMS_IN_PRIORITY_ORDER: [&str; 5] = [
31    CRC_64_NVME_NAME,
32    CRC_32_C_NAME,
33    CRC_32_NAME,
34    SHA_1_NAME,
35    SHA_256_NAME,
36];
37
38pub trait HttpChecksum: Checksum + Send + Sync {
42    fn headers(self: Box<Self>) -> HeaderMap<HeaderValue> {
45        let mut header_map = HeaderMap::new();
46        header_map.insert(self.header_name(), self.header_value());
47
48        header_map
49    }
50
51    fn header_name(&self) -> &'static str;
53
54    fn header_value(self: Box<Self>) -> HeaderValue {
56        let hash = self.finalize();
57        HeaderValue::from_str(&base64::encode(&hash[..]))
58            .expect("base64 encoded bytes are always valid header values")
59    }
60
61    fn size(&self) -> u64 {
66        let trailer_name_size_in_bytes = self.header_name().len();
67        let base64_encoded_checksum_size_in_bytes =
68            base64::encoded_length(Checksum::size(self) as usize);
69
70        let size = trailer_name_size_in_bytes
71            + ":".len()
74            + base64_encoded_checksum_size_in_bytes;
75
76        size as u64
77    }
78}
79
80impl HttpChecksum for Crc32 {
81    fn header_name(&self) -> &'static str {
82        CRC_32_HEADER_NAME
83    }
84}
85
86impl HttpChecksum for Crc32c {
87    fn header_name(&self) -> &'static str {
88        CRC_32_C_HEADER_NAME
89    }
90}
91
92impl HttpChecksum for Crc64Nvme {
93    fn header_name(&self) -> &'static str {
94        CRC_64_NVME_HEADER_NAME
95    }
96}
97
98impl HttpChecksum for Sha1 {
99    fn header_name(&self) -> &'static str {
100        SHA_1_HEADER_NAME
101    }
102}
103
104impl HttpChecksum for Sha256 {
105    fn header_name(&self) -> &'static str {
106        SHA_256_HEADER_NAME
107    }
108}
109
110impl HttpChecksum for Md5 {
111    fn header_name(&self) -> &'static str {
112        MD5_HEADER_NAME
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use aws_smithy_types::base64;
119    use bytes::Bytes;
120
121    use crate::{
122        ChecksumAlgorithm, CRC_32_C_NAME, CRC_32_NAME, CRC_64_NVME_NAME, SHA_1_NAME, SHA_256_NAME,
123    };
124
125    use super::HttpChecksum;
126
127    #[test]
128    fn test_trailer_length_of_crc32_checksum_body() {
129        let checksum = CRC_32_NAME
130            .parse::<ChecksumAlgorithm>()
131            .unwrap()
132            .into_impl();
133        let expected_size = 29;
134        let actual_size = HttpChecksum::size(&*checksum);
135        assert_eq!(expected_size, actual_size)
136    }
137
138    #[test]
139    fn test_trailer_value_of_crc32_checksum_body() {
140        let checksum = CRC_32_NAME
141            .parse::<ChecksumAlgorithm>()
142            .unwrap()
143            .into_impl();
144        let expected_value = Bytes::from_static(b"\0\0\0\0");
146        let expected_value = base64::encode(&expected_value);
147        let actual_value = checksum.header_value();
148        assert_eq!(expected_value, actual_value)
149    }
150
151    #[test]
152    fn test_trailer_length_of_crc32c_checksum_body() {
153        let checksum = CRC_32_C_NAME
154            .parse::<ChecksumAlgorithm>()
155            .unwrap()
156            .into_impl();
157        let expected_size = 30;
158        let actual_size = HttpChecksum::size(&*checksum);
159        assert_eq!(expected_size, actual_size)
160    }
161
162    #[test]
163    fn test_trailer_value_of_crc32c_checksum_body() {
164        let checksum = CRC_32_C_NAME
165            .parse::<ChecksumAlgorithm>()
166            .unwrap()
167            .into_impl();
168        let expected_value = Bytes::from_static(b"\0\0\0\0");
170        let expected_value = base64::encode(&expected_value);
171        let actual_value = checksum.header_value();
172        assert_eq!(expected_value, actual_value)
173    }
174
175    #[test]
176    fn test_trailer_length_of_crc64nvme_checksum_body() {
177        let checksum = CRC_64_NVME_NAME
178            .parse::<ChecksumAlgorithm>()
179            .unwrap()
180            .into_impl();
181        let expected_size = 37;
182        let actual_size = HttpChecksum::size(&*checksum);
183        assert_eq!(expected_size, actual_size)
184    }
185
186    #[test]
187    fn test_trailer_value_of_crc64nvme_checksum_body() {
188        let checksum = CRC_64_NVME_NAME
189            .parse::<ChecksumAlgorithm>()
190            .unwrap()
191            .into_impl();
192        let expected_value = Bytes::from_static(b"\0\0\0\0\0\0\0\0");
194        let expected_value = base64::encode(&expected_value);
195        let actual_value = checksum.header_value();
196        assert_eq!(expected_value, actual_value)
197    }
198
199    #[test]
200    fn test_trailer_length_of_sha1_checksum_body() {
201        let checksum = SHA_1_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
202        let expected_size = 48;
203        let actual_size = HttpChecksum::size(&*checksum);
204        assert_eq!(expected_size, actual_size)
205    }
206
207    #[test]
208    fn test_trailer_value_of_sha1_checksum_body() {
209        let checksum = SHA_1_NAME.parse::<ChecksumAlgorithm>().unwrap().into_impl();
210        let expected_value = Bytes::from_static(&[
212            0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60,
213            0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09,
214        ]);
215        let expected_value = base64::encode(&expected_value);
216        let actual_value = checksum.header_value();
217        assert_eq!(expected_value, actual_value)
218    }
219
220    #[test]
221    fn test_trailer_length_of_sha256_checksum_body() {
222        let checksum = SHA_256_NAME
223            .parse::<ChecksumAlgorithm>()
224            .unwrap()
225            .into_impl();
226        let expected_size = 66;
227        let actual_size = HttpChecksum::size(&*checksum);
228        assert_eq!(expected_size, actual_size)
229    }
230
231    #[test]
232    fn test_trailer_value_of_sha256_checksum_body() {
233        let checksum = SHA_256_NAME
234            .parse::<ChecksumAlgorithm>()
235            .unwrap()
236            .into_impl();
237        let expected_value = Bytes::from_static(&[
239            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
240            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
241            0x78, 0x52, 0xb8, 0x55,
242        ]);
243        let expected_value = base64::encode(&expected_value);
244        let actual_value = checksum.header_value();
245        assert_eq!(expected_value, actual_value)
246    }
247}