mirror of
https://github.com/ntex-rs/ntex.git
synced 2025-04-04 21:37:58 +03:00
Fix search order for normal and prefix paths
This commit is contained in:
parent
f4006c72ae
commit
391cf24fa9
5 changed files with 248 additions and 212 deletions
|
@ -1,5 +1,9 @@
|
|||
# Changes
|
||||
|
||||
## [0.4.4] - 2021-06-14
|
||||
|
||||
* Fix search order for normal and prefix paths
|
||||
|
||||
## [0.4.3] - 2021-04-03
|
||||
|
||||
* Disable some of regex features
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "ntex-router"
|
||||
version = "0.4.3"
|
||||
version = "0.4.4"
|
||||
authors = ["ntex contributors <team@ntex.rs>"]
|
||||
description = "Path router"
|
||||
keywords = ["ntex"]
|
||||
|
@ -21,7 +21,7 @@ serde = "1.0"
|
|||
bytestring = "1.0"
|
||||
log = "0.4"
|
||||
http = { version = "0.2", optional = true }
|
||||
regex = { version = "1.4.5", default-features = false, features = ["std"] }
|
||||
regex = { version = "1.5.4", default-features = false, features = ["std"] }
|
||||
|
||||
[dev-dependencies]
|
||||
http = "0.2"
|
||||
|
|
|
@ -8,22 +8,27 @@ use super::{Resource, ResourcePath};
|
|||
#[derive(Debug, Clone)]
|
||||
pub(super) struct Tree {
|
||||
key: Vec<Segment>,
|
||||
value: Vec<Value>,
|
||||
children: Vec<Tree>,
|
||||
items: Vec<Item>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Item {
|
||||
Value(Value),
|
||||
Subtree(Tree),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
enum Value {
|
||||
Val(usize),
|
||||
Slesh(usize),
|
||||
Slash(usize),
|
||||
Prefix(usize),
|
||||
PrefixSlesh(usize),
|
||||
PrefixSlash(usize),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
enum PathState {
|
||||
Empty,
|
||||
Slesh,
|
||||
Slash,
|
||||
Tail,
|
||||
}
|
||||
|
||||
|
@ -31,9 +36,9 @@ impl Value {
|
|||
fn value(&self) -> usize {
|
||||
match self {
|
||||
Value::Val(v) => *v,
|
||||
Value::Slesh(v) => *v,
|
||||
Value::Slash(v) => *v,
|
||||
Value::Prefix(v) => *v,
|
||||
Value::PrefixSlesh(v) => *v,
|
||||
Value::PrefixSlash(v) => *v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +47,7 @@ impl Default for Tree {
|
|||
fn default() -> Tree {
|
||||
Tree {
|
||||
key: Vec::new(),
|
||||
value: Vec::new(),
|
||||
children: Vec::new(),
|
||||
items: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,9 +60,9 @@ impl Tree {
|
|||
|
||||
let val = if resource.tp[0].slesh {
|
||||
if resource.prefix {
|
||||
Value::PrefixSlesh(value)
|
||||
Value::PrefixSlash(value)
|
||||
} else {
|
||||
Value::Slesh(value)
|
||||
Value::Slash(value)
|
||||
}
|
||||
} else if resource.prefix {
|
||||
Value::Prefix(value)
|
||||
|
@ -71,9 +75,9 @@ impl Tree {
|
|||
for seg in &resource.tp[1..] {
|
||||
let val = if seg.slesh {
|
||||
if resource.prefix {
|
||||
Value::PrefixSlesh(value)
|
||||
Value::PrefixSlash(value)
|
||||
} else {
|
||||
Value::Slesh(value)
|
||||
Value::Slash(value)
|
||||
}
|
||||
} else if resource.prefix {
|
||||
Value::Prefix(value)
|
||||
|
@ -86,25 +90,21 @@ impl Tree {
|
|||
}
|
||||
|
||||
fn child(key: Vec<Segment>, value: Option<Value>) -> Tree {
|
||||
let value = if let Some(val) = value {
|
||||
vec![val]
|
||||
let items = if let Some(val) = value {
|
||||
vec![Item::Value(val)]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
Tree {
|
||||
key,
|
||||
value,
|
||||
children: Vec::new(),
|
||||
}
|
||||
Tree { key, items }
|
||||
}
|
||||
|
||||
pub(super) fn insert(&mut self, resource: &ResourceDef, value: usize) {
|
||||
for seg in &resource.tp {
|
||||
let value = if seg.slesh {
|
||||
if resource.prefix {
|
||||
Value::PrefixSlesh(value)
|
||||
Value::PrefixSlash(value)
|
||||
} else {
|
||||
Value::Slesh(value)
|
||||
Value::Slash(value)
|
||||
}
|
||||
} else if resource.prefix {
|
||||
Value::Prefix(value)
|
||||
|
@ -123,35 +123,36 @@ impl Tree {
|
|||
if p < self.key.len() {
|
||||
let child = Tree {
|
||||
key: self.key.split_off(p),
|
||||
value: mem::take(&mut self.value),
|
||||
children: mem::take(&mut self.children),
|
||||
items: mem::take(&mut self.items),
|
||||
};
|
||||
self.children.push(child);
|
||||
self.items.push(Item::Subtree(child));
|
||||
}
|
||||
|
||||
// update value if key is the same
|
||||
if p == key.len() {
|
||||
match value {
|
||||
Value::PrefixSlesh(v) => {
|
||||
self.children
|
||||
.push(Tree::child(Vec::new(), Some(Value::Prefix(v))));
|
||||
Value::PrefixSlash(v) => {
|
||||
self.items.push(Item::Subtree(Tree::child(
|
||||
Vec::new(),
|
||||
Some(Value::Prefix(v)),
|
||||
)));
|
||||
}
|
||||
value => self.value.push(value),
|
||||
value => self.items.push(Item::Value(value)),
|
||||
}
|
||||
} else {
|
||||
// insert into sub tree
|
||||
let mut child = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.find(|x| common_prefix(&x.key, &key[p..]) > 0);
|
||||
if let Some(ref mut child) = child {
|
||||
child.insert_path(key[p..].to_vec(), value)
|
||||
} else {
|
||||
self.children
|
||||
.push(Tree::child(key[p..].to_vec(), Some(value)));
|
||||
for child in &mut self.items {
|
||||
if let Item::Subtree(ref mut tree) = child {
|
||||
if common_prefix(&tree.key, &key[p..]) > 0 {
|
||||
tree.insert_path(key[p..].to_vec(), value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.items
|
||||
.push(Item::Subtree(Tree::child(key[p..].to_vec(), Some(value))));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn find<T, R>(&self, resource: &mut R) -> Option<usize>
|
||||
where
|
||||
|
@ -213,17 +214,42 @@ impl Tree {
|
|||
|
||||
if self.key.is_empty() {
|
||||
if path == "/" {
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
match val {
|
||||
Item::Value(val) => {
|
||||
let v = match val {
|
||||
Value::Slesh(v) | Value::Prefix(v) | Value::PrefixSlesh(v) => *v,
|
||||
Value::Slash(v)
|
||||
| Value::Prefix(v)
|
||||
| Value::PrefixSlash(v) => *v,
|
||||
_ => continue,
|
||||
};
|
||||
if check(v, resource) {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
Item::Subtree(ref tree) => {
|
||||
let result = tree.find_inner_wrapped(
|
||||
"",
|
||||
resource,
|
||||
check,
|
||||
1,
|
||||
&mut segments,
|
||||
insensitive,
|
||||
base_skip - 1,
|
||||
);
|
||||
if let Some((val, skip)) = result {
|
||||
let path = resource.resource_path();
|
||||
path.segments = segments;
|
||||
path.skip += skip as u16;
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if path.is_empty() {
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
match val {
|
||||
Item::Value(val) => {
|
||||
let v = match val {
|
||||
Value::Val(v) | Value::Prefix(v) => *v,
|
||||
_ => continue,
|
||||
|
@ -232,80 +258,90 @@ impl Tree {
|
|||
return Some(v);
|
||||
}
|
||||
}
|
||||
Item::Subtree(ref tree) => {
|
||||
let result = tree.find_inner_wrapped(
|
||||
"",
|
||||
resource,
|
||||
check,
|
||||
1,
|
||||
&mut segments,
|
||||
insensitive,
|
||||
base_skip,
|
||||
);
|
||||
if let Some((val, skip)) = result {
|
||||
let path = resource.resource_path();
|
||||
path.segments = segments;
|
||||
path.skip += skip as u16;
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for val in &self.value {
|
||||
let subtree_path = if let Some(spath) = path.strip_prefix('/') {
|
||||
spath
|
||||
} else {
|
||||
base_skip -= 1;
|
||||
path
|
||||
};
|
||||
|
||||
for val in &self.items {
|
||||
match val {
|
||||
Item::Value(val) => {
|
||||
let v = match val {
|
||||
Value::PrefixSlesh(v) => *v,
|
||||
Value::PrefixSlash(v) => *v,
|
||||
_ => continue,
|
||||
};
|
||||
if check(v, resource) {
|
||||
return Some(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let path = if let Some(path) = path.strip_prefix('/') {
|
||||
path
|
||||
} else {
|
||||
base_skip -= 1;
|
||||
path
|
||||
};
|
||||
|
||||
let res = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.find_inner_wrapped(
|
||||
path,
|
||||
Item::Subtree(ref tree) => {
|
||||
let result = tree.find_inner_wrapped(
|
||||
subtree_path,
|
||||
resource,
|
||||
check,
|
||||
1,
|
||||
&mut segments,
|
||||
insensitive,
|
||||
base_skip,
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.next();
|
||||
|
||||
return if let Some((val, skip)) = res {
|
||||
);
|
||||
if let Some((val, skip)) = result {
|
||||
let path = resource.resource_path();
|
||||
path.segments = segments;
|
||||
path.skip += skip as u16;
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
return Some(val);
|
||||
}
|
||||
|
||||
if path.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let path = if let Some(path) = path.strip_prefix('/') {
|
||||
path
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if !path.is_empty() {
|
||||
let subtree_path = if let Some(spath) = path.strip_prefix('/') {
|
||||
spath
|
||||
} else {
|
||||
base_skip -= 1;
|
||||
path
|
||||
};
|
||||
|
||||
if let Some((val, skip)) = self.find_inner_wrapped(
|
||||
path,
|
||||
let res = self.find_inner_wrapped(
|
||||
subtree_path,
|
||||
resource,
|
||||
check,
|
||||
1,
|
||||
&mut segments,
|
||||
insensitive,
|
||||
base_skip,
|
||||
) {
|
||||
);
|
||||
|
||||
if let Some((val, skip)) = res {
|
||||
let path = resource.resource_path();
|
||||
path.segments = segments;
|
||||
path.skip += skip as u16;
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
return Some(val);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn find_inner_wrapped<T, R, F>(
|
||||
|
@ -357,9 +393,9 @@ impl Tree {
|
|||
{
|
||||
if self.key.is_empty() {
|
||||
if path.is_empty() {
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
let v = match val {
|
||||
Value::Val(v) | Value::Prefix(v) => *v,
|
||||
Item::Value(Value::Val(v)) | Item::Value(Value::Prefix(v)) => *v,
|
||||
_ => continue,
|
||||
};
|
||||
if check(v, resource) {
|
||||
|
@ -367,9 +403,9 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
let v = match val {
|
||||
Value::Prefix(v) => *v,
|
||||
Item::Value(Value::Prefix(v)) => *v,
|
||||
_ => continue,
|
||||
};
|
||||
if check(v, resource) {
|
||||
|
@ -434,13 +470,15 @@ impl Tree {
|
|||
// we have to process checker for tail matches separately
|
||||
if tail && is_match {
|
||||
// checker
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
if let Item::Value(ref val) = val {
|
||||
let v = val.value();
|
||||
if check(v, resource) {
|
||||
return Some((v, skip + idx));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is_match
|
||||
} else {
|
||||
|
@ -458,30 +496,37 @@ impl Tree {
|
|||
return {
|
||||
if key.is_empty() {
|
||||
// checker
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
if let Item::Value(ref val) = val {
|
||||
let v = match val {
|
||||
Value::Val(v) | Value::Prefix(v) => *v,
|
||||
Value::Slesh(_) | Value::PrefixSlesh(_) => continue,
|
||||
Value::Slash(_) | Value::PrefixSlash(_) => {
|
||||
continue
|
||||
}
|
||||
};
|
||||
if check(v, resource) {
|
||||
return Some((v, skip));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
} else if key.is_empty() {
|
||||
path = &path[idx..];
|
||||
let subtree_path = if path.len() != 1 { &path[1..] } else { path };
|
||||
|
||||
let p = if path.is_empty() {
|
||||
PathState::Empty
|
||||
} else if path == "/" {
|
||||
PathState::Slesh
|
||||
PathState::Slash
|
||||
} else {
|
||||
PathState::Tail
|
||||
};
|
||||
|
||||
for val in &self.value {
|
||||
for val in &self.items {
|
||||
match val {
|
||||
Item::Value(val) => {
|
||||
let v = match val {
|
||||
Value::Val(v) => {
|
||||
if p == PathState::Empty {
|
||||
|
@ -490,48 +535,24 @@ impl Tree {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
Value::Slesh(v) => {
|
||||
if p == PathState::Slesh {
|
||||
Value::Slash(v) => {
|
||||
if p == PathState::Slash {
|
||||
*v
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Value::Prefix(v) => {
|
||||
if p == PathState::Slesh || p == PathState::Tail {
|
||||
if !self.children.is_empty() {
|
||||
let p = if path.len() != 1 {
|
||||
&path[1..]
|
||||
} else {
|
||||
path
|
||||
};
|
||||
if let Some(res) = self
|
||||
.children
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.find_inner_wrapped(
|
||||
p,
|
||||
resource,
|
||||
check,
|
||||
skip,
|
||||
segments,
|
||||
insensitive,
|
||||
base_skip,
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.next()
|
||||
if p == PathState::Slash || p == PathState::Tail
|
||||
{
|
||||
return Some(res);
|
||||
}
|
||||
}
|
||||
*v
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
Value::PrefixSlesh(v) => {
|
||||
if p == PathState::Slesh || p == PathState::Tail {
|
||||
Value::PrefixSlash(v) => {
|
||||
if p == PathState::Slash || p == PathState::Tail
|
||||
{
|
||||
*v
|
||||
} else {
|
||||
continue;
|
||||
|
@ -542,25 +563,23 @@ impl Tree {
|
|||
return Some((v, skip - 1));
|
||||
}
|
||||
}
|
||||
|
||||
let path = if path.len() != 1 { &path[1..] } else { path };
|
||||
|
||||
return self
|
||||
.children
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.find_inner_wrapped(
|
||||
path,
|
||||
Item::Subtree(ref tree) => {
|
||||
let result = tree.find_inner_wrapped(
|
||||
subtree_path,
|
||||
resource,
|
||||
check,
|
||||
skip,
|
||||
segments,
|
||||
insensitive,
|
||||
base_skip,
|
||||
)
|
||||
})
|
||||
.flatten()
|
||||
.next();
|
||||
);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return None;
|
||||
} else {
|
||||
path = &path[idx + 1..];
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ http-framework = ["h2", "http", "httparse",
|
|||
[dependencies]
|
||||
ntex-codec = "0.4.1"
|
||||
ntex-rt = "0.2.2"
|
||||
ntex-router = "0.4.3"
|
||||
ntex-router = "0.4.4"
|
||||
ntex-service = "0.1.9"
|
||||
ntex-macros = "0.1.3"
|
||||
ntex-util = "0.1.1"
|
||||
|
@ -55,15 +55,15 @@ base64 = "0.13"
|
|||
bitflags = "1.2"
|
||||
bytes = "1.0"
|
||||
bytestring = { version = "1.0", features = ["serde"] }
|
||||
derive_more = "0.99.13"
|
||||
derive_more = "0.99.14"
|
||||
futures-core = { version = "0.3.15", default-features = false, features = ["alloc"] }
|
||||
futures-sink = { version = "0.3.15", default-features = false, features = ["alloc"] }
|
||||
log = "0.4"
|
||||
mio = "0.7.10"
|
||||
mio = "0.7.11"
|
||||
num_cpus = "1.13"
|
||||
nanorand = { version = "0.5", default-features = false, features = ["std", "wyrand"] }
|
||||
pin-project-lite = "0.2"
|
||||
regex = { version = "1.4.5", default-features = false, features = ["std"] }
|
||||
regex = { version = "1.5.4", default-features = false, features = ["std"] }
|
||||
sha-1 = "0.9"
|
||||
slab = "0.4"
|
||||
serde = { version = "1.0", features=["derive"] }
|
||||
|
@ -73,7 +73,7 @@ tokio = { version = "1", default-features = false, features = ["sync"] }
|
|||
# http/web framework
|
||||
h2 = { version = "0.3", optional = true }
|
||||
http = { version = "0.2", optional = true }
|
||||
httparse = { version = "1.3", optional = true }
|
||||
httparse = { version = "1.4.1", optional = true }
|
||||
httpdate = { version = "1.0", optional = true }
|
||||
encoding_rs = { version = "0.8", optional = true }
|
||||
mime = { version = "0.3", optional = true }
|
||||
|
|
|
@ -900,25 +900,38 @@ mod tests {
|
|||
|
||||
#[crate::rt_test]
|
||||
async fn test_scope_guard() {
|
||||
let srv =
|
||||
init_service(App::new().service(
|
||||
web::scope("/app").guard(guard::Get()).service(
|
||||
let srv = init_service(
|
||||
App::new()
|
||||
.service(web::scope("/app").guard(guard::Get()).service(
|
||||
web::resource("/path1").to(|| async { HttpResponse::Ok() }),
|
||||
),
|
||||
))
|
||||
.service(web::scope("/app").guard(guard::Post()).service(
|
||||
web::resource("/path1").to(|| async { HttpResponse::NotModified() }),
|
||||
))
|
||||
.service(
|
||||
web::resource("/app/path1")
|
||||
.to(|| async { HttpResponse::NoContent() }),
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::POST)
|
||||
.to_request();
|
||||
let resp = srv.call(req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||
assert_eq!(resp.status(), StatusCode::NOT_MODIFIED);
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::GET)
|
||||
.to_request();
|
||||
let resp = srv.call(req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/path1")
|
||||
.method(Method::DELETE)
|
||||
.to_request();
|
||||
let resp = srv.call(req).await.unwrap();
|
||||
assert_eq!(resp.status(), StatusCode::NO_CONTENT);
|
||||
}
|
||||
|
||||
#[crate::rt_test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue