1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-11-30 13:31:09 +00:00

fix routes registration order

This commit is contained in:
Nikolay Kim 2018-02-22 05:48:18 -08:00
parent 4a9c1ae894
commit aff43cc8b8
4 changed files with 72 additions and 58 deletions

View file

@ -100,7 +100,7 @@ struct ApplicationParts<S> {
prefix: String,
settings: ServerSettings,
default: Resource<S>,
resources: HashMap<Pattern, Option<Resource<S>>>,
resources: Vec<(Pattern, Option<Resource<S>>)>,
handlers: Vec<(String, Box<RouteHandler<S>>)>,
external: HashMap<String, Pattern>,
encoding: ContentEncoding,
@ -123,7 +123,7 @@ impl Application<()> {
prefix: "/".to_owned(),
settings: ServerSettings::default(),
default: Resource::default_not_found(),
resources: HashMap::new(),
resources: Vec::new(),
handlers: Vec::new(),
external: HashMap::new(),
encoding: ContentEncoding::Auto,
@ -153,7 +153,7 @@ impl<S> Application<S> where S: 'static {
prefix: "/".to_owned(),
settings: ServerSettings::default(),
default: Resource::default_not_found(),
resources: HashMap::new(),
resources: Vec::new(),
handlers: Vec::new(),
external: HashMap::new(),
middlewares: Vec::new(),
@ -242,11 +242,7 @@ impl<S> Application<S> where S: 'static {
f(&mut resource);
let pattern = Pattern::new(resource.get_name(), path);
if parts.resources.contains_key(&pattern) {
panic!("Resource {:?} is registered.", path);
}
parts.resources.insert(pattern, Some(resource));
parts.resources.push((pattern, Some(resource)));
}
self
}
@ -354,7 +350,7 @@ impl<S> Application<S> where S: 'static {
let mut resources = parts.resources;
for (_, pattern) in parts.external {
resources.insert(pattern, None);
resources.push((pattern, None));
}
let (router, resources) = Router::new(prefix, parts.settings, resources);

View file

