mirror of https://github.com/fafhrd91/actix-web
feat: Add improvements in resource path resolve
This commit is contained in:
parent
072fdc182d
commit
ffa95b7f60
|
@ -662,13 +662,13 @@ mod tests {
|
||||||
let rdef = ResourceDef::new("/{key}");
|
let rdef = ResourceDef::new("/{key}");
|
||||||
|
|
||||||
let mut path = Path::new("/%25");
|
let mut path = Path::new("/%25");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
let segment: String = serde::Deserialize::deserialize(de).unwrap();
|
let segment: String = serde::Deserialize::deserialize(de).unwrap();
|
||||||
assert_eq!(segment, "%");
|
assert_eq!(segment, "%");
|
||||||
|
|
||||||
let mut path = Path::new("/%2F");
|
let mut path = Path::new("/%2F");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
let segment: String = serde::Deserialize::deserialize(de).unwrap();
|
let segment: String = serde::Deserialize::deserialize(de).unwrap();
|
||||||
assert_eq!(segment, "/")
|
assert_eq!(segment, "/")
|
||||||
|
@ -679,7 +679,7 @@ mod tests {
|
||||||
let rdef = ResourceDef::new("/{key}/{value}");
|
let rdef = ResourceDef::new("/{key}/{value}");
|
||||||
|
|
||||||
let mut path = Path::new("/%30%25/%30%2F");
|
let mut path = Path::new("/%30%25/%30%2F");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
let segment: (String, String) = serde::Deserialize::deserialize(de).unwrap();
|
let segment: (String, String) = serde::Deserialize::deserialize(de).unwrap();
|
||||||
assert_eq!(segment.0, "0%");
|
assert_eq!(segment.0, "0%");
|
||||||
|
@ -697,7 +697,7 @@ mod tests {
|
||||||
let rdef = ResourceDef::new("/{key}/{value}");
|
let rdef = ResourceDef::new("/{key}/{value}");
|
||||||
|
|
||||||
let mut path = Path::new("/%25/%2F");
|
let mut path = Path::new("/%25/%2F");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
let vals: Vals = serde::Deserialize::deserialize(de).unwrap();
|
let vals: Vals = serde::Deserialize::deserialize(de).unwrap();
|
||||||
assert_eq!(vals.key, "%");
|
assert_eq!(vals.key, "%");
|
||||||
|
@ -714,7 +714,7 @@ mod tests {
|
||||||
let rdef = ResourceDef::new("/{val}");
|
let rdef = ResourceDef::new("/{val}");
|
||||||
|
|
||||||
let mut path = Path::new("/X");
|
let mut path = Path::new("/X");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
let params: Params<'_> = serde::Deserialize::deserialize(de).unwrap();
|
let params: Params<'_> = serde::Deserialize::deserialize(de).unwrap();
|
||||||
assert_eq!(params.val, "X");
|
assert_eq!(params.val, "X");
|
||||||
|
@ -723,7 +723,7 @@ mod tests {
|
||||||
assert_eq!(params, "X");
|
assert_eq!(params, "X");
|
||||||
|
|
||||||
let mut path = Path::new("/%2F");
|
let mut path = Path::new("/%2F");
|
||||||
rdef.capture_match_info(&mut path);
|
rdef.resolve_resource_if_matches(&mut path);
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
assert!(<Params<'_> as serde::Deserialize>::deserialize(de).is_err());
|
assert!(<Params<'_> as serde::Deserialize>::deserialize(de).is_err());
|
||||||
let de = PathDeserializer::new(&path);
|
let de = PathDeserializer::new(&path);
|
||||||
|
|
|
@ -8,9 +8,9 @@ use std::{
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
IntoPatterns,
|
||||||
path::PathItem,
|
path::PathItem,
|
||||||
regex_set::{escape, Regex, RegexSet},
|
Patterns, regex_set::{escape, Regex, RegexSet}, Resource, ResourcePath,
|
||||||
IntoPatterns, Patterns, Resource, ResourcePath,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
const MAX_DYNAMIC_SEGMENTS: usize = 16;
|
||||||
|
@ -96,7 +96,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
||||||
/// assert!(!resource.is_match("/user/"));
|
/// assert!(!resource.is_match("/user/"));
|
||||||
///
|
///
|
||||||
/// let mut path = Path::new("/user/123");
|
/// let mut path = Path::new("/user/123");
|
||||||
/// resource.capture_match_info(&mut path);
|
/// resource.resolve_resource_if_matches(&mut path);
|
||||||
/// assert_eq!(path.get("id").unwrap(), "123");
|
/// assert_eq!(path.get("id").unwrap(), "123");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -171,7 +171,7 @@ const REGEX_FLAGS: &str = "(?s-m)";
|
||||||
/// assert!(resource.is_match("/blob/HEAD/README.md"));
|
/// assert!(resource.is_match("/blob/HEAD/README.md"));
|
||||||
///
|
///
|
||||||
/// let mut path = Path::new("/blob/main/LICENSE");
|
/// let mut path = Path::new("/blob/main/LICENSE");
|
||||||
/// resource.capture_match_info(&mut path);
|
/// resource.resolve_resource_if_matches(&mut path);
|
||||||
/// assert_eq!(path.get("tail").unwrap(), "main/LICENSE");
|
/// assert_eq!(path.get("tail").unwrap(), "main/LICENSE");
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
@ -249,6 +249,17 @@ enum PatternType {
|
||||||
DynamicSet(RegexSet, Vec<(Regex, Vec<&'static str>)>),
|
DynamicSet(RegexSet, Vec<(Regex, Vec<&'static str>)>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ResourceMatchInfo<'a> {
|
||||||
|
Static {
|
||||||
|
matched_len: u16,
|
||||||
|
},
|
||||||
|
Dynamic {
|
||||||
|
matched_len: u16,
|
||||||
|
matched_vars: &'a [&'static str],
|
||||||
|
segments: [PathItem; MAX_DYNAMIC_SEGMENTS],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
impl ResourceDef {
|
impl ResourceDef {
|
||||||
/// Constructs a new resource definition from patterns.
|
/// Constructs a new resource definition from patterns.
|
||||||
///
|
///
|
||||||
|
@ -440,7 +451,7 @@ impl ResourceDef {
|
||||||
/// assert_eq!(iter.next().unwrap(), "/root");
|
/// assert_eq!(iter.next().unwrap(), "/root");
|
||||||
/// assert_eq!(iter.next().unwrap(), "/backup");
|
/// assert_eq!(iter.next().unwrap(), "/backup");
|
||||||
/// assert!(iter.next().is_none());
|
/// assert!(iter.next().is_none());
|
||||||
pub fn pattern_iter(&self) -> impl Iterator<Item = &str> {
|
pub fn pattern_iter(&self) -> impl Iterator<Item=&str> {
|
||||||
struct PatternIter<'a> {
|
struct PatternIter<'a> {
|
||||||
patterns: &'a Patterns,
|
patterns: &'a Patterns,
|
||||||
list_idx: usize,
|
list_idx: usize,
|
||||||
|
@ -623,18 +634,24 @@ impl ResourceDef {
|
||||||
///
|
///
|
||||||
/// let resource = ResourceDef::prefix("/user/{id}");
|
/// let resource = ResourceDef::prefix("/user/{id}");
|
||||||
/// let mut path = Path::new("/user/123/stars");
|
/// let mut path = Path::new("/user/123/stars");
|
||||||
/// assert!(resource.capture_match_info(&mut path));
|
/// assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
/// assert_eq!(path.get("id").unwrap(), "123");
|
/// assert_eq!(path.get("id").unwrap(), "123");
|
||||||
/// assert_eq!(path.unprocessed(), "/stars");
|
/// assert_eq!(path.unprocessed(), "/stars");
|
||||||
///
|
///
|
||||||
/// let resource = ResourceDef::new("/blob/{path}*");
|
/// let resource = ResourceDef::new("/blob/{path}*");
|
||||||
/// let mut path = Path::new("/blob/HEAD/Cargo.toml");
|
/// let mut path = Path::new("/blob/HEAD/Cargo.toml");
|
||||||
/// assert!(resource.capture_match_info(&mut path));
|
/// assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
/// assert_eq!(path.get("path").unwrap(), "HEAD/Cargo.toml");
|
/// assert_eq!(path.get("path").unwrap(), "HEAD/Cargo.toml");
|
||||||
/// assert_eq!(path.unprocessed(), "");
|
/// assert_eq!(path.unprocessed(), "");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn capture_match_info<R: Resource>(&self, resource: &mut R) -> bool {
|
pub fn resolve_resource_if_matches<R: Resource>(&self, resource: &mut R) -> bool {
|
||||||
self.capture_match_info_fn(resource, |_| true)
|
match self.capture_match_info(resource) {
|
||||||
|
None => { false }
|
||||||
|
Some(match_info) => {
|
||||||
|
self.resolve_resource(resource, match_info);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Collects dynamic segment values into `resource` after matching paths and executing
|
/// Collects dynamic segment values into `resource` after matching paths and executing
|
||||||
|
@ -674,80 +691,90 @@ impl ResourceDef {
|
||||||
/// assert!(!try_match(&resource, &mut path));
|
/// assert!(!try_match(&resource, &mut path));
|
||||||
/// assert_eq!(path.unprocessed(), "/user/admin/stars");
|
/// assert_eq!(path.unprocessed(), "/user/admin/stars");
|
||||||
/// ```
|
/// ```
|
||||||
pub fn capture_match_info_fn<R, F>(&self, resource: &mut R, check_fn: F) -> bool
|
pub fn capture_match_info<R>(&self, resource: &mut R) -> Option<ResourceMatchInfo<'_>>
|
||||||
where
|
where
|
||||||
R: Resource,
|
R: Resource,
|
||||||
F: FnOnce(&R) -> bool,
|
|
||||||
{
|
{
|
||||||
let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default();
|
|
||||||
let path = resource.resource_path();
|
let path = resource.resource_path();
|
||||||
let path_str = path.unprocessed();
|
let path_str = path.unprocessed();
|
||||||
|
match &self.pat_type {
|
||||||
let (matched_len, matched_vars) = match &self.pat_type {
|
|
||||||
PatternType::Static(pattern) => match self.static_match(pattern, path_str) {
|
PatternType::Static(pattern) => match self.static_match(pattern, path_str) {
|
||||||
Some(len) => (len, None),
|
Some(len) => Some(ResourceMatchInfo::Static { matched_len: len as u16 }),
|
||||||
None => return false,
|
None => return None,
|
||||||
},
|
},
|
||||||
|
|
||||||
PatternType::Dynamic(re, names) => {
|
PatternType::Dynamic(re, names) => {
|
||||||
let captures = match re.captures(path.unprocessed()) {
|
let captures = match re.captures(path_str) {
|
||||||
Some(captures) => captures,
|
Some(captures) => captures,
|
||||||
_ => return false,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default();
|
||||||
for (no, name) in names.iter().enumerate() {
|
for (no, name) in names.iter().enumerate() {
|
||||||
if let Some(m) = captures.name(name) {
|
if let Some(m) = captures.name(name) {
|
||||||
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
|
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
|
||||||
} else {
|
} else {
|
||||||
error!("Dynamic path match but not all segments found: {}", name);
|
error!("Dynamic path match but not all segments found: {}", name);
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(captures[1].len(), Some(names))
|
Some(ResourceMatchInfo::Dynamic {
|
||||||
|
matched_len: captures[1].len() as u16,
|
||||||
|
matched_vars: names,
|
||||||
|
segments,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
PatternType::DynamicSet(re, params) => {
|
PatternType::DynamicSet(re, params) => {
|
||||||
let path = path.unprocessed();
|
let (pattern, names) = match re.first_match_idx(path_str) {
|
||||||
let (pattern, names) = match re.first_match_idx(path) {
|
|
||||||
Some(idx) => ¶ms[idx],
|
Some(idx) => ¶ms[idx],
|
||||||
_ => return false,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let captures = match pattern.captures(path.path()) {
|
let captures = match pattern.captures(path_str) {
|
||||||
Some(captures) => captures,
|
Some(captures) => captures,
|
||||||
_ => return false,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut segments = <[PathItem; MAX_DYNAMIC_SEGMENTS]>::default();
|
||||||
for (no, name) in names.iter().enumerate() {
|
for (no, name) in names.iter().enumerate() {
|
||||||
if let Some(m) = captures.name(name) {
|
if let Some(m) = captures.name(name) {
|
||||||
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
|
segments[no] = PathItem::Segment(m.start() as u16, m.end() as u16);
|
||||||
} else {
|
} else {
|
||||||
error!("Dynamic path match but not all segments found: {}", name);
|
error!("Dynamic path match but not all segments found: {}", name);
|
||||||
return false;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(captures[1].len(), Some(names))
|
Some(ResourceMatchInfo::Dynamic {
|
||||||
|
matched_len: captures[1].len() as u16,
|
||||||
|
matched_vars: names,
|
||||||
|
segments,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if !check_fn(resource) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modify `path` to skip matched part and store matched segments
|
|
||||||
|
///
|
||||||
|
pub fn resolve_resource<R>(&self, resource: &mut R, match_info: ResourceMatchInfo<'_>)
|
||||||
|
where
|
||||||
|
R: Resource,
|
||||||
|
{
|
||||||
let path = resource.resource_path();
|
let path = resource.resource_path();
|
||||||
|
match match_info {
|
||||||
if let Some(vars) = matched_vars {
|
ResourceMatchInfo::Static { matched_len } => {
|
||||||
for i in 0..vars.len() {
|
path.skip(matched_len);
|
||||||
path.add(vars[i], mem::take(&mut segments[i]));
|
|
||||||
}
|
}
|
||||||
|
ResourceMatchInfo::Dynamic { matched_len, matched_vars, mut segments } => {
|
||||||
|
for i in 0..matched_vars.len() {
|
||||||
|
path.add(matched_vars[i], mem::take(&mut segments[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
path.skip(matched_len as u16);
|
path.skip(matched_len);
|
||||||
|
}
|
||||||
true
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assembles resource path using a closure that maps variable segment names to values.
|
/// Assembles resource path using a closure that maps variable segment names to values.
|
||||||
|
@ -1118,9 +1145,10 @@ pub(crate) fn insert_slash(path: &str) -> Cow<'_, str> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
|
||||||
use crate::Path;
|
use crate::Path;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn equivalence() {
|
fn equivalence() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -1171,7 +1199,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/name~"));
|
assert!(!re.is_match("/name~"));
|
||||||
|
|
||||||
let mut path = Path::new("/name");
|
let mut path = Path::new("/name");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
assert_eq!(re.find_match("/name"), Some(5));
|
assert_eq!(re.find_match("/name"), Some(5));
|
||||||
|
@ -1189,7 +1217,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/user/profile/profile"));
|
assert!(!re.is_match("/user/profile/profile"));
|
||||||
|
|
||||||
let mut path = Path::new("/user/profile");
|
let mut path = Path::new("/user/profile");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,12 +1230,12 @@ mod tests {
|
||||||
assert!(!re.is_match("/user/2345/sdg"));
|
assert!(!re.is_match("/user/2345/sdg"));
|
||||||
|
|
||||||
let mut path = Path::new("/user/profile");
|
let mut path = Path::new("/user/profile");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "profile");
|
assert_eq!(path.get("id").unwrap(), "profile");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
let mut path = Path::new("/user/1245125");
|
let mut path = Path::new("/user/1245125");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "1245125");
|
assert_eq!(path.get("id").unwrap(), "1245125");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
|
@ -1217,7 +1245,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/resource"));
|
assert!(!re.is_match("/resource"));
|
||||||
|
|
||||||
let mut path = Path::new("/v151/resource/adage32");
|
let mut path = Path::new("/v151/resource/adage32");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("version").unwrap(), "151");
|
assert_eq!(path.get("version").unwrap(), "151");
|
||||||
assert_eq!(path.get("id").unwrap(), "adage32");
|
assert_eq!(path.get("id").unwrap(), "adage32");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
@ -1229,7 +1257,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/XXXXXX"));
|
assert!(!re.is_match("/XXXXXX"));
|
||||||
|
|
||||||
let mut path = Path::new("/012345");
|
let mut path = Path::new("/012345");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "012345");
|
assert_eq!(path.get("id").unwrap(), "012345");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
}
|
}
|
||||||
|
@ -1249,12 +1277,12 @@ mod tests {
|
||||||
assert!(!re.is_match("/user/2345/sdg"));
|
assert!(!re.is_match("/user/2345/sdg"));
|
||||||
|
|
||||||
let mut path = Path::new("/user/profile");
|
let mut path = Path::new("/user/profile");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "profile");
|
assert_eq!(path.get("id").unwrap(), "profile");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
let mut path = Path::new("/user/1245125");
|
let mut path = Path::new("/user/1245125");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "1245125");
|
assert_eq!(path.get("id").unwrap(), "1245125");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
|
@ -1263,7 +1291,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/resource"));
|
assert!(!re.is_match("/resource"));
|
||||||
|
|
||||||
let mut path = Path::new("/v151/resource/adage32");
|
let mut path = Path::new("/v151/resource/adage32");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("version").unwrap(), "151");
|
assert_eq!(path.get("version").unwrap(), "151");
|
||||||
assert_eq!(path.get("id").unwrap(), "adage32");
|
assert_eq!(path.get("id").unwrap(), "adage32");
|
||||||
|
|
||||||
|
@ -1277,7 +1305,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/static/a"));
|
assert!(!re.is_match("/static/a"));
|
||||||
|
|
||||||
let mut path = Path::new("/012345");
|
let mut path = Path::new("/012345");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "012345");
|
assert_eq!(path.get("id").unwrap(), "012345");
|
||||||
|
|
||||||
let re = ResourceDef::new([
|
let re = ResourceDef::new([
|
||||||
|
@ -1314,7 +1342,7 @@ mod tests {
|
||||||
assert_eq!(re.find_match("/12345"), None);
|
assert_eq!(re.find_match("/12345"), None);
|
||||||
|
|
||||||
let mut path = Path::new("/151/res");
|
let mut path = Path::new("/151/res");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "151");
|
assert_eq!(path.get("id").unwrap(), "151");
|
||||||
assert_eq!(path.unprocessed(), "/res");
|
assert_eq!(path.unprocessed(), "/res");
|
||||||
}
|
}
|
||||||
|
@ -1324,19 +1352,19 @@ mod tests {
|
||||||
let re = ResourceDef::new("/user/-{id}*");
|
let re = ResourceDef::new("/user/-{id}*");
|
||||||
|
|
||||||
let mut path = Path::new("/user/-profile");
|
let mut path = Path::new("/user/-profile");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "profile");
|
assert_eq!(path.get("id").unwrap(), "profile");
|
||||||
|
|
||||||
let mut path = Path::new("/user/-2345");
|
let mut path = Path::new("/user/-2345");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "2345");
|
assert_eq!(path.get("id").unwrap(), "2345");
|
||||||
|
|
||||||
let mut path = Path::new("/user/-2345/");
|
let mut path = Path::new("/user/-2345/");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "2345/");
|
assert_eq!(path.get("id").unwrap(), "2345/");
|
||||||
|
|
||||||
let mut path = Path::new("/user/-2345/sdg");
|
let mut path = Path::new("/user/-2345/sdg");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "2345/sdg");
|
assert_eq!(path.get("id").unwrap(), "2345/sdg");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1364,7 +1392,7 @@ mod tests {
|
||||||
let re = ResourceDef::new("/user/{id}/{tail}*");
|
let re = ResourceDef::new("/user/{id}/{tail}*");
|
||||||
assert!(!re.is_match("/user/2345"));
|
assert!(!re.is_match("/user/2345"));
|
||||||
let mut path = Path::new("/user/2345/sdg");
|
let mut path = Path::new("/user/2345/sdg");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "2345");
|
assert_eq!(path.get("id").unwrap(), "2345");
|
||||||
assert_eq!(path.get("tail").unwrap(), "sdg");
|
assert_eq!(path.get("tail").unwrap(), "sdg");
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
@ -1379,7 +1407,7 @@ mod tests {
|
||||||
|
|
||||||
let re = ResourceDef::new("/a{x}b/test/a{y}b");
|
let re = ResourceDef::new("/a{x}b/test/a{y}b");
|
||||||
let mut path = Path::new("/a\nb/test/a\nb");
|
let mut path = Path::new("/a\nb/test/a\nb");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("x").unwrap(), "\n");
|
assert_eq!(path.get("x").unwrap(), "\n");
|
||||||
assert_eq!(path.get("y").unwrap(), "\n");
|
assert_eq!(path.get("y").unwrap(), "\n");
|
||||||
|
|
||||||
|
@ -1388,12 +1416,12 @@ mod tests {
|
||||||
|
|
||||||
let re = ResourceDef::new("/user/{id}*");
|
let re = ResourceDef::new("/user/{id}*");
|
||||||
let mut path = Path::new("/user/a\nb/a\nb");
|
let mut path = Path::new("/user/a\nb/a\nb");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "a\nb/a\nb");
|
assert_eq!(path.get("id").unwrap(), "a\nb/a\nb");
|
||||||
|
|
||||||
let re = ResourceDef::new("/user/{id:.*}");
|
let re = ResourceDef::new("/user/{id:.*}");
|
||||||
let mut path = Path::new("/user/a\nb/a\nb");
|
let mut path = Path::new("/user/a\nb/a\nb");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "a\nb/a\nb");
|
assert_eq!(path.get("id").unwrap(), "a\nb/a\nb");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1403,16 +1431,16 @@ mod tests {
|
||||||
let re = ResourceDef::new("/user/{id}/test");
|
let re = ResourceDef::new("/user/{id}/test");
|
||||||
|
|
||||||
let mut path = Path::new("/user/2345/test");
|
let mut path = Path::new("/user/2345/test");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "2345");
|
assert_eq!(path.get("id").unwrap(), "2345");
|
||||||
|
|
||||||
let mut path = Path::new("/user/qwe%25/test");
|
let mut path = Path::new("/user/qwe%25/test");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "qwe%25");
|
assert_eq!(path.get("id").unwrap(), "qwe%25");
|
||||||
|
|
||||||
let uri = http::Uri::try_from("/user/qwe%25/test").unwrap();
|
let uri = http::Uri::try_from("/user/qwe%25/test").unwrap();
|
||||||
let mut path = Path::new(uri);
|
let mut path = Path::new(uri);
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.get("id").unwrap(), "qwe%25");
|
assert_eq!(path.get("id").unwrap(), "qwe%25");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1429,11 +1457,11 @@ mod tests {
|
||||||
assert!(!re.is_match("/name~"));
|
assert!(!re.is_match("/name~"));
|
||||||
|
|
||||||
let mut path = Path::new("/name");
|
let mut path = Path::new("/name");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.unprocessed(), "");
|
assert_eq!(path.unprocessed(), "");
|
||||||
|
|
||||||
let mut path = Path::new("/name/test");
|
let mut path = Path::new("/name/test");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.unprocessed(), "/test");
|
assert_eq!(path.unprocessed(), "/test");
|
||||||
|
|
||||||
assert_eq!(re.find_match("/name"), Some(5));
|
assert_eq!(re.find_match("/name"), Some(5));
|
||||||
|
@ -1449,10 +1477,10 @@ mod tests {
|
||||||
assert!(!re.is_match("/name"));
|
assert!(!re.is_match("/name"));
|
||||||
|
|
||||||
let mut path = Path::new("/name/gs");
|
let mut path = Path::new("/name/gs");
|
||||||
assert!(!re.capture_match_info(&mut path));
|
assert!(!re.resolve_resource_if_matches(&mut path));
|
||||||
|
|
||||||
let mut path = Path::new("/name//gs");
|
let mut path = Path::new("/name//gs");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(path.unprocessed(), "/gs");
|
assert_eq!(path.unprocessed(), "/gs");
|
||||||
|
|
||||||
let re = ResourceDef::root_prefix("name/");
|
let re = ResourceDef::root_prefix("name/");
|
||||||
|
@ -1462,7 +1490,7 @@ mod tests {
|
||||||
assert!(!re.is_match("/name"));
|
assert!(!re.is_match("/name"));
|
||||||
|
|
||||||
let mut path = Path::new("/name/gs");
|
let mut path = Path::new("/name/gs");
|
||||||
assert!(!re.capture_match_info(&mut path));
|
assert!(!re.resolve_resource_if_matches(&mut path));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1481,13 +1509,13 @@ mod tests {
|
||||||
assert_eq!(re.find_match(""), None);
|
assert_eq!(re.find_match(""), None);
|
||||||
|
|
||||||
let mut path = Path::new("/test2/");
|
let mut path = Path::new("/test2/");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(&path["name"], "test2");
|
assert_eq!(&path["name"], "test2");
|
||||||
assert_eq!(&path[0], "test2");
|
assert_eq!(&path[0], "test2");
|
||||||
assert_eq!(path.unprocessed(), "/");
|
assert_eq!(path.unprocessed(), "/");
|
||||||
|
|
||||||
let mut path = Path::new("/test2/subpath1/subpath2/index.html");
|
let mut path = Path::new("/test2/subpath1/subpath2/index.html");
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
assert_eq!(&path["name"], "test2");
|
assert_eq!(&path["name"], "test2");
|
||||||
assert_eq!(&path[0], "test2");
|
assert_eq!(&path[0], "test2");
|
||||||
assert_eq!(path.unprocessed(), "/subpath1/subpath2/index.html");
|
assert_eq!(path.unprocessed(), "/subpath1/subpath2/index.html");
|
||||||
|
@ -1543,7 +1571,7 @@ mod tests {
|
||||||
assert!(resource.resource_path_from_iter(
|
assert!(resource.resource_path_from_iter(
|
||||||
&mut s,
|
&mut s,
|
||||||
#[allow(clippy::useless_vec)]
|
#[allow(clippy::useless_vec)]
|
||||||
&mut vec!["item", "item2"].iter()
|
&mut vec!["item", "item2"].iter(),
|
||||||
));
|
));
|
||||||
assert_eq!(s, "/user/item/item2/");
|
assert_eq!(s, "/user/item/item2/");
|
||||||
}
|
}
|
||||||
|
@ -1561,22 +1589,22 @@ mod tests {
|
||||||
let resource = ResourceDef::new(["/user/{id}", "/profile/{id}"]);
|
let resource = ResourceDef::new(["/user/{id}", "/profile/{id}"]);
|
||||||
|
|
||||||
let mut path = Path::new("/user/123");
|
let mut path = Path::new("/user/123");
|
||||||
assert!(resource.capture_match_info(&mut path));
|
assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
assert!(path.get("id").is_some());
|
assert!(path.get("id").is_some());
|
||||||
|
|
||||||
let mut path = Path::new("/profile/123");
|
let mut path = Path::new("/profile/123");
|
||||||
assert!(resource.capture_match_info(&mut path));
|
assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
assert!(path.get("id").is_some());
|
assert!(path.get("id").is_some());
|
||||||
|
|
||||||
let resource = ResourceDef::new(["/user/{id}", "/profile/{uid}"]);
|
let resource = ResourceDef::new(["/user/{id}", "/profile/{uid}"]);
|
||||||
|
|
||||||
let mut path = Path::new("/user/123");
|
let mut path = Path::new("/user/123");
|
||||||
assert!(resource.capture_match_info(&mut path));
|
assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
assert!(path.get("id").is_some());
|
assert!(path.get("id").is_some());
|
||||||
assert!(path.get("uid").is_none());
|
assert!(path.get("uid").is_none());
|
||||||
|
|
||||||
let mut path = Path::new("/profile/123");
|
let mut path = Path::new("/profile/123");
|
||||||
assert!(resource.capture_match_info(&mut path));
|
assert!(resource.resolve_resource_if_matches(&mut path));
|
||||||
assert!(path.get("id").is_none());
|
assert!(path.get("id").is_none());
|
||||||
assert!(path.get("uid").is_some());
|
assert!(path.get("uid").is_some());
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,12 +13,13 @@ pub struct ResourceId(pub u16);
|
||||||
/// not required.
|
/// not required.
|
||||||
pub struct Router<T, U = ()> {
|
pub struct Router<T, U = ()> {
|
||||||
routes: Vec<(ResourceDef, T, U)>,
|
routes: Vec<(ResourceDef, T, U)>,
|
||||||
|
max_path_conflicts: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> Router<T, U> {
|
impl<T, U> Router<T, U> {
|
||||||
/// Constructs new `RouterBuilder` with empty route list.
|
/// Constructs new `RouterBuilder` with empty route list.
|
||||||
pub fn build() -> RouterBuilder<T, U> {
|
pub fn build() -> RouterBuilder<T, U> {
|
||||||
RouterBuilder { routes: Vec::new() }
|
RouterBuilder { routes: Vec::new(), path_conflicts: vec![] }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds the value in the router that matches a given [routing resource](Resource).
|
/// Finds the value in the router that matches a given [routing resource](Resource).
|
||||||
|
@ -46,14 +47,24 @@ impl<T, U> Router<T, U> {
|
||||||
/// the `check` closure is executed, passing the resource and each route's context data. If the
|
/// the `check` closure is executed, passing the resource and each route's context data. If the
|
||||||
/// closure returns true then the match result is stored into `resource` and a reference to
|
/// closure returns true then the match result is stored into `resource` and a reference to
|
||||||
/// the matched _value_ is returned.
|
/// the matched _value_ is returned.
|
||||||
pub fn recognize_fn<R, F>(&self, resource: &mut R, mut check: F) -> Option<(&T, ResourceId)>
|
pub fn recognize_fn<R, F>(&self, resource: &mut R, mut check_fn: F) -> Option<(&T, ResourceId)>
|
||||||
where
|
where
|
||||||
R: Resource,
|
R: Resource,
|
||||||
F: FnMut(&R, &U) -> bool,
|
F: FnMut(&R, &U) -> bool,
|
||||||
{
|
{
|
||||||
|
let mut matches = 0;
|
||||||
for (rdef, val, ctx) in self.routes.iter() {
|
for (rdef, val, ctx) in self.routes.iter() {
|
||||||
if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {
|
match rdef.capture_match_info(resource) {
|
||||||
|
None => {}
|
||||||
|
Some(match_info) => {
|
||||||
|
matches += 1;
|
||||||
|
if check_fn(resource, ctx) {
|
||||||
|
rdef.resolve_resource(resource, match_info);
|
||||||
return Some((val, ResourceId(rdef.id())));
|
return Some((val, ResourceId(rdef.id())));
|
||||||
|
} else if matches == self.max_path_conflicts {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,15 +76,25 @@ impl<T, U> Router<T, U> {
|
||||||
pub fn recognize_mut_fn<R, F>(
|
pub fn recognize_mut_fn<R, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
resource: &mut R,
|
resource: &mut R,
|
||||||
mut check: F,
|
mut check_fn: F,
|
||||||
) -> Option<(&mut T, ResourceId)>
|
) -> Option<(&mut T, ResourceId)>
|
||||||
where
|
where
|
||||||
R: Resource,
|
R: Resource,
|
||||||
F: FnMut(&R, &U) -> bool,
|
F: FnMut(&R, &U) -> bool,
|
||||||
{
|
{
|
||||||
|
let mut matches = 0;
|
||||||
for (rdef, val, ctx) in self.routes.iter_mut() {
|
for (rdef, val, ctx) in self.routes.iter_mut() {
|
||||||
if rdef.capture_match_info_fn(resource, |res| check(res, ctx)) {
|
match rdef.capture_match_info(resource) {
|
||||||
|
None => {}
|
||||||
|
Some(match_info) => {
|
||||||
|
matches += 1;
|
||||||
|
if check_fn(resource, ctx) {
|
||||||
|
rdef.resolve_resource(resource, match_info);
|
||||||
return Some((val, ResourceId(rdef.id())));
|
return Some((val, ResourceId(rdef.id())));
|
||||||
|
} else if matches == self.max_path_conflicts {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,6 +105,7 @@ impl<T, U> Router<T, U> {
|
||||||
/// Builder for an ordered [routing](Router) list.
|
/// Builder for an ordered [routing](Router) list.
|
||||||
pub struct RouterBuilder<T, U = ()> {
|
pub struct RouterBuilder<T, U = ()> {
|
||||||
routes: Vec<(ResourceDef, T, U)>,
|
routes: Vec<(ResourceDef, T, U)>,
|
||||||
|
path_conflicts: Vec<(ResourceDef, u16)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, U> RouterBuilder<T, U> {
|
impl<T, U> RouterBuilder<T, U> {
|
||||||
|
@ -96,6 +118,13 @@ impl<T, U> RouterBuilder<T, U> {
|
||||||
val: T,
|
val: T,
|
||||||
ctx: U,
|
ctx: U,
|
||||||
) -> (&mut ResourceDef, &mut T, &mut U) {
|
) -> (&mut ResourceDef, &mut T, &mut U) {
|
||||||
|
if let Some((_, path_conflicts)) = self.path_conflicts.iter_mut()
|
||||||
|
.find(|(current_rdef, _)| rdef.eq(current_rdef)) {
|
||||||
|
*path_conflicts += 1;
|
||||||
|
} else {
|
||||||
|
self.path_conflicts.push((rdef.clone(), 1));
|
||||||
|
}
|
||||||
|
|
||||||
self.routes.push((rdef, val, ctx));
|
self.routes.push((rdef, val, ctx));
|
||||||
#[allow(clippy::map_identity)] // map is used to distribute &mut-ness to tuple elements
|
#[allow(clippy::map_identity)] // map is used to distribute &mut-ness to tuple elements
|
||||||
self.routes
|
self.routes
|
||||||
|
@ -106,8 +135,13 @@ impl<T, U> RouterBuilder<T, U> {
|
||||||
|
|
||||||
/// Finish configuration and create router instance.
|
/// Finish configuration and create router instance.
|
||||||
pub fn finish(self) -> Router<T, U> {
|
pub fn finish(self) -> Router<T, U> {
|
||||||
|
let max_path_conflicts = self.path_conflicts.iter()
|
||||||
|
.map(|(_, path_conflicts)| *path_conflicts)
|
||||||
|
.max()
|
||||||
|
.unwrap_or(1);
|
||||||
Router {
|
Router {
|
||||||
routes: self.routes,
|
routes: self.routes,
|
||||||
|
max_path_conflicts,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ mod tests {
|
||||||
let re = ResourceDef::new(pattern);
|
let re = ResourceDef::new(pattern);
|
||||||
let uri = Uri::try_from(url.as_ref()).unwrap();
|
let uri = Uri::try_from(url.as_ref()).unwrap();
|
||||||
let mut path = Path::new(Url::new(uri));
|
let mut path = Path::new(Url::new(uri));
|
||||||
assert!(re.capture_match_info(&mut path));
|
assert!(re.resolve_resource_if_matches(&mut path));
|
||||||
path
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,25 @@
|
||||||
use std::{cell::RefCell, mem, rc::Rc};
|
use std::{cell::RefCell, mem, rc::Rc};
|
||||||
|
|
||||||
use actix_http::Request;
|
|
||||||
use actix_router::{Path, ResourceDef, Router, Url};
|
|
||||||
use actix_service::{boxed, fn_service, Service, ServiceFactory};
|
use actix_service::{boxed, fn_service, Service, ServiceFactory};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
|
|
||||||
|
use actix_http::Request;
|
||||||
|
use actix_router::{Path, ResourceDef, Router, Url};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
body::BoxBody,
|
body::BoxBody,
|
||||||
config::{AppConfig, AppService},
|
config::{AppConfig, AppService},
|
||||||
data::FnDataFactory,
|
data::FnDataFactory,
|
||||||
dev::Extensions,
|
dev::Extensions,
|
||||||
|
Error,
|
||||||
guard::Guard,
|
guard::Guard,
|
||||||
|
HttpResponse,
|
||||||
request::{HttpRequest, HttpRequestPool},
|
request::{HttpRequest, HttpRequestPool},
|
||||||
rmap::ResourceMap,
|
rmap::ResourceMap, service::{
|
||||||
service::{
|
|
||||||
AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, ServiceRequest,
|
AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, ServiceRequest,
|
||||||
ServiceResponse,
|
ServiceResponse,
|
||||||
},
|
},
|
||||||
Error, HttpResponse,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Service factory to convert [`Request`] to a [`ServiceRequest<S>`].
|
/// Service factory to convert [`Request`] to a [`ServiceRequest<S>`].
|
||||||
|
@ -28,10 +29,10 @@ pub struct AppInit<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
>,
|
>,
|
||||||
{
|
{
|
||||||
pub(crate) endpoint: T,
|
pub(crate) endpoint: T,
|
||||||
|
@ -47,10 +48,10 @@ impl<T, B> ServiceFactory<Request> for AppInit<T, B>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
>,
|
>,
|
||||||
T::Future: 'static,
|
T::Future: 'static,
|
||||||
{
|
{
|
||||||
|
@ -144,7 +145,7 @@ where
|
||||||
/// Wraps a service receiving a [`ServiceRequest`] into one receiving a [`Request`].
|
/// Wraps a service receiving a [`ServiceRequest`] into one receiving a [`Request`].
|
||||||
pub struct AppInitService<T, B>
|
pub struct AppInitService<T, B>
|
||||||
where
|
where
|
||||||
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
T: Service<ServiceRequest, Response=ServiceResponse<B>, Error=Error>,
|
||||||
{
|
{
|
||||||
service: T,
|
service: T,
|
||||||
app_data: Rc<Extensions>,
|
app_data: Rc<Extensions>,
|
||||||
|
@ -189,7 +190,7 @@ impl AppInitServiceState {
|
||||||
|
|
||||||
impl<T, B> Service<Request> for AppInitService<T, B>
|
impl<T, B> Service<Request> for AppInitService<T, B>
|
||||||
where
|
where
|
||||||
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
T: Service<ServiceRequest, Response=ServiceResponse<B>, Error=Error>,
|
||||||
{
|
{
|
||||||
type Response = ServiceResponse<B>;
|
type Response = ServiceResponse<B>;
|
||||||
type Error = T::Error;
|
type Error = T::Error;
|
||||||
|
@ -229,7 +230,7 @@ where
|
||||||
|
|
||||||
impl<T, B> Drop for AppInitService<T, B>
|
impl<T, B> Drop for AppInitService<T, B>
|
||||||
where
|
where
|
||||||
T: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
|
T: Service<ServiceRequest, Response=ServiceResponse<B>, Error=Error>,
|
||||||
{
|
{
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.app_state.pool().clear();
|
self.app_state.pool().clear();
|
||||||
|
@ -306,16 +307,16 @@ impl Service<ServiceRequest> for AppRouting {
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let res = self.router.recognize_fn(&mut req, |req, guards| {
|
let guards_check_fn = |req: &ServiceRequest, guards: &Vec<Box<dyn Guard>>| {
|
||||||
let guard_ctx = req.guard_ctx();
|
let guard_ctx = req.guard_ctx();
|
||||||
guards.iter().all(|guard| guard.check(&guard_ctx))
|
guards.iter().all(|guard| guard.check(&guard_ctx))
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let res = self.router.recognize_fn(&mut req, guards_check_fn);
|
||||||
if let Some((srv, _info)) = res {
|
if let Some((srv, _info)) = res {
|
||||||
srv.call(req)
|
return srv.call(req);
|
||||||
} else {
|
|
||||||
self.default.call(req)
|
|
||||||
}
|
}
|
||||||
|
self.default.call(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,15 +347,15 @@ impl ServiceFactory<ServiceRequest> for AppEntry {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::{
|
use std::sync::{
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
Arc,
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
};
|
};
|
||||||
|
|
||||||
use actix_service::Service;
|
use actix_service::Service;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
test::{init_service, TestRequest},
|
App,
|
||||||
web, App, HttpResponse,
|
HttpResponse, test::{init_service, TestRequest}, web,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DropData(Arc<AtomicBool>);
|
struct DropData(Arc<AtomicBool>);
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc};
|
use std::{cell::RefCell, fmt, future::Future, mem, rc::Rc};
|
||||||
|
|
||||||
use actix_http::{body::MessageBody, Extensions};
|
|
||||||
use actix_router::{ResourceDef, Router};
|
|
||||||
use actix_service::{
|
use actix_service::{
|
||||||
apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt,
|
apply, apply_fn_factory, boxed, IntoServiceFactory, Service, ServiceFactory, ServiceFactoryExt,
|
||||||
Transform,
|
Transform,
|
||||||
|
@ -9,17 +7,20 @@ use actix_service::{
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
use futures_util::future::join_all;
|
use futures_util::future::join_all;
|
||||||
|
|
||||||
|
use actix_http::{body::MessageBody, Extensions};
|
||||||
|
use actix_router::{ResourceDef, Router};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::ServiceConfig,
|
config::ServiceConfig,
|
||||||
data::Data,
|
data::Data,
|
||||||
dev::AppService,
|
dev::AppService,
|
||||||
|
Error,
|
||||||
guard::Guard,
|
guard::Guard,
|
||||||
rmap::ResourceMap,
|
Resource,
|
||||||
service::{
|
rmap::ResourceMap, Route, service::{
|
||||||
AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory,
|
AppServiceFactory, BoxedHttpService, BoxedHttpServiceFactory, HttpServiceFactory,
|
||||||
ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
|
ServiceFactoryWrapper, ServiceRequest, ServiceResponse,
|
||||||
},
|
},
|
||||||
Error, Resource, Route,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type Guards = Vec<Box<dyn Guard>>;
|
type Guards = Vec<Box<dyn Guard>>;
|
||||||
|
@ -85,7 +86,7 @@ impl Scope {
|
||||||
|
|
||||||
impl<T> Scope<T>
|
impl<T> Scope<T>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<ServiceRequest, Config = (), Error = Error, InitError = ()>,
|
T: ServiceFactory<ServiceRequest, Config=(), Error=Error, InitError=()>,
|
||||||
{
|
{
|
||||||
/// Add match guard to a scope.
|
/// Add match guard to a scope.
|
||||||
///
|
///
|
||||||
|
@ -272,7 +273,7 @@ where
|
||||||
pub fn default_service<F, U>(mut self, f: F) -> Self
|
pub fn default_service<F, U>(mut self, f: F) -> Self
|
||||||
where
|
where
|
||||||
F: IntoServiceFactory<U, ServiceRequest>,
|
F: IntoServiceFactory<U, ServiceRequest>,
|
||||||
U: ServiceFactory<ServiceRequest, Config = (), Response = ServiceResponse, Error = Error>
|
U: ServiceFactory<ServiceRequest, Config=(), Response=ServiceResponse, Error=Error>
|
||||||
+ 'static,
|
+ 'static,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
{
|
{
|
||||||
|
@ -300,19 +301,19 @@ where
|
||||||
) -> Scope<
|
) -> Scope<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
M: Transform<
|
M: Transform<
|
||||||
T::Service,
|
T::Service,
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
> + 'static,
|
> + 'static,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
|
@ -343,15 +344,15 @@ where
|
||||||
) -> Scope<
|
) -> Scope<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
>,
|
>,
|
||||||
>
|
>
|
||||||
where
|
where
|
||||||
F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
|
F: Fn(ServiceRequest, &T::Service) -> R + Clone + 'static,
|
||||||
R: Future<Output = Result<ServiceResponse<B>, Error>>,
|
R: Future<Output=Result<ServiceResponse<B>, Error>>,
|
||||||
B: MessageBody,
|
B: MessageBody,
|
||||||
{
|
{
|
||||||
Scope {
|
Scope {
|
||||||
|
@ -371,10 +372,10 @@ impl<T, B> HttpServiceFactory for Scope<T>
|
||||||
where
|
where
|
||||||
T: ServiceFactory<
|
T: ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<B>,
|
Response=ServiceResponse<B>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
> + 'static,
|
> + 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
|
@ -510,16 +511,16 @@ impl Service<ServiceRequest> for ScopeService {
|
||||||
actix_service::always_ready!();
|
actix_service::always_ready!();
|
||||||
|
|
||||||
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
fn call(&self, mut req: ServiceRequest) -> Self::Future {
|
||||||
let res = self.router.recognize_fn(&mut req, |req, guards| {
|
let guards_check_fn = |req: &ServiceRequest, guards: &Vec<Box<dyn Guard>>| {
|
||||||
let guard_ctx = req.guard_ctx();
|
let guard_ctx = req.guard_ctx();
|
||||||
guards.iter().all(|guard| guard.check(&guard_ctx))
|
guards.iter().all(|guard| guard.check(&guard_ctx))
|
||||||
});
|
};
|
||||||
|
|
||||||
|
let res = self.router.recognize_fn(&mut req, guards_check_fn);
|
||||||
if let Some((srv, _info)) = res {
|
if let Some((srv, _info)) = res {
|
||||||
srv.call(req)
|
return srv.call(req);
|
||||||
} else {
|
|
||||||
self.default.call(req)
|
|
||||||
}
|
}
|
||||||
|
self.default.call(req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,18 +553,19 @@ mod tests {
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
|
|
||||||
use super::*;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
App,
|
||||||
guard,
|
guard,
|
||||||
http::{
|
http::{
|
||||||
header::{self, HeaderValue},
|
header::{self, HeaderValue},
|
||||||
Method, StatusCode,
|
Method, StatusCode,
|
||||||
},
|
},
|
||||||
middleware::DefaultHeaders,
|
HttpMessage,
|
||||||
test::{assert_body_eq, call_service, init_service, read_body, TestRequest},
|
HttpRequest, HttpResponse, middleware::DefaultHeaders, test::{assert_body_eq, call_service, init_service, read_body, TestRequest}, web,
|
||||||
web, App, HttpMessage, HttpRequest, HttpResponse,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn can_be_returned_from_fn() {
|
fn can_be_returned_from_fn() {
|
||||||
fn my_scope_1() -> Scope {
|
fn my_scope_1() -> Scope {
|
||||||
|
@ -574,10 +576,10 @@ mod tests {
|
||||||
fn my_scope_2() -> Scope<
|
fn my_scope_2() -> Scope<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
ServiceRequest,
|
ServiceRequest,
|
||||||
Config = (),
|
Config=(),
|
||||||
Response = ServiceResponse<impl MessageBody>,
|
Response=ServiceResponse<impl MessageBody>,
|
||||||
Error = Error,
|
Error=Error,
|
||||||
InitError = (),
|
InitError=(),
|
||||||
>,
|
>,
|
||||||
> {
|
> {
|
||||||
web::scope("/test-compat")
|
web::scope("/test-compat")
|
||||||
|
|
|
@ -176,7 +176,7 @@ mod tests {
|
||||||
let resource = ResourceDef::new("/{value}/");
|
let resource = ResourceDef::new("/{value}/");
|
||||||
|
|
||||||
let mut req = TestRequest::with_uri("/32/").to_srv_request();
|
let mut req = TestRequest::with_uri("/32/").to_srv_request();
|
||||||
resource.capture_match_info(req.match_info_mut());
|
resource.resolve_resource_if_matches(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
assert_eq!(*Path::<i8>::from_request(&req, &mut pl).await.unwrap(), 32);
|
assert_eq!(*Path::<i8>::from_request(&req, &mut pl).await.unwrap(), 32);
|
||||||
|
@ -189,7 +189,7 @@ mod tests {
|
||||||
let resource = ResourceDef::new("/{key}/{value}/");
|
let resource = ResourceDef::new("/{key}/{value}/");
|
||||||
|
|
||||||
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
|
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
|
||||||
resource.capture_match_info(req.match_info_mut());
|
resource.resolve_resource_if_matches(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
|
let (Path(res),) = <(Path<(String, String)>,)>::from_request(&req, &mut pl)
|
||||||
|
@ -215,7 +215,7 @@ mod tests {
|
||||||
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
|
let mut req = TestRequest::with_uri("/name/user1/?id=test").to_srv_request();
|
||||||
|
|
||||||
let resource = ResourceDef::new("/{key}/{value}/");
|
let resource = ResourceDef::new("/{key}/{value}/");
|
||||||
resource.capture_match_info(req.match_info_mut());
|
resource.resolve_resource_if_matches(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
let mut s = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();
|
let mut s = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();
|
||||||
|
@ -238,7 +238,7 @@ mod tests {
|
||||||
|
|
||||||
let mut req = TestRequest::with_uri("/name/32/").to_srv_request();
|
let mut req = TestRequest::with_uri("/name/32/").to_srv_request();
|
||||||
let resource = ResourceDef::new("/{key}/{value}/");
|
let resource = ResourceDef::new("/{key}/{value}/");
|
||||||
resource.capture_match_info(req.match_info_mut());
|
resource.resolve_resource_if_matches(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
let s = Path::<Test2>::from_request(&req, &mut pl).await.unwrap();
|
let s = Path::<Test2>::from_request(&req, &mut pl).await.unwrap();
|
||||||
|
@ -262,7 +262,7 @@ mod tests {
|
||||||
async fn paths_decoded() {
|
async fn paths_decoded() {
|
||||||
let resource = ResourceDef::new("/{key}/{value}");
|
let resource = ResourceDef::new("/{key}/{value}");
|
||||||
let mut req = TestRequest::with_uri("/na%2Bme/us%2Fer%254%32").to_srv_request();
|
let mut req = TestRequest::with_uri("/na%2Bme/us%2Fer%254%32").to_srv_request();
|
||||||
resource.capture_match_info(req.match_info_mut());
|
resource.resolve_resource_if_matches(req.match_info_mut());
|
||||||
|
|
||||||
let (req, mut pl) = req.into_parts();
|
let (req, mut pl) = req.into_parts();
|
||||||
let path_items = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();
|
let path_items = Path::<MyStruct>::from_request(&req, &mut pl).await.unwrap();
|
||||||
|
|
Loading…
Reference in New Issue