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