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}