1use blake2::{Blake2b, Digest};
18use http::Extensions;
19use serde::{Deserialize, Serialize};
20use std::fmt::{Display, Formatter, Result as FmtResult};
21
22const KEY_SIZE: usize = 16;
24
25pub type HashBinary = [u8; KEY_SIZE];
27
28fn hex2str(hex: &[u8]) -> String {
29 use std::fmt::Write;
30 let mut s = String::with_capacity(KEY_SIZE * 2);
31 for c in hex {
32 write!(s, "{:02x}", c).unwrap(); }
34 s
35}
36
37pub fn str2hex(s: &str) -> Option<HashBinary> {
41 if s.len() != KEY_SIZE * 2 {
42 return None;
43 }
44 let mut output = [0; KEY_SIZE];
45 hex::decode_to_slice(s.as_bytes(), &mut output).ok()?;
47 Some(output)
48}
49
50pub trait CacheHashKey {
52 fn primary_bin(&self) -> HashBinary;
54
55 fn variance_bin(&self) -> Option<HashBinary>;
59
60 fn combined_bin(&self) -> HashBinary {
62 let key = self.primary_bin();
63 if let Some(v) = self.variance_bin() {
64 let mut hasher = Blake2b128::new();
65 hasher.update(key);
66 hasher.update(v);
67 hasher.finalize().into()
68 } else {
69 key
71 }
72 }
73
74 fn user_tag(&self) -> &str;
78
79 fn primary(&self) -> String {
81 hex2str(&self.primary_bin())
82 }
83
84 fn variance(&self) -> Option<String> {
86 self.variance_bin().as_ref().map(|b| hex2str(&b[..]))
87 }
88
89 fn combined(&self) -> String {
91 hex2str(&self.combined_bin())
92 }
93}
94
95#[derive(Debug, Clone)]
108pub struct CacheKey {
109 primary: Vec<u8>,
112 primary_bin_override: Option<HashBinary>,
113 variance: Option<HashBinary>,
114 pub user_tag: String,
118
119 pub extensions: Extensions,
121}
122
123impl CacheKey {
124 pub fn set_variance_key(&mut self, key: HashBinary) {
126 self.variance = Some(key)
127 }
128
129 pub fn get_variance_key(&self) -> Option<&HashBinary> {
131 self.variance.as_ref()
132 }
133
134 pub fn remove_variance_key(&mut self) {
136 self.variance = None
137 }
138
139 pub fn set_primary_bin_override(&mut self, key: HashBinary) {
141 self.primary_bin_override = Some(key)
142 }
143
144 pub fn primary_key_str(&self) -> Option<&str> {
146 std::str::from_utf8(&self.primary).ok()
147 }
148}
149
150#[derive(Debug, Default, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
153pub struct CompactCacheKey {
154 pub primary: HashBinary,
155 pub variance: Option<Box<HashBinary>>,
157 pub user_tag: Box<str>, }
159
160impl Display for CompactCacheKey {
161 fn fmt(&self, f: &mut Formatter) -> FmtResult {
162 write!(f, "{}", hex2str(&self.primary))?;
163 if let Some(var) = &self.variance {
164 write!(f, ", variance: {}", hex2str(var.as_ref()))?;
165 }
166 write!(f, ", user_tag: {}", self.user_tag)
167 }
168}
169
170impl CacheHashKey for CompactCacheKey {
171 fn primary_bin(&self) -> HashBinary {
172 self.primary
173 }
174
175 fn variance_bin(&self) -> Option<HashBinary> {
176 self.variance.as_ref().map(|s| *s.as_ref())
177 }
178
179 fn user_tag(&self) -> &str {
180 &self.user_tag
181 }
182}
183
184pub(crate) type Blake2b128 = Blake2b<blake2::digest::consts::U16>;
194
195pub fn hash_u8(key: &str) -> u8 {
197 let mut hasher = Blake2b128::new();
198 hasher.update(key);
199 let raw = hasher.finalize();
200 raw[0]
201}
202
203pub fn hash_key<K: AsRef<[u8]>>(key: K) -> HashBinary {
205 let mut hasher = Blake2b128::new();
206 hasher.update(key.as_ref());
207 let raw = hasher.finalize();
208 raw.into()
209}
210
211impl CacheKey {
212 fn primary_hasher(&self) -> Blake2b128 {
213 let mut hasher = Blake2b128::new();
214 hasher.update(&self.primary);
215 hasher
216 }
217
218 pub fn new<B, S>(primary: B, user_tag: S) -> Self
224 where
225 B: Into<Vec<u8>>,
226 S: Into<String>,
227 {
228 CacheKey {
229 primary: primary.into(),
230 primary_bin_override: None,
231 variance: None,
232 user_tag: user_tag.into(),
233 extensions: Extensions::new(),
234 }
235 }
236
237 pub fn primary_key(&self) -> &[u8] {
239 &self.primary[..]
240 }
241
242 pub fn to_compact(&self) -> CompactCacheKey {
244 let primary = self.primary_bin();
245 CompactCacheKey {
246 primary,
247 variance: self.variance_bin().map(Box::new),
248 user_tag: self.user_tag.clone().into_boxed_str(),
249 }
250 }
251}
252
253impl CacheHashKey for CacheKey {
254 fn primary_bin(&self) -> HashBinary {
255 if let Some(primary_bin_override) = self.primary_bin_override {
256 primary_bin_override
257 } else {
258 self.primary_hasher().finalize().into()
259 }
260 }
261
262 fn variance_bin(&self) -> Option<HashBinary> {
263 self.variance
264 }
265
266 fn user_tag(&self) -> &str {
267 &self.user_tag
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_cache_key_hash() {
277 let key = CacheKey::new("aa", "1");
278 let hash = key.primary();
279 assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
280 assert!(key.variance().is_none());
281 assert_eq!(key.combined(), hash);
282 let compact = key.to_compact();
283 assert_eq!(compact.primary(), hash);
284 assert!(compact.variance().is_none());
285 assert_eq!(compact.combined(), hash);
286 }
287
288 #[test]
289 fn test_caller_framed_primary_avoids_ambiguous_component_boundaries() {
290 let left_components = [b"tenant_a".as_slice(), b"/path".as_slice()];
291 let right_components = [b"tenant_".as_slice(), b"a/path".as_slice()];
292 let legacy_primary = left_components.concat();
293 assert_eq!(legacy_primary, right_components.concat());
294
295 fn length_prefixed(components: &[&[u8]]) -> Vec<u8> {
296 let mut primary = Vec::new();
297 for component in components {
298 primary.extend_from_slice(&(component.len() as u64).to_be_bytes());
299 primary.extend_from_slice(component);
300 }
301 primary
302 }
303
304 let left = CacheKey::new(length_prefixed(&left_components), "1");
305 let right = CacheKey::new(length_prefixed(&right_components), "1");
306 assert_ne!(left.primary_bin(), right.primary_bin());
307 assert_ne!(left.primary_bin(), hash_key(legacy_primary));
308 }
309
310 #[test]
311 fn test_raw_concatenation_preserves_legacy_hash() {
312 let mut primary = b"tenant_a".to_vec();
313 primary.extend_from_slice(b"/path");
314
315 let key = CacheKey::new(primary, "1");
316 assert_eq!(key.primary(), "6c79e74e88bacb8eb370adb7617068c8");
317 }
318
319 #[test]
320 fn test_cache_key_hash_override() {
321 let mut key = CacheKey {
322 primary: b"aa".to_vec(),
323 primary_bin_override: str2hex("27c35e6e9373877f29e562464e46497e"),
324 variance: None,
325 user_tag: "1".into(),
326 extensions: Extensions::new(),
327 };
328 let hash = key.primary();
329 assert_eq!(hash, "27c35e6e9373877f29e562464e46497e");
330 assert!(key.variance().is_none());
331 assert_eq!(key.combined(), hash);
332 let compact = key.to_compact();
333 assert_eq!(compact.primary(), hash);
334 assert!(compact.variance().is_none());
335 assert_eq!(compact.combined(), hash);
336
337 key.set_primary_bin_override(str2hex("004174d3e75a811a5b44c46b3856f3ee").unwrap());
339 let hash = key.primary();
340 assert_eq!(hash, "004174d3e75a811a5b44c46b3856f3ee");
341 assert!(key.variance().is_none());
342 assert_eq!(key.combined(), hash);
343 let compact = key.to_compact();
344 assert_eq!(compact.primary(), hash);
345 assert!(compact.variance().is_none());
346 assert_eq!(compact.combined(), hash);
347 }
348
349 #[test]
350 fn test_cache_key_vary_hash() {
351 let key = CacheKey {
352 primary: b"aa".to_vec(),
353 primary_bin_override: None,
354 variance: Some([0u8; 16]),
355 user_tag: "1".into(),
356 extensions: Extensions::new(),
357 };
358 let hash = key.primary();
359 assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
360 assert_eq!(key.variance().unwrap(), "00000000000000000000000000000000");
361 assert_eq!(key.combined(), "004174d3e75a811a5b44c46b3856f3ee");
362 let compact = key.to_compact();
363 assert_eq!(compact.primary(), "ac10f2aef117729f8dad056b3059eb7e");
364 assert_eq!(
365 compact.variance().unwrap(),
366 "00000000000000000000000000000000"
367 );
368 assert_eq!(compact.combined(), "004174d3e75a811a5b44c46b3856f3ee");
369 }
370
371 #[test]
372 fn test_cache_key_vary_hash_override() {
373 let key = CacheKey {
374 primary: b"saaaad".to_vec(),
375 primary_bin_override: str2hex("ac10f2aef117729f8dad056b3059eb7e"),
376 variance: Some([0u8; 16]),
377 user_tag: "1".into(),
378 extensions: Extensions::new(),
379 };
380 let hash = key.primary();
381 assert_eq!(hash, "ac10f2aef117729f8dad056b3059eb7e");
382 assert_eq!(key.variance().unwrap(), "00000000000000000000000000000000");
383 assert_eq!(key.combined(), "004174d3e75a811a5b44c46b3856f3ee");
384 let compact = key.to_compact();
385 assert_eq!(compact.primary(), "ac10f2aef117729f8dad056b3059eb7e");
386 assert_eq!(
387 compact.variance().unwrap(),
388 "00000000000000000000000000000000"
389 );
390 assert_eq!(compact.combined(), "004174d3e75a811a5b44c46b3856f3ee");
391 }
392
393 #[test]
394 fn test_hex_str() {
395 let mut key = [0; KEY_SIZE];
396 for (i, v) in key.iter_mut().enumerate() {
397 *v = i as u8;
399 }
400 let hex_str = hex2str(&key);
401 let key2 = str2hex(&hex_str).unwrap();
402 for i in 0..KEY_SIZE {
403 assert_eq!(key[i], key2[i]);
404 }
405 }
406 #[test]
407 fn test_primary_key_str_valid_utf8() {
408 let valid_utf8_key = CacheKey {
409 primary: b"/valid/path?query=1".to_vec(),
410 primary_bin_override: None,
411 variance: None,
412 user_tag: "1".into(),
413 extensions: Extensions::new(),
414 };
415
416 assert_eq!(
417 valid_utf8_key.primary_key_str(),
418 Some("/valid/path?query=1")
419 )
420 }
421
422 #[test]
423 fn test_primary_key_str_invalid_utf8() {
424 let invalid_utf8_key = CacheKey {
425 primary: vec![0x66, 0x6f, 0x6f, 0xff],
426 primary_bin_override: None,
427 variance: None,
428 user_tag: "1".into(),
429 extensions: Extensions::new(),
430 };
431
432 assert!(invalid_utf8_key.primary_key_str().is_none())
433 }
434}