1use super::{
2 certification_tree_entry::HttpCertificationTreeEntry,
3 certification_tree_path::CertificationTreePathSegment,
4};
5use crate::{
6 tree::HttpCertificationPathType,
7 utils::{more_specific_wildcards_for, PATH_PREFIX_BYTES},
8 HttpCertificationError, HttpCertificationPath, HttpCertificationResult,
9};
10use ic_certification::{labeled, labeled_hash, merge_hash_trees, AsHashTree, HashTree, NestedTree};
11use ic_representation_independent_hash::Sha256Digest;
12use std::fmt::{Debug, Formatter};
13
14type CertificationTree = NestedTree<CertificationTreePathSegment, Vec<u8>>;
15
16#[derive(Clone)]
18pub struct HttpCertificationTree {
19 tree: CertificationTree,
20}
21
22impl Debug for HttpCertificationTree {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 write!(f, "tree: {:#?}", self.tree)
25 }
26}
27
28impl Default for HttpCertificationTree {
29 fn default() -> Self {
30 Self::new(CertificationTree::default())
31 }
32}
33
34impl HttpCertificationTree {
35 pub fn new(tree: CertificationTree) -> Self {
38 Self { tree }
39 }
40
41 pub fn root_hash(&self) -> Sha256Digest {
44 labeled_hash(PATH_PREFIX_BYTES, &self.tree.root_hash())
45 }
46
47 pub fn insert(&mut self, entry: &HttpCertificationTreeEntry) {
51 let tree_path = entry.to_tree_path();
52 self.tree.insert(&tree_path, vec![]);
53 }
54
55 pub fn delete(&mut self, entry: &HttpCertificationTreeEntry) {
59 let tree_path = entry.to_tree_path();
60 self.tree.delete(&tree_path);
61 }
62
63 pub fn delete_by_path(&mut self, path: &HttpCertificationPath) {
67 let tree_path = path.to_tree_path();
68 self.tree.delete(&tree_path);
69 }
70
71 pub fn clear(&mut self) {
75 self.tree.clear();
76 }
77
78 pub fn as_hash_tree(&self) -> HashTree {
82 labeled(PATH_PREFIX_BYTES, self.tree.as_hash_tree())
83 }
84
85 pub fn witness(
90 &self,
91 entry: &HttpCertificationTreeEntry,
92 request_url: &str,
93 ) -> HttpCertificationResult<HashTree> {
94 let witness = match entry.path.get_type() {
95 HttpCertificationPathType::Exact(_) => self.tree.witness(&entry.to_tree_path()),
96
97 HttpCertificationPathType::Wildcard(wildcard_path) => {
98 let requested_tree_path = HttpCertificationPath::exact(request_url).to_tree_path();
99 let responding_tree_path = entry.path.to_tree_path();
100
101 if responding_tree_path.len() > requested_tree_path.len() {
102 return Err(HttpCertificationError::WildcardPathNotValidForRequestPath {
103 wildcard_path: wildcard_path.to_string(),
104 request_path: request_url.to_string(),
105 });
106 }
107
108 let witness = self.tree.witness(&requested_tree_path);
111
112 let witness = merge_hash_trees(witness, self.tree.witness(&responding_tree_path));
114
115 more_specific_wildcards_for(&requested_tree_path, &responding_tree_path)
118 .iter()
119 .fold(witness, |acc, path| {
120 merge_hash_trees(acc, self.tree.witness(path))
121 })
122 }
123 };
124
125 Ok(labeled(PATH_PREFIX_BYTES, witness))
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::{
133 DefaultCelBuilder, DefaultResponseCertification, HttpCertification, HttpRequest,
134 HttpResponse, StatusCode, CERTIFICATE_EXPRESSION_HEADER_NAME,
135 };
136 use assert_matches::assert_matches;
137 use ic_certification::SubtreeLookupResult;
138 use rstest::*;
139
140 #[rstest]
141 fn test_witness() {
142 let mut tree = HttpCertificationTree::default();
143
144 let cel_expr = DefaultCelBuilder::full_certification()
145 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
146 vec![],
147 ))
148 .build();
149
150 let not_found_request = HttpRequest::get("/assets/js/not-found.js").build();
151 let not_found_response = HttpResponse::not_found(
152 br#"404 Not Found"#,
153 vec![(
154 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
155 cel_expr.to_string(),
156 )],
157 )
158 .build();
159
160 let hello_world_request = HttpRequest::get("/assets/js/hello-world.js").build();
161 let hello_world_response = HttpResponse::not_found(
162 br#"console.log("Hello, World!")"#,
163 vec![(
164 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
165 cel_expr.to_string(),
166 )],
167 )
168 .build();
169
170 let not_found_entry = HttpCertificationTreeEntry::new(
171 HttpCertificationPath::wildcard("/assets/js"),
172 HttpCertification::full(&cel_expr, ¬_found_request, ¬_found_response, None)
173 .unwrap(),
174 );
175 tree.insert(¬_found_entry);
176
177 let hello_world_entry = HttpCertificationTreeEntry::new(
178 HttpCertificationPath::exact(hello_world_request.url()),
179 HttpCertification::full(&cel_expr, &hello_world_request, &hello_world_response, None)
180 .unwrap(),
181 );
182 tree.insert(&hello_world_entry);
183
184 let not_found_witness = tree.witness(¬_found_entry, "/assets/js/0.js").unwrap();
185
186 assert_eq!(
187 not_found_witness.lookup_subtree(["http_expr", "<*>"]),
188 SubtreeLookupResult::Absent
189 );
190 assert_eq!(
191 not_found_witness.lookup_subtree(["http_expr", "", "<*>"]),
192 SubtreeLookupResult::Absent
193 );
194 assert_eq!(
195 not_found_witness.lookup_subtree(["http_expr", "assets", "<*>"]),
196 SubtreeLookupResult::Absent
197 );
198 assert_eq!(
199 not_found_witness.lookup_subtree(["http_expr", "assets", "", "<*>"]),
200 SubtreeLookupResult::Absent
201 );
202 assert_matches!(
203 not_found_witness.lookup_subtree(["http_expr", "assets", "js", "<*>"]),
204 SubtreeLookupResult::Found(_)
205 );
206 assert_eq!(
207 not_found_witness.lookup_subtree(["http_expr", "assets", "js", "0.js", "<*>"]),
208 SubtreeLookupResult::Absent
209 );
210
211 let hello_world_witness = tree
212 .witness(&hello_world_entry, "/assets/js/hello-world.js")
213 .unwrap();
214
215 assert_matches!(
216 hello_world_witness.lookup_subtree([
217 "http_expr",
218 "assets",
219 "js",
220 "hello-world.js",
221 "<$>"
222 ]),
223 SubtreeLookupResult::Found(_)
224 );
225 }
226
227 #[rstest]
228 fn test_delete_by_path() {
229 let mut http_tree = HttpCertificationTree::default();
230 let cel_expr = DefaultCelBuilder::full_certification()
231 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
232 vec![],
233 ))
234 .build();
235 let req_one_url = "/assets/js/hello.js";
236 let req_two_url = "/assets/js/world.js";
237
238 let req_one = HttpRequest::get(req_one_url).build();
260 let req_two = HttpRequest::get(req_two_url).build();
261
262 let res = HttpResponse::builder()
263 .with_status_code(StatusCode::OK)
264 .with_body(br#"console.log("Hello, World!")"#)
265 .with_headers(vec![(
266 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
267 cel_expr.to_string(),
268 )])
269 .build();
270 let alt_res = HttpResponse::builder()
271 .with_status_code(StatusCode::OK)
272 .with_body(br#"console.log("Hello, ALT World!")"#)
273 .with_headers(vec![(
274 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
275 cel_expr.to_string(),
276 )])
277 .build();
278
279 let req_one_entry = HttpCertificationTreeEntry::new(
280 HttpCertificationPath::exact(req_one.url()),
281 HttpCertification::full(&cel_expr, &req_one, &res, None).unwrap(),
282 );
283 http_tree.insert(&req_one_entry);
284
285 let req_one_alt_entry = HttpCertificationTreeEntry::new(
286 HttpCertificationPath::exact(req_one.url()),
287 HttpCertification::full(&cel_expr, &req_one, &alt_res, None).unwrap(),
288 );
289 http_tree.insert(&req_one_alt_entry);
290
291 let req_two_entry = HttpCertificationTreeEntry::new(
292 HttpCertificationPath::exact(req_two.url()),
293 HttpCertification::full(&cel_expr, &req_two, &res, None).unwrap(),
294 );
295 http_tree.insert(&req_two_entry);
296
297 let req_two_alt_entry = HttpCertificationTreeEntry::new(
298 HttpCertificationPath::exact(req_two.url()),
299 HttpCertification::full(&cel_expr, &req_two, &alt_res, None).unwrap(),
300 );
301 http_tree.insert(&req_two_alt_entry);
302
303 assert_matches!(
304 http_tree
305 .witness(&req_one_entry, req_one_url)
306 .unwrap()
307 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
308 SubtreeLookupResult::Found(_)
309 );
310 assert_matches!(
311 http_tree
312 .witness(&req_one_alt_entry, req_one_url)
313 .unwrap()
314 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
315 SubtreeLookupResult::Found(_)
316 );
317
318 assert_matches!(
319 http_tree
320 .witness(&req_two_entry, req_two_url)
321 .unwrap()
322 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
323 SubtreeLookupResult::Found(_)
324 );
325 assert_matches!(
326 http_tree
327 .witness(&req_two_alt_entry, req_two_url)
328 .unwrap()
329 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
330 SubtreeLookupResult::Found(_)
331 );
332
333 assert!(http_tree
334 .tree
335 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
336 assert!(http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
337 assert!(http_tree
338 .tree
339 .contains_path(&req_one_alt_entry.to_tree_path()));
340
341 assert!(http_tree
342 .tree
343 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
344 assert!(http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
345 assert!(http_tree
346 .tree
347 .contains_path(&req_two_alt_entry.to_tree_path()));
348
349 http_tree.delete_by_path(&HttpCertificationPath::exact(req_one.url()));
351
352 assert_matches!(
353 http_tree
354 .witness(&req_one_entry, req_one_url)
355 .unwrap()
356 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
357 SubtreeLookupResult::Absent
358 );
359 assert_matches!(
360 http_tree
361 .witness(&req_one_alt_entry, req_one_url)
362 .unwrap()
363 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
364 SubtreeLookupResult::Absent
365 );
366
367 assert_matches!(
368 http_tree
369 .witness(&req_two_entry, req_two_url)
370 .unwrap()
371 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
372 SubtreeLookupResult::Found(_)
373 );
374 assert_matches!(
375 http_tree
376 .witness(&req_two_alt_entry, req_two_url)
377 .unwrap()
378 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
379 SubtreeLookupResult::Found(_)
380 );
381
382 assert!(!http_tree
383 .tree
384 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
385 assert!(!http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
386 assert!(!http_tree
387 .tree
388 .contains_path(&req_one_alt_entry.to_tree_path()));
389
390 assert!(http_tree
391 .tree
392 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
393 assert!(http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
394 assert!(http_tree
395 .tree
396 .contains_path(&req_two_alt_entry.to_tree_path()));
397
398 http_tree.delete_by_path(&HttpCertificationPath::exact(req_two.url()));
400
401 assert_matches!(
402 http_tree
403 .witness(&req_one_entry, req_one_url)
404 .unwrap()
405 .lookup_subtree(&lookup_path_from_entry(&req_one_entry)),
406 SubtreeLookupResult::Absent
407 );
408 assert_matches!(
409 http_tree
410 .witness(&req_one_alt_entry, req_one_url)
411 .unwrap()
412 .lookup_subtree(&lookup_path_from_entry(&req_one_alt_entry)),
413 SubtreeLookupResult::Absent
414 );
415
416 assert_matches!(
417 http_tree
418 .witness(&req_two_entry, req_two_url)
419 .unwrap()
420 .lookup_subtree(&lookup_path_from_entry(&req_two_entry)),
421 SubtreeLookupResult::Absent
422 );
423 assert_matches!(
424 http_tree
425 .witness(&req_two_alt_entry, req_two_url)
426 .unwrap()
427 .lookup_subtree(&lookup_path_from_entry(&req_two_alt_entry)),
428 SubtreeLookupResult::Absent
429 );
430
431 assert!(!http_tree
432 .tree
433 .contains_path(&HttpCertificationPath::exact(req_one_url).to_tree_path()));
434 assert!(!http_tree.tree.contains_path(&req_one_entry.to_tree_path()));
435 assert!(!http_tree
436 .tree
437 .contains_path(&req_one_alt_entry.to_tree_path()));
438
439 assert!(!http_tree
440 .tree
441 .contains_path(&HttpCertificationPath::exact(req_two_url).to_tree_path()));
442 assert!(!http_tree.tree.contains_path(&req_two_entry.to_tree_path()));
443 assert!(!http_tree
444 .tree
445 .contains_path(&req_two_alt_entry.to_tree_path()));
446 }
447
448 #[rstest]
449 fn delete_removes_empty_subpaths() {
450 let mut http_tree = HttpCertificationTree::default();
451 let cel_expr = DefaultCelBuilder::full_certification()
452 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
453 vec![],
454 ))
455 .build();
456 let req_url = "/assets/js/hello-world.js";
457
458 let get_request = HttpRequest::get(req_url).build();
478 let post_request = HttpRequest::post(req_url).build();
479
480 let response = HttpResponse::ok(
481 br#"console.log("Hello, World!")"#,
482 vec![(
483 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
484 cel_expr.to_string(),
485 )],
486 )
487 .build();
488 let alt_response = HttpResponse::ok(
489 br#"console.log("Hello, ALT World!")"#,
490 vec![(
491 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
492 cel_expr.to_string(),
493 )],
494 )
495 .build();
496
497 let get_entry = HttpCertificationTreeEntry::new(
498 HttpCertificationPath::exact(get_request.url()),
499 HttpCertification::full(&cel_expr, &get_request, &response, None).unwrap(),
500 );
501 http_tree.insert(&get_entry);
502
503 let post_entry = HttpCertificationTreeEntry::new(
504 HttpCertificationPath::exact(post_request.url()),
505 HttpCertification::full(&cel_expr, &post_request, &response, None).unwrap(),
506 );
507 http_tree.insert(&post_entry);
508
509 let alt_get_entry = HttpCertificationTreeEntry::new(
510 HttpCertificationPath::exact(get_request.url()),
511 HttpCertification::full(&cel_expr, &get_request, &alt_response, None).unwrap(),
512 );
513 http_tree.insert(&alt_get_entry);
514
515 let alt_post_entry = HttpCertificationTreeEntry::new(
516 HttpCertificationPath::exact(post_request.url()),
517 HttpCertification::full(&cel_expr, &post_request, &alt_response, None).unwrap(),
518 );
519 http_tree.insert(&alt_post_entry);
520
521 assert_matches!(
522 http_tree
523 .witness(&get_entry, req_url)
524 .unwrap()
525 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
526 SubtreeLookupResult::Found(_)
527 );
528 assert_matches!(
529 http_tree
530 .witness(&post_entry, req_url)
531 .unwrap()
532 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
533 SubtreeLookupResult::Found(_)
534 );
535 assert_matches!(
536 http_tree
537 .witness(&alt_get_entry, req_url)
538 .unwrap()
539 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
540 SubtreeLookupResult::Found(_)
541 );
542 assert_matches!(
543 http_tree
544 .witness(&alt_post_entry, req_url)
545 .unwrap()
546 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
547 SubtreeLookupResult::Found(_)
548 );
549
550 assert!(http_tree
551 .tree
552 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
553 assert!(http_tree.tree.contains_path(&get_entry.to_tree_path()));
554 assert!(http_tree.tree.contains_path(&post_entry.to_tree_path()));
555 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
556 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
557
558 http_tree.delete(&get_entry);
560
561 assert_matches!(
562 http_tree
563 .witness(&get_entry, req_url)
564 .unwrap()
565 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
566 SubtreeLookupResult::Absent
567 );
568 assert_matches!(
569 http_tree
570 .witness(&post_entry, req_url)
571 .unwrap()
572 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
573 SubtreeLookupResult::Found(_)
574 );
575 assert_matches!(
576 http_tree
577 .witness(&alt_get_entry, req_url)
578 .unwrap()
579 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
580 SubtreeLookupResult::Found(_)
581 );
582 assert_matches!(
583 http_tree
584 .witness(&alt_post_entry, req_url)
585 .unwrap()
586 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
587 SubtreeLookupResult::Found(_)
588 );
589
590 assert!(http_tree
591 .tree
592 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
593 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
594 assert!(http_tree.tree.contains_path(&post_entry.to_tree_path()));
595 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
596 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
597
598 http_tree.delete(&post_entry);
600
601 assert_matches!(
602 http_tree
603 .witness(&get_entry, req_url)
604 .unwrap()
605 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
606 SubtreeLookupResult::Absent
607 );
608 assert_matches!(
609 http_tree
610 .witness(&post_entry, req_url)
611 .unwrap()
612 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
613 SubtreeLookupResult::Absent
614 );
615 assert_matches!(
616 http_tree
617 .witness(&alt_get_entry, req_url)
618 .unwrap()
619 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
620 SubtreeLookupResult::Found(_)
621 );
622 assert_matches!(
623 http_tree
624 .witness(&alt_post_entry, req_url)
625 .unwrap()
626 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
627 SubtreeLookupResult::Found(_)
628 );
629
630 assert!(http_tree
631 .tree
632 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
633 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
634 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
635 assert!(http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
636 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
637
638 http_tree.delete(&alt_get_entry);
640
641 assert_matches!(
642 http_tree
643 .witness(&get_entry, req_url)
644 .unwrap()
645 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
646 SubtreeLookupResult::Absent
647 );
648 assert_matches!(
649 http_tree
650 .witness(&post_entry, req_url)
651 .unwrap()
652 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
653 SubtreeLookupResult::Absent
654 );
655 assert_matches!(
656 http_tree
657 .witness(&alt_get_entry, req_url)
658 .unwrap()
659 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
660 SubtreeLookupResult::Absent
661 );
662 assert_matches!(
663 http_tree
664 .witness(&alt_post_entry, req_url)
665 .unwrap()
666 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
667 SubtreeLookupResult::Found(_)
668 );
669
670 assert!(http_tree
671 .tree
672 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
673 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
674 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
675 assert!(!http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
676 assert!(http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
677
678 http_tree.delete(&alt_post_entry);
680
681 assert_matches!(
682 http_tree
683 .witness(&get_entry, req_url)
684 .unwrap()
685 .lookup_subtree(&lookup_path_from_entry(&get_entry)),
686 SubtreeLookupResult::Absent
687 );
688 assert_matches!(
689 http_tree
690 .witness(&post_entry, req_url)
691 .unwrap()
692 .lookup_subtree(&lookup_path_from_entry(&post_entry)),
693 SubtreeLookupResult::Absent
694 );
695 assert_matches!(
696 http_tree
697 .witness(&alt_get_entry, req_url)
698 .unwrap()
699 .lookup_subtree(&lookup_path_from_entry(&alt_get_entry)),
700 SubtreeLookupResult::Absent
701 );
702 assert_matches!(
703 http_tree
704 .witness(&alt_post_entry, req_url)
705 .unwrap()
706 .lookup_subtree(&lookup_path_from_entry(&alt_post_entry)),
707 SubtreeLookupResult::Absent
708 );
709
710 assert!(!http_tree
711 .tree
712 .contains_path(&HttpCertificationPath::exact(req_url).to_tree_path()));
713 assert!(!http_tree.tree.contains_path(&get_entry.to_tree_path()));
714 assert!(!http_tree.tree.contains_path(&post_entry.to_tree_path()));
715 assert!(!http_tree.tree.contains_path(&alt_get_entry.to_tree_path()));
716 assert!(!http_tree.tree.contains_path(&alt_post_entry.to_tree_path()));
717 }
718
719 #[rstest]
720 fn test_witness_wildcard_too_long() {
721 let mut tree = HttpCertificationTree::default();
722
723 let cel_expr = DefaultCelBuilder::full_certification()
724 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
725 vec![],
726 ))
727 .build();
728
729 let not_found_request = HttpRequest::get("/assets/js/not-found.js").build();
730 let not_found_response = HttpResponse::not_found(
731 br#"404 Not Found"#,
732 vec![(
733 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
734 cel_expr.to_string(),
735 )],
736 )
737 .build();
738
739 let not_found_entry = HttpCertificationTreeEntry::new(
740 HttpCertificationPath::wildcard("/assets/js"),
741 HttpCertification::full(&cel_expr, ¬_found_request, ¬_found_response, None)
742 .unwrap(),
743 );
744 tree.insert(¬_found_entry);
745
746 let witness = tree.witness(¬_found_entry, "/assets");
747
748 assert_matches!(
749 witness,
750 Err(HttpCertificationError::WildcardPathNotValidForRequestPath { .. })
751 );
752 }
753
754 #[rstest]
755 fn test_witness_wildcard_matches_asset() {
756 let mut tree = HttpCertificationTree::default();
757
758 let cel_expr = DefaultCelBuilder::full_certification()
759 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
760 vec![],
761 ))
762 .build();
763
764 let index_html_request = HttpRequest::get("/").build();
765
766 let index_html_body = b"<html><body><h1>Hello World!</h1></body></html>".to_vec();
767 let index_html_response = HttpResponse::not_found(
768 index_html_body,
769 vec![(
770 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
771 cel_expr.to_string(),
772 )],
773 )
774 .build();
775
776 let certification =
777 HttpCertification::full(&cel_expr, &index_html_request, &index_html_response, None)
778 .unwrap();
779 let index_html_entry =
780 HttpCertificationTreeEntry::new(HttpCertificationPath::wildcard("/"), certification);
781 tree.insert(&index_html_entry);
782
783 let witness = tree.witness(&index_html_entry, "/").unwrap();
784
785 let mut path = index_html_entry.to_tree_path();
786 path.insert(0, b"http_expr".to_vec());
787
788 assert_matches!(witness.lookup_subtree(&path), SubtreeLookupResult::Found(_));
789 }
790
791 #[rstest]
792 fn test_as_hash_tree_contains_http_expr_label() {
793 let mut tree = HttpCertificationTree::default();
794
795 let cel_expr = DefaultCelBuilder::full_certification()
796 .with_response_certification(DefaultResponseCertification::response_header_exclusions(
797 vec![],
798 ))
799 .build();
800
801 let request = HttpRequest::get("/index.html").build();
802 let response = HttpResponse::ok(
803 b"hello",
804 vec![(
805 CERTIFICATE_EXPRESSION_HEADER_NAME.into(),
806 cel_expr.to_string(),
807 )],
808 )
809 .build();
810
811 let certification = HttpCertification::full(&cel_expr, &request, &response, None).unwrap();
812 let entry = HttpCertificationTreeEntry::new(
813 HttpCertificationPath::exact("/index.html"),
814 certification,
815 );
816 tree.insert(&entry);
817
818 let full_tree = tree.as_hash_tree();
819
820 assert_matches!(
821 full_tree.lookup_subtree(["http_expr", "index.html", "<$>"]),
822 SubtreeLookupResult::Found(_)
823 );
824 }
825
826 fn lookup_path_from_entry(entry: &HttpCertificationTreeEntry) -> Vec<Vec<u8>> {
827 let mut lookup_path = entry.to_tree_path();
828 lookup_path.insert(0, b"http_expr".to_vec());
829 lookup_path
830 }
831}