@ -883,9 +883,9 @@ mod tests {
let mut resource = Resource::<()>::default();
resource.name("index");
let mut map = HashMap::new();
map.insert(Pattern::new("index", "/{key}/"), Some(resource));
let (router, _) = Router::new("", ServerSettings::default(), map);
let mut routes = Vec::new();
routes.push((Pattern::new("index", "/{key}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes);
assert!(router.recognize(&mut req).is_some());
assert_eq!(req.match_info().get("key"), Some("value"));
@ -994,9 +994,8 @@ mod tests {
let mut resource = Resource::<()>::default();
resource.name("index");
let mut map = HashMap::new();
map.insert(Pattern::new("index", "/user/{name}.{ext}"), Some(resource));
let (router, _) = Router::new("/", ServerSettings::default(), map);
let routes = vec!((Pattern::new("index", "/user/{name}.{ext}"), Some(resource)));
let (router, _) = Router::new("/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/test/unknown"));
@ -1019,9 +1018,8 @@ mod tests {
let mut resource = Resource::<()>::default();
resource.name("index");
let mut map = HashMap::new();
map.insert(Pattern::new("index", "/user/{name}.{ext}"), Some(resource));
let (router, _) = Router::new("/prefix/", ServerSettings::default(), map);
let routes = vec![(Pattern::new("index", "/user/{name}.{ext}"), Some(resource))];
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes);
assert!(router.has_route("/user/test.html"));
assert!(!router.has_route("/prefix/user/test.html"));
@ -1036,9 +1034,9 @@ mod tests {
let mut resource = Resource::<()>::default();
resource.name("index");
let mut map = HashMap::new();
map.insert(Pattern::new("youtube", "https://youtube.com/watch/{video_id}"), None);
let (router, _) = Router::new::<()>("", ServerSettings::default(), map);
let routes = vec![
(Pattern::new("youtube", "https://youtube.com/watch/{video_id}"), None)];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes);
assert!(!router.has_route("https://youtube.com/watch/unknown"));
let req = req.with_state(Rc::new(()), router);

View file

@ -27,7 +27,7 @@ impl Router {
/// Create new router
pub fn new<S>(prefix: &str,
settings: ServerSettings,
map: HashMap<Pattern, Option<Resource<S>>>) -> (Router, Vec<Resource<S>>)
map: Vec<(Pattern, Option<Resource<S>>)>) -> (Router, Vec<Resource<S>>)
{
let prefix = prefix.trim().trim_right_matches('/').to_owned();
let mut named = HashMap::new();
@ -133,7 +133,7 @@ enum PatternElement {
Var(String),
}
#[derive(Clone)]
#[derive(Clone, Debug)]
enum PatternType {
Static(String),
Dynamic(Regex, Vec<String>),
@ -243,7 +243,8 @@ impl Pattern {
fn parse(pattern: &str) -> (String, Vec<PatternElement>, bool) {
const DEFAULT_PATTERN: &str = "[^/]+";
let mut re = String::from("/");
let mut re1 = String::from("^/");
let mut re2 = String::from("/");
let mut el = String::new();
let mut in_param = false;
let mut in_param_pattern = false;
@ -262,7 +263,7 @@ impl Pattern {
// In parameter segment: `{....}`
if ch == '}' {
elems.push(PatternElement::Var(param_name.clone()));
re.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern));
re1.push_str(&format!(r"(?P<{}>{})", &param_name, &param_pattern));
param_name.clear();
param_pattern = String::from(DEFAULT_PATTERN);
@ -287,15 +288,18 @@ impl Pattern {
elems.push(PatternElement::Str(el.clone()));
el.clear();
} else {
re.push_str(escape(&ch.to_string()).as_str());
re1.push_str(escape(&ch.to_string()).as_str());
re2.push(ch);
el.push(ch);
}
}
if is_dynamic {
re.insert(0, '^');
re.push('$');
}
let re = if is_dynamic {
re1.push('$');
re1
} else {
re2
};
(re, elems, is_dynamic)
}
}
@ -321,78 +325,95 @@ mod tests {
#[test]
fn test_recognizer() {
let mut routes = HashMap::new();
routes.insert(Pattern::new("", "/name"), Some(Resource::default()));
routes.insert(Pattern::new("", "/name/{val}"), Some(Resource::default()));
routes.insert(Pattern::new("", "/name/{val}/index.html"),
Some(Resource::default()));
routes.insert(Pattern::new("", "/file/{file}.{ext}"), Some(Resource::default()));
routes.insert(Pattern::new("", "/v{val}/{val2}/index.html"),
Some(Resource::default()));
routes.insert(Pattern::new("", "/v/{tail:.*}"), Some(Resource::default()));
routes.insert(Pattern::new("", "{test}/index.html"), Some(Resource::default()));
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default())),
(Pattern::new("", "/name/{val}/index.html"), Some(Resource::default())),
(Pattern::new("", "/file/{file}.{ext}"), Some(Resource::default())),
(Pattern::new("", "/v{val}/{val2}/index.html"), Some(Resource::default())),
(Pattern::new("", "/v/{tail:.*}"), Some(Resource::default())),
(Pattern::new("", "{test}/index.html"), Some(Resource::default()))];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(0));
assert!(req.match_info().is_empty());
let mut req = TestRequest::with_uri("/name/value").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(1));
assert_eq!(req.match_info().get("val").unwrap(), "value");
assert_eq!(&req.match_info()["val"], "value");
let mut req = TestRequest::with_uri("/name/value2/index.html").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(2));
assert_eq!(req.match_info().get("val").unwrap(), "value2");
let mut req = TestRequest::with_uri("/file/file.gz").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(3));
assert_eq!(req.match_info().get("file").unwrap(), "file");
assert_eq!(req.match_info().get("ext").unwrap(), "gz");
let mut req = TestRequest::with_uri("/vtest/ttt/index.html").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(4));
assert_eq!(req.match_info().get("val").unwrap(), "test");
assert_eq!(req.match_info().get("val2").unwrap(), "ttt");
let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(5));
assert_eq!(req.match_info().get("tail").unwrap(), "blah-blah/index.html");
let mut req = TestRequest::with_uri("/bbb/index.html").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(6));
assert_eq!(req.match_info().get("test").unwrap(), "bbb");
}
#[test]
fn test_recognizer_2() {
let routes = vec![
(Pattern::new("", "/index.json"), Some(Resource::default())),
(Pattern::new("", "/{source}.json"), Some(Resource::default()))];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/index.json").finish();
assert_eq!(rec.recognize(&mut req), Some(0));
let mut req = TestRequest::with_uri("/test.json").finish();
assert_eq!(rec.recognize(&mut req), Some(1));
}
#[test]
fn test_recognizer_with_prefix() {
let mut routes = HashMap::new();
routes.insert(Pattern::new("", "/name"), Some(Resource::default()));
routes.insert(Pattern::new("", "/name/{val}"), Some(Resource::default()));
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default()))];
let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
assert!(rec.recognize(&mut req).is_none());
let mut req = TestRequest::with_uri("/test/name").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(0));
let mut req = TestRequest::with_uri("/test/name/value").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(1));
assert_eq!(req.match_info().get("val").unwrap(), "value");
assert_eq!(&req.match_info()["val"], "value");
// same patterns
let mut routes = HashMap::new();
routes.insert(Pattern::new("", "/name"), Some(Resource::default()));
routes.insert(Pattern::new("", "/name/{val}"), Some(Resource::default()));
let routes = vec![
(Pattern::new("", "/name"), Some(Resource::default())),
(Pattern::new("", "/name/{val}"), Some(Resource::default()))];
let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes);
let mut req = TestRequest::with_uri("/name").finish();
assert!(rec.recognize(&mut req).is_none());
let mut req = TestRequest::with_uri("/test2/name").finish();
assert!(rec.recognize(&mut req).is_some());
assert_eq!(rec.recognize(&mut req), Some(0));
let mut req = TestRequest::with_uri("/test2/name-test").finish();
assert!(rec.recognize(&mut req).is_none());
let mut req = TestRequest::with_uri("/test2/name/ttt").finish();
assert_eq!(rec.recognize(&mut req), Some(1));
assert_eq!(&req.match_info()["val"], "ttt");
}
#[test]

View file

@ -4,7 +4,6 @@ use std::{net, thread};
use std::rc::Rc;
use std::sync::mpsc;
use std::str::FromStr;
use std::collections::HashMap;
use actix::{Arbiter, Addr, Syn, System, SystemRunner, msgs};
use cookie::Cookie;
@ -411,7 +410,7 @@ impl<S> TestRequest<S> {
let req = HttpRequest::new(method, uri, version, headers, payload);
req.as_mut().cookies = cookies;
req.as_mut().params = params;
let (router, _) = Router::new::<S>("/", ServerSettings::default(), HashMap::new());
let (router, _) = Router::new::<S>("/", ServerSettings::default(), Vec::new());
req.with_state(Rc::new(state), router)
}