1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

Refactor resource route parsing to allow repetition in the regexes

This commit is contained in:
Maciej Piechotka 2018-09-05 16:14:54 +02:00
parent 42f3773bec
commit 4251b0bc10
2 changed files with 58 additions and 65 deletions

View File

@ -5,7 +5,7 @@
### Fixed ### Fixed
* Fix system_exit in HttpServer #501 * Fix system_exit in HttpServer #501
* Fix parsing of route param containin regexes with repetition #500
## [0.7.5] - 2018-09-04 ## [0.7.5] - 2018-09-04

View File

@ -815,73 +815,56 @@ impl ResourceDef {
Ok(()) Ok(())
} }
fn parse( fn parse_param(
pattern: &str, for_prefix: bool, pattern: &str,
) -> (String, Vec<PatternElement>, bool, usize) { ) -> (PatternElement, String, &str) {
const DEFAULT_PATTERN: &str = "[^/]+"; const DEFAULT_PATTERN: &str = "[^/]+";
let mut params_nesting = 0usize;
let mut re1 = String::from("^"); let close_idx = pattern.find(|c| match c {
let mut re2 = String::new(); '{' => {params_nesting += 1; false},
let mut el = String::new(); '}' => {params_nesting -= 1; params_nesting == 0},
let mut in_param = false; _ => false
let mut in_param_pattern = false; }).expect("malformed param");
let mut param_name = String::new(); let (mut param, rem) = pattern.split_at(close_idx + 1);
let mut param_pattern = String::from(DEFAULT_PATTERN); param = &param[1..param.len() - 1]; // Remove outer brackets
let mut is_dynamic = false; let (name, pattern) = match param.find(":") {
let mut elems = Vec::new(); Some(idx) => {
let mut len = 0; let (name, pattern) = param.split_at(idx);
(name, &pattern[1..])
for ch in pattern.chars() {
if in_param {
// In parameter segment: `{....}`
if ch == '}' {
elems.push(PatternElement::Var(param_name.clone()));
re1.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern));
param_name.clear();
param_pattern = String::from(DEFAULT_PATTERN);
len = 0;
in_param_pattern = false;
in_param = false;
} else if ch == ':' {
// The parameter name has been determined; custom pattern land
in_param_pattern = true;
param_pattern.clear();
} else if in_param_pattern {
// Ignore leading whitespace for pattern
if !(ch == ' ' && param_pattern.is_empty()) {
param_pattern.push(ch);
}
} else {
param_name.push(ch);
}
} else if ch == '{' {
in_param = true;
is_dynamic = true;
elems.push(PatternElement::Str(el.clone()));
el.clear();
} else {
re1.push_str(escape(&ch.to_string()).as_str());
re2.push(ch);
el.push(ch);
len += 1;
} }
} None => (param, DEFAULT_PATTERN)
if !el.is_empty() {
elems.push(PatternElement::Str(el.clone()));
}
let re = if is_dynamic {
if !for_prefix {
re1.push('$');
}
re1
} else {
re2
}; };
(re, elems, is_dynamic, len) (PatternElement::Var(name.to_string()), format!(r"(?P<{}>{})", &name, &pattern), rem)
}
fn parse(
mut pattern: &str, for_prefix: bool,
) -> (String, Vec<PatternElement>, bool, usize) {
if pattern.find("{").is_none() {
return (String::from(pattern), vec![PatternElement::Str(String::from(pattern))], false, pattern.chars().count())
};
let mut elems = Vec::new();
let mut re = String::from("^");
while let Some(idx) = pattern.find("{") {
let (prefix, rem) = pattern.split_at(idx);
elems.push(PatternElement::Str(String::from(prefix)));
re.push_str(&escape(prefix));
let (param_pattern, re_part, rem) = Self::parse_param(rem);
elems.push(param_pattern);
re.push_str(&re_part);
pattern = rem;
}
elems.push(PatternElement::Str(String::from(pattern)));
re.push_str(&escape(pattern));
if !for_prefix {
re.push_str("$");
}
(re, elems, true, pattern.chars().count())
} }
} }
@ -1072,6 +1055,16 @@ mod tests {
let info = re.match_with_params(&req, 0).unwrap(); let info = re.match_with_params(&req, 0).unwrap();
assert_eq!(info.get("version").unwrap(), "151"); assert_eq!(info.get("version").unwrap(), "151");
assert_eq!(info.get("id").unwrap(), "adahg32"); assert_eq!(info.get("id").unwrap(), "adahg32");
let re = ResourceDef::new("/{id:[[:digit:]]{6}}");
assert!(re.is_match("/012345"));
assert!(!re.is_match("/012"));
assert!(!re.is_match("/01234567"));
assert!(!re.is_match("/XXXXXX"));
let req = TestRequest::with_uri("/012345").finish();
let info = re.match_with_params(&req, 0).unwrap();
assert_eq!(info.get("id").unwrap(), "012345");
} }
#[test] #[test]