diff --git a/src/csrf_fairing.rs b/src/csrf_fairing.rs index 2d1101d9..d8f1eb6c 100644 --- a/src/csrf_fairing.rs +++ b/src/csrf_fairing.rs @@ -236,7 +236,7 @@ impl CsrfFairingBuilder { let default_target = Path::from(&self.default_target.0); let mut hashmap = HashMap::new(); - hashmap.insert("uri", ""); + hashmap.insert("uri", "".to_owned()); if default_target.map(&hashmap).is_none() { return Err(()); } //verify if this path is valid as default path, i.e. it have at most one dynamic part which is @@ -350,8 +350,8 @@ impl Fairing for CsrfFairing { let uri = request.uri().to_string(); let uri = Uri::percent_encode(&uri); - let mut param: HashMap<&str, &str> = HashMap::new(); - param.insert("uri", &uri); + let mut param: HashMap<&str, String> = HashMap::new(); + param.insert("uri", uri.to_string()); request.set_uri(self.default_target.0.map(¶m).unwrap()); request.set_method(self.default_target.1) } diff --git a/src/path.rs b/src/path.rs index b6adfd96..56e420a5 100644 --- a/src/path.rs +++ b/src/path.rs @@ -17,22 +17,28 @@ impl Path { } else { (path, None) }; - Path { - path: path + let path: Vec<_> = path .split('/')//split path at each '/' .filter(|seg| seg != &"")//remove empty segments .map(|seg| { - if seg.get(..1) == Some("<") && seg.get(seg.len() - 1..) == Some(">") {//if the segment start with '<' and end with '>', it is dynamic + if seg.starts_with('<') && seg.ends_with("..>") { + PathPart::MultiDynamic(seg[1..seg.len() - 3].to_owned()) + } else if seg.starts_with('<') && seg.ends_with('>') { PathPart::Dynamic(seg[1..seg.len() - 1].to_owned()) } else {//else it's static PathPart::Static(seg.to_owned()) - }//TODO add support for <..path> to match more than one segment + } }) - .collect(), - param: query.map(|query| { - parse_args(query) - .map(|(k, v)| { - ( + .collect(); + if path[0..path.len()-1].iter().any(|a| + if let PathPart::MultiDynamic(_) = a {true} else {false} + ) { + panic!("invalid path provided"); //TODO return error instead of panic + } + + let param = query.map(|query| { + parse_args(query) + .map(|(k, v)| {( k.to_owned(), if v.get(..1) == Some("<") && v.get(v.len() - 1..) == Some(">") { //do the same kind of parsing as above, but on query params @@ -42,14 +48,17 @@ impl Path { }, ) }) - .collect() - }), + .collect() + }); + Path { + path, + param, } } - pub fn extract<'a>(&self, uri: &'a str) -> Option> { + pub fn extract<'a>(&self, uri: &'a str) -> Option> { //try to match a str against a path, give back a hashmap of correponding parts if it matched - let mut res: HashMap<&str, &'a str> = HashMap::new(); + let mut res: HashMap<&str, String> = HashMap::new(); let (path, query) = if let Some(pos) = uri.find('?') { let (path, query) = uri.split_at(pos); let query = &query[1..]; @@ -60,25 +69,35 @@ impl Path { let mut path = path.split('/').filter(|seg| seg != &""); let mut reference = self.path.iter(); loop { - match path.next() { - Some(v) => { - if let Some(reference) = reference.next() { + match reference.next() { + Some(reference) => { match reference { - PathPart::Static(refe) => if refe != v { + PathPart::Static(reference) => { //static, but not the same, fail to parse - return None; + if let Some(val) = path.next() { + if val!=reference{ + return None + } + } else { + return None + } }, PathPart::Dynamic(key) => { //dynamic, store to hashmap - res.insert(key, v); + if let Some(val) = path.next() { + res.insert(key, val.to_owned()); + } else { + return None + } + } + PathPart::MultiDynamic(key) => { + let val = path.collect::>().join("/"); + res.insert(key, val); + break; } }; - } else { - //not the same lenght, fail to parse - return None; } - } - None => if reference.next().is_some() { + None => if path.next().is_some() { //not the same lenght, fail to parse return None; } else { @@ -97,8 +116,9 @@ impl Path { }, PathPart::Dynamic(key) => { //dynamic, store to hashmap - res.insert(key, hm.get::(k)?); + res.insert(key, hm.get::<&str>(&(k as &str))?.to_string()); } + PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"), } } } else { @@ -113,7 +133,7 @@ impl Path { Some(res) } - pub fn map(&self, param: &HashMap<&str, &str>) -> Option { + pub fn map(&self, param: &HashMap<&str, String>) -> Option { //Generate a path from a reference and a hashmap let mut res = String::new(); for seg in &self.path { @@ -121,7 +141,7 @@ impl Path { res.push('/'); match seg { PathPart::Static(val) => res.push_str(val), - PathPart::Dynamic(val) => res.push_str(param.get::(val)?), + PathPart::Dynamic(val) | PathPart::MultiDynamic(val) => res.push_str(param.get::(val)?), } } if let Some(ref keymap) = self.param { @@ -133,6 +153,7 @@ impl Path { match v { PathPart::Static(val) => res.push_str(val), PathPart::Dynamic(val) => res.push_str(param.get::(val)?), + PathPart::MultiDynamic(_) => panic!("PathPart::MultiDynamic is invalid in query part"), } res.push('&'); } @@ -145,6 +166,7 @@ impl Path { enum PathPart { Static(String), Dynamic(String), + MultiDynamic(String), } #[cfg(test)] @@ -165,7 +187,7 @@ mod tests{ assert_eq!(no_query.map(&HashMap::new()).unwrap(), "/path/no_query"); let mut hashmap = HashMap::new(); - hashmap.insert("key","value"); + hashmap.insert("key","value".to_owned()); assert_eq!(no_query.map(&hashmap).unwrap(), "/path/no_query"); } @@ -188,8 +210,59 @@ mod tests{ let mut hashmap = HashMap::new(); - hashmap.insert("key","value"); + hashmap.insert("key","value".to_owned()); let uri = query.map(&hashmap).unwrap(); assert!(uri=="/path/query?param=value¶m2=value2" || uri=="/path/query?param2=value2¶m=value"); } + + #[test] + fn test_dynamic_path_without_query() { + let no_query = Path::from("/path///values"); + assert!(no_query.extract("/path/with/dynamic/values/longer").is_none()); + assert!(no_query.extract("/path/with/dynamic").is_none()); + assert!(no_query.extract("/path/with/dynamic/non_value").is_none()); + assert!(no_query.extract("/path/with/dynamic/values?and=query").is_none()); + + let hashmap = no_query.extract("/path/containing/moving/values").unwrap(); + assert_eq!(hashmap.len(), 2); + assert_eq!(hashmap.get("with").unwrap(), "containing"); + assert_eq!(hashmap.get("dynamic").unwrap(), "moving"); + + assert!(no_query.map(&HashMap::new()).is_none()); + + let mut hashmap = HashMap::new(); + hashmap.insert("with","with".to_owned()); + hashmap.insert("dynamic","non_static".to_owned()); + assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values"); + hashmap.insert("random","value".to_owned()); + assert_eq!(no_query.map(&hashmap).unwrap(), "/path/with/non_static/values"); + } + + #[test] + fn test_dynamic_path_with_query() { + let query = Path::from("/path///values?key=&static=static"); + assert!(query.extract("/path/with/dynamic/values?key=something&static=error").is_none()); + + let hashmap = query.extract("/path/containing/moving/values?key=val&static=static").unwrap(); + assert_eq!(hashmap.len(), 3); + assert_eq!(hashmap.get("with").unwrap(), "containing"); + assert_eq!(hashmap.get("dynamic").unwrap(), "moving"); + assert_eq!(hashmap.get("value").unwrap(), "val"); + + let hashmap = query.extract("/path/containing/moving/values?static=static&key=val").unwrap(); + assert_eq!(hashmap.len(), 3); + assert_eq!(hashmap.get("with").unwrap(), "containing"); + assert_eq!(hashmap.get("dynamic").unwrap(), "moving"); + assert_eq!(hashmap.get("value").unwrap(), "val"); + + assert!(query.map(&HashMap::new()).is_none()); + + let mut hashmap = HashMap::new(); + hashmap.insert("with","with".to_owned()); + hashmap.insert("dynamic","non_static".to_owned()); + hashmap.insert("value", "something".to_owned()); + assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something"); + hashmap.insert("random","value".to_owned()); + assert!(query.map(&hashmap).unwrap()=="/path/with/non_static/values?key=something&static=static" || query.map(&hashmap).unwrap()=="/path/with/non_static/values?static=static&key=something"); + } }