From b4e02fe29a3399601a03e9fa429c9af0e85d1649 Mon Sep 17 00:00:00 2001 From: Matt Gathu Date: Fri, 25 Sep 2020 18:42:49 +0200 Subject: [PATCH] Fix cyclic references in ResourceMap (#1708) --- CHANGES.md | 2 ++ src/rmap.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 608c237b..3419ce81 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,7 +4,9 @@ ### Changed * Add `TrailingSlash::MergeOnly` behaviour to `NormalizePath`, which allow `NormalizePath` to keep the trailing slash's existance as it is. [#1695] +* Fix `ResourceMap` recursive references when printing/debugging. [#1708] +[#1708]: https://github.com/actix/actix-web/pull/1708 ## 3.0.2 - 2020-09-15 ### Fixed diff --git a/src/rmap.rs b/src/rmap.rs index 5e79830e..05c1f3f1 100644 --- a/src/rmap.rs +++ b/src/rmap.rs @@ -1,5 +1,5 @@ use std::cell::RefCell; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use actix_router::ResourceDef; use fxhash::FxHashMap; @@ -11,7 +11,7 @@ use crate::request::HttpRequest; #[derive(Clone, Debug)] pub struct ResourceMap { root: ResourceDef, - parent: RefCell>>, + parent: RefCell>, named: FxHashMap, patterns: Vec<(ResourceDef, Option>)>, } @@ -20,7 +20,7 @@ impl ResourceMap { pub fn new(root: ResourceDef) -> Self { ResourceMap { root, - parent: RefCell::new(None), + parent: RefCell::new(Weak::new()), named: FxHashMap::default(), patterns: Vec::new(), } @@ -38,7 +38,7 @@ impl ResourceMap { pub(crate) fn finish(&self, current: Rc) { for (_, nested) in &self.patterns { if let Some(ref nested) = nested { - *nested.parent.borrow_mut() = Some(current.clone()); + *nested.parent.borrow_mut() = Rc::downgrade(¤t); nested.finish(nested.clone()); } } @@ -210,7 +210,7 @@ impl ResourceMap { U: Iterator, I: AsRef, { - if let Some(ref parent) = *self.parent.borrow() { + if let Some(ref parent) = self.parent.borrow().upgrade() { parent.fill_root(path, elements)?; } if self.root.resource_path(path, elements) { @@ -230,7 +230,7 @@ impl ResourceMap { U: Iterator, I: AsRef, { - if let Some(ref parent) = *self.parent.borrow() { + if let Some(ref parent) = self.parent.borrow().upgrade() { if let Some(pattern) = parent.named.get(name) { self.fill_root(path, elements)?; if pattern.resource_path(path, elements) { @@ -367,4 +367,42 @@ mod tests { assert_eq!(root.match_name("/user/22/"), None); assert_eq!(root.match_name("/user/22/post/55"), Some("user_post")); } + + #[test] + fn bug_fix_issue_1582_debug_print_exits() { + // ref: https://github.com/actix/actix-web/issues/1582 + let mut root = ResourceMap::new(ResourceDef::root_prefix("")); + + let mut user_map = ResourceMap::new(ResourceDef::root_prefix("")); + user_map.add(&mut ResourceDef::new("/"), None); + user_map.add(&mut ResourceDef::new("/profile"), None); + user_map.add(&mut ResourceDef::new("/article/{id}"), None); + user_map.add(&mut ResourceDef::new("/post/{post_id}"), None); + user_map.add( + &mut ResourceDef::new("/post/{post_id}/comment/{comment_id}"), + None, + ); + + root.add( + &mut ResourceDef::root_prefix("/user/{id}"), + Some(Rc::new(user_map)), + ); + + let root = Rc::new(root); + root.finish(Rc::clone(&root)); + + // check root has no parent + assert!(root.parent.borrow().upgrade().is_none()); + // check child has parent reference + assert!(root.patterns[0].1.is_some()); + // check child's parent root id matches root's root id + assert_eq!( + root.patterns[0].1.as_ref().unwrap().root.id(), + root.root.id() + ); + + let output = format!("{:?}", root); + assert!(output.starts_with("ResourceMap {")); + assert!(output.ends_with(" }")); + } }