mirror of
https://github.com/actix/actix-extras.git
synced 2024-11-24 16:02:59 +01:00
refactor route matching
This commit is contained in:
parent
5c42b0902f
commit
27b6af2800
@ -73,35 +73,32 @@ impl<S: 'static> HttpApplication<S> {
|
|||||||
HandlerType::Normal(idx)
|
HandlerType::Normal(idx)
|
||||||
} else {
|
} else {
|
||||||
let inner = self.as_ref();
|
let inner = self.as_ref();
|
||||||
let path: &'static str =
|
req.match_info_mut().set_tail(0);
|
||||||
unsafe { &*(&req.path()[inner.prefix..] as *const _) };
|
|
||||||
let path_len = path.len();
|
|
||||||
'outer: for idx in 0..inner.handlers.len() {
|
'outer: for idx in 0..inner.handlers.len() {
|
||||||
match inner.handlers[idx] {
|
match inner.handlers[idx] {
|
||||||
PrefixHandlerType::Handler(ref prefix, _) => {
|
PrefixHandlerType::Handler(ref prefix, _) => {
|
||||||
let m = {
|
let m = {
|
||||||
|
let path = &req.path()[inner.prefix..];
|
||||||
|
let path_len = path.len();
|
||||||
|
|
||||||
path.starts_with(prefix)
|
path.starts_with(prefix)
|
||||||
&& (path_len == prefix.len()
|
&& (path_len == prefix.len()
|
||||||
|| path.split_at(prefix.len()).1.starts_with('/'))
|
|| path.split_at(prefix.len()).1.starts_with('/'))
|
||||||
};
|
};
|
||||||
|
|
||||||
if m {
|
if m {
|
||||||
let prefix_len = inner.prefix + prefix.len();
|
let prefix_len = (inner.prefix + prefix.len()) as u16;
|
||||||
let path: &'static str =
|
let url = req.url().clone();
|
||||||
unsafe { &*(&req.path()[prefix_len..] as *const _) };
|
req.set_prefix_len(prefix_len);
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
req.set_prefix_len(prefix_len as u16);
|
req.match_info_mut().set_tail(prefix_len);
|
||||||
if path.is_empty() {
|
|
||||||
req.match_info_mut().add("tail", "/");
|
|
||||||
} else {
|
|
||||||
req.match_info_mut().add("tail", path);
|
|
||||||
}
|
|
||||||
return HandlerType::Handler(idx);
|
return HandlerType::Handler(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PrefixHandlerType::Scope(ref pattern, _, ref filters) => {
|
PrefixHandlerType::Scope(ref pattern, _, ref filters) => {
|
||||||
if let Some(prefix_len) =
|
if let Some(prefix_len) =
|
||||||
pattern.match_prefix_with_params(path, req.match_info_mut())
|
pattern.match_prefix_with_params(req, inner.prefix)
|
||||||
{
|
{
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
if !filter.check(req) {
|
if !filter.check(req) {
|
||||||
@ -109,12 +106,12 @@ impl<S: 'static> HttpApplication<S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix_len = inner.prefix + prefix_len;
|
let prefix_len = (inner.prefix + prefix_len) as u16;
|
||||||
let path: &'static str =
|
let url = req.url().clone();
|
||||||
unsafe { &*(&req.path()[prefix_len..] as *const _) };
|
req.set_prefix_len(prefix_len);
|
||||||
|
let params = req.match_info_mut();
|
||||||
req.set_prefix_len(prefix_len as u16);
|
params.set_tail(prefix_len);
|
||||||
req.match_info_mut().set("tail", path);
|
params.set_url(url);
|
||||||
return HandlerType::Handler(idx);
|
return HandlerType::Handler(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
src/de.rs
17
src/de.rs
@ -1,9 +1,7 @@
|
|||||||
use serde::de::{self, Deserializer, Error as DeError, Visitor};
|
use serde::de::{self, Deserializer, Error as DeError, Visitor};
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::convert::AsRef;
|
|
||||||
use std::slice::Iter;
|
|
||||||
|
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
|
use param::ParamsIter;
|
||||||
|
|
||||||
macro_rules! unsupported_type {
|
macro_rules! unsupported_type {
|
||||||
($trait_fn:ident, $name:expr) => {
|
($trait_fn:ident, $name:expr) => {
|
||||||
@ -191,7 +189,7 @@ impl<'de, S: 'de> Deserializer<'de> for PathDeserializer<'de, S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ParamsDeserializer<'de> {
|
struct ParamsDeserializer<'de> {
|
||||||
params: Iter<'de, (Cow<'de, str>, Cow<'de, str>)>,
|
params: ParamsIter<'de>,
|
||||||
current: Option<(&'de str, &'de str)>,
|
current: Option<(&'de str, &'de str)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,10 +200,7 @@ impl<'de> de::MapAccess<'de> for ParamsDeserializer<'de> {
|
|||||||
where
|
where
|
||||||
K: de::DeserializeSeed<'de>,
|
K: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
self.current = self
|
self.current = self.params.next().map(|ref item| (item.0, item.1));
|
||||||
.params
|
|
||||||
.next()
|
|
||||||
.map(|&(ref k, ref v)| (k.as_ref(), v.as_ref()));
|
|
||||||
match self.current {
|
match self.current {
|
||||||
Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)),
|
Some((key, _)) => Ok(Some(seed.deserialize(Key { key })?)),
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
@ -381,7 +376,7 @@ impl<'de> Deserializer<'de> for Value<'de> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ParamsSeq<'de> {
|
struct ParamsSeq<'de> {
|
||||||
params: Iter<'de, (Cow<'de, str>, Cow<'de, str>)>,
|
params: ParamsIter<'de>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'de> de::SeqAccess<'de> for ParamsSeq<'de> {
|
impl<'de> de::SeqAccess<'de> for ParamsSeq<'de> {
|
||||||
@ -392,9 +387,7 @@ impl<'de> de::SeqAccess<'de> for ParamsSeq<'de> {
|
|||||||
T: de::DeserializeSeed<'de>,
|
T: de::DeserializeSeed<'de>,
|
||||||
{
|
{
|
||||||
match self.params.next() {
|
match self.params.next() {
|
||||||
Some(item) => Ok(Some(seed.deserialize(Value {
|
Some(item) => Ok(Some(seed.deserialize(Value { value: item.1 })?)),
|
||||||
value: item.1.as_ref(),
|
|
||||||
})?)),
|
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1190,7 +1190,7 @@ mod tests {
|
|||||||
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
|
||||||
|
|
||||||
let mut req = HttpRequest::default();
|
let mut req = HttpRequest::default();
|
||||||
req.match_info_mut().add("tail", "");
|
req.match_info_mut().add_static("tail", "");
|
||||||
|
|
||||||
st.show_index = true;
|
st.show_index = true;
|
||||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||||
@ -1207,7 +1207,7 @@ mod tests {
|
|||||||
fn test_redirect_to_index() {
|
fn test_redirect_to_index() {
|
||||||
let mut st = StaticFiles::new(".").index_file("index.html");
|
let mut st = StaticFiles::new(".").index_file("index.html");
|
||||||
let mut req = HttpRequest::default();
|
let mut req = HttpRequest::default();
|
||||||
req.match_info_mut().add("tail", "tests");
|
req.match_info_mut().add_static("tail", "tests");
|
||||||
|
|
||||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||||
let resp = resp.as_msg();
|
let resp = resp.as_msg();
|
||||||
@ -1218,7 +1218,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let mut req = HttpRequest::default();
|
let mut req = HttpRequest::default();
|
||||||
req.match_info_mut().add("tail", "tests/");
|
req.match_info_mut().add_static("tail", "tests/");
|
||||||
|
|
||||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||||
let resp = resp.as_msg();
|
let resp = resp.as_msg();
|
||||||
@ -1233,7 +1233,7 @@ mod tests {
|
|||||||
fn test_redirect_to_index_nested() {
|
fn test_redirect_to_index_nested() {
|
||||||
let mut st = StaticFiles::new(".").index_file("mod.rs");
|
let mut st = StaticFiles::new(".").index_file("mod.rs");
|
||||||
let mut req = HttpRequest::default();
|
let mut req = HttpRequest::default();
|
||||||
req.match_info_mut().add("tail", "src/client");
|
req.match_info_mut().add_static("tail", "src/client");
|
||||||
|
|
||||||
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
|
||||||
let resp = resp.as_msg();
|
let resp = resp.as_msg();
|
||||||
|
@ -40,7 +40,7 @@ pub struct HttpInnerMessage {
|
|||||||
pub(crate) flags: MessageFlags,
|
pub(crate) flags: MessageFlags,
|
||||||
pub headers: HeaderMap,
|
pub headers: HeaderMap,
|
||||||
pub extensions: Extensions,
|
pub extensions: Extensions,
|
||||||
pub params: Params<'static>,
|
pub params: Params,
|
||||||
pub addr: Option<SocketAddr>,
|
pub addr: Option<SocketAddr>,
|
||||||
pub payload: Option<Payload>,
|
pub payload: Option<Payload>,
|
||||||
pub prefix: u16,
|
pub prefix: u16,
|
||||||
@ -268,6 +268,11 @@ impl<S> HttpRequest<S> {
|
|||||||
self.as_ref().url.path()
|
self.as_ref().url.path()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub(crate) fn url(&self) -> &InnerUrl {
|
||||||
|
&self.as_ref().url
|
||||||
|
}
|
||||||
|
|
||||||
/// Get *ConnectionInfo* for correct request.
|
/// Get *ConnectionInfo* for correct request.
|
||||||
pub fn connection_info(&self) -> &ConnectionInfo {
|
pub fn connection_info(&self) -> &ConnectionInfo {
|
||||||
if self.extensions().get::<Info>().is_none() {
|
if self.extensions().get::<Info>().is_none() {
|
||||||
|
125
src/param.rs
125
src/param.rs
@ -1,13 +1,12 @@
|
|||||||
use http::StatusCode;
|
use http::StatusCode;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std;
|
use std;
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::slice::Iter;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use error::{InternalError, ResponseError, UriSegmentError};
|
use error::{InternalError, ResponseError, UriSegmentError};
|
||||||
|
use uri::Url;
|
||||||
|
|
||||||
/// A trait to abstract the idea of creating a new instance of a type from a
|
/// A trait to abstract the idea of creating a new instance of a type from a
|
||||||
/// path parameter.
|
/// path parameter.
|
||||||
@ -19,73 +18,79 @@ pub trait FromParam: Sized {
|
|||||||
fn from_param(s: &str) -> Result<Self, Self::Err>;
|
fn from_param(s: &str) -> Result<Self, Self::Err>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub(crate) enum ParamItem {
|
||||||
|
Static(&'static str),
|
||||||
|
UrlSegment(u16, u16),
|
||||||
|
}
|
||||||
|
|
||||||
/// Route match information
|
/// Route match information
|
||||||
///
|
///
|
||||||
/// If resource path contains variable patterns, `Params` stores this variables.
|
/// If resource path contains variable patterns, `Params` stores this variables.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Params<'a>(SmallVec<[(Cow<'a, str>, Cow<'a, str>); 3]>);
|
pub struct Params {
|
||||||
|
url: Url,
|
||||||
|
pub(crate) tail: u16,
|
||||||
|
segments: SmallVec<[(&'static str, ParamItem); 3]>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Params<'a> {
|
impl Params {
|
||||||
pub(crate) fn new() -> Params<'a> {
|
pub(crate) fn new() -> Params {
|
||||||
Params(SmallVec::new())
|
Params {
|
||||||
|
url: Url::default(),
|
||||||
|
tail: 0,
|
||||||
|
segments: SmallVec::new(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear(&mut self) {
|
pub(crate) fn clear(&mut self) {
|
||||||
self.0.clear();
|
self.segments.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn add<N, V>(&mut self, name: N, value: V)
|
pub(crate) fn set_url(&mut self, url: Url) {
|
||||||
where
|
self.url = url;
|
||||||
N: Into<Cow<'a, str>>,
|
|
||||||
V: Into<Cow<'a, str>>,
|
|
||||||
{
|
|
||||||
self.0.push((name.into(), value.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set<N, V>(&mut self, name: N, value: V)
|
pub(crate) fn set_tail(&mut self, tail: u16) {
|
||||||
where
|
self.tail = tail;
|
||||||
N: Into<Cow<'a, str>>,
|
|
||||||
V: Into<Cow<'a, str>>,
|
|
||||||
{
|
|
||||||
let name = name.into();
|
|
||||||
let value = value.into();
|
|
||||||
for item in &mut self.0 {
|
|
||||||
if item.0 == name {
|
|
||||||
item.1 = value;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.0.push((name, value));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove(&mut self, name: &str) {
|
pub(crate) fn add(&mut self, name: &'static str, value: ParamItem) {
|
||||||
for idx in (0..self.0.len()).rev() {
|
self.segments.push((name, value));
|
||||||
if self.0[idx].0 == name {
|
|
||||||
self.0.remove(idx);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn add_static(&mut self, name: &'static str, value: &'static str) {
|
||||||
|
self.segments.push((name, ParamItem::Static(value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if there are any matched patterns
|
/// Check if there are any matched patterns
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.0.is_empty()
|
self.segments.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check number of extracted parameters
|
/// Check number of extracted parameters
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.0.len()
|
self.segments.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get matched parameter by name without type conversion
|
/// Get matched parameter by name without type conversion
|
||||||
pub fn get(&'a self, key: &str) -> Option<&'a str> {
|
pub fn get(&self, key: &str) -> Option<&str> {
|
||||||
for item in self.0.iter() {
|
for item in self.segments.iter() {
|
||||||
if key == item.0 {
|
if key == item.0 {
|
||||||
return Some(item.1.as_ref());
|
return match item.1 {
|
||||||
|
ParamItem::Static(s) => Some(s),
|
||||||
|
ParamItem::UrlSegment(s, e) => {
|
||||||
|
Some(&self.url.path()[(s as usize)..(e as usize)])
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if key == "tail" {
|
||||||
|
Some(&self.url.path()[(self.tail as usize)..])
|
||||||
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get matched `FromParam` compatible parameter by name.
|
/// Get matched `FromParam` compatible parameter by name.
|
||||||
///
|
///
|
||||||
@ -101,7 +106,7 @@ impl<'a> Params<'a> {
|
|||||||
/// }
|
/// }
|
||||||
/// # fn main() {}
|
/// # fn main() {}
|
||||||
/// ```
|
/// ```
|
||||||
pub fn query<T: FromParam>(&'a self, key: &str) -> Result<T, <T as FromParam>::Err> {
|
pub fn query<T: FromParam>(&self, key: &str) -> Result<T, <T as FromParam>::Err> {
|
||||||
if let Some(s) = self.get(key) {
|
if let Some(s) = self.get(key) {
|
||||||
T::from_param(s)
|
T::from_param(s)
|
||||||
} else {
|
} else {
|
||||||
@ -110,12 +115,41 @@ impl<'a> Params<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Return iterator to items in parameter container
|
/// Return iterator to items in parameter container
|
||||||
pub fn iter(&self) -> Iter<(Cow<'a, str>, Cow<'a, str>)> {
|
pub fn iter(&self) -> ParamsIter {
|
||||||
self.0.iter()
|
ParamsIter {
|
||||||
|
idx: 0,
|
||||||
|
params: self,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'b, 'c: 'a> Index<&'b str> for &'c Params<'a> {
|
#[derive(Debug)]
|
||||||
|
pub struct ParamsIter<'a> {
|
||||||
|
idx: usize,
|
||||||
|
params: &'a Params,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for ParamsIter<'a> {
|
||||||
|
type Item = (&'a str, &'a str);
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn next(&mut self) -> Option<(&'a str, &'a str)> {
|
||||||
|
if self.idx < self.params.len() {
|
||||||
|
let idx = self.idx;
|
||||||
|
let res = match self.params.segments[idx].1 {
|
||||||
|
ParamItem::Static(s) => s,
|
||||||
|
ParamItem::UrlSegment(s, e) => {
|
||||||
|
&self.params.url.path()[(s as usize)..(e as usize)]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.idx += 1;
|
||||||
|
return Some((self.params.segments[idx].0, res));
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Index<&'b str> for &'a Params {
|
||||||
type Output = str;
|
type Output = str;
|
||||||
|
|
||||||
fn index(&self, name: &'b str) -> &str {
|
fn index(&self, name: &'b str) -> &str {
|
||||||
@ -124,11 +158,14 @@ impl<'a, 'b, 'c: 'a> Index<&'b str> for &'c Params<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, 'c: 'a> Index<usize> for &'c Params<'a> {
|
impl<'a> Index<usize> for &'a Params {
|
||||||
type Output = str;
|
type Output = str;
|
||||||
|
|
||||||
fn index(&self, idx: usize) -> &str {
|
fn index(&self, idx: usize) -> &str {
|
||||||
self.0[idx].1.as_ref()
|
match self.segments[idx].1 {
|
||||||
|
ParamItem::Static(s) => s,
|
||||||
|
ParamItem::UrlSegment(s, e) => &self.url.path()[(s as usize)..(e as usize)],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
134
src/router.rs
134
src/router.rs
@ -1,12 +1,13 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::mem;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use regex::{escape, Regex};
|
use regex::{escape, Regex};
|
||||||
|
|
||||||
use error::UrlGenerationError;
|
use error::UrlGenerationError;
|
||||||
use httprequest::HttpRequest;
|
use httprequest::HttpRequest;
|
||||||
use param::Params;
|
use param::ParamItem;
|
||||||
use resource::ResourceHandler;
|
use resource::ResourceHandler;
|
||||||
use server::ServerSettings;
|
use server::ServerSettings;
|
||||||
|
|
||||||
@ -78,11 +79,10 @@ impl Router {
|
|||||||
if self.0.prefix_len > req.path().len() {
|
if self.0.prefix_len > req.path().len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path = unsafe { &*(&req.path()[self.0.prefix_len..] as *const str) };
|
|
||||||
let route_path = if path.is_empty() { "/" } else { path };
|
|
||||||
|
|
||||||
for (idx, pattern) in self.0.patterns.iter().enumerate() {
|
for (idx, pattern) in self.0.patterns.iter().enumerate() {
|
||||||
if pattern.match_with_params(route_path, req.match_info_mut()) {
|
if pattern.match_with_params(req, self.0.prefix_len, true) {
|
||||||
|
let url = req.url().clone();
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
req.set_resource(idx);
|
req.set_resource(idx);
|
||||||
req.set_prefix_len(self.0.prefix_len as u16);
|
req.set_prefix_len(self.0.prefix_len as u16);
|
||||||
return Some(idx);
|
return Some(idx);
|
||||||
@ -261,63 +261,108 @@ impl Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Are the given path and parameters a match against this resource?
|
/// Are the given path and parameters a match against this resource?
|
||||||
pub fn match_with_params<'a>(
|
pub fn match_with_params<S>(
|
||||||
&'a self, path: &'a str, params: &'a mut Params<'a>,
|
&self, req: &mut HttpRequest<S>, plen: usize, insert: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let mut segments: [ParamItem; 24] = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
|
let (names, segments_len) = {
|
||||||
|
let path = &req.path()[plen..];
|
||||||
|
if insert {
|
||||||
|
if path.is_empty() {
|
||||||
|
"/"
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
};
|
||||||
|
|
||||||
match self.tp {
|
match self.tp {
|
||||||
PatternType::Static(ref s) => s == path,
|
PatternType::Static(ref s) => return s == path,
|
||||||
PatternType::Dynamic(ref re, ref names, _) => {
|
PatternType::Dynamic(ref re, ref names, _) => {
|
||||||
if let Some(captures) = re.captures(path) {
|
if let Some(captures) = re.captures(path) {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
let mut passed = false;
|
||||||
for capture in captures.iter() {
|
for capture in captures.iter() {
|
||||||
if let Some(ref m) = capture {
|
if let Some(ref m) = capture {
|
||||||
if idx != 0 {
|
if !passed {
|
||||||
params.add(names[idx - 1].as_str(), m.as_str());
|
passed = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
segments[idx] = ParamItem::UrlSegment(
|
||||||
|
(plen + m.start()) as u16,
|
||||||
|
(plen + m.end()) as u16,
|
||||||
|
);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
(names, idx)
|
||||||
} else {
|
} else {
|
||||||
false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PatternType::Prefix(ref s) => path.starts_with(s),
|
PatternType::Prefix(ref s) => return path.starts_with(s),
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let len = req.path().len();
|
||||||
|
let params = req.match_info_mut();
|
||||||
|
params.set_tail(len as u16);
|
||||||
|
for idx in 0..segments_len {
|
||||||
|
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
||||||
|
params.add(name, segments[idx]);
|
||||||
|
}
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the given path a prefix match and do the parameters match against this resource?
|
/// Is the given path a prefix match and do the parameters match against this resource?
|
||||||
pub fn match_prefix_with_params<'a>(
|
pub fn match_prefix_with_params<S>(
|
||||||
&'a self, path: &'a str, params: &'a mut Params<'a>,
|
&self, req: &mut HttpRequest<S>, plen: usize,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
|
let mut segments: [ParamItem; 24] = unsafe { mem::uninitialized() };
|
||||||
|
|
||||||
|
let (names, segments_len, tail_len) = {
|
||||||
|
let path = &req.path()[plen..];
|
||||||
|
let path = if path.is_empty() { "/" } else { path };
|
||||||
|
|
||||||
match self.tp {
|
match self.tp {
|
||||||
PatternType::Static(ref s) => if s == path {
|
PatternType::Static(ref s) => if s == path {
|
||||||
Some(s.len())
|
return Some(s.len());
|
||||||
} else {
|
} else {
|
||||||
None
|
return None;
|
||||||
},
|
},
|
||||||
PatternType::Dynamic(ref re, ref names, len) => {
|
PatternType::Dynamic(ref re, ref names, len) => {
|
||||||
if let Some(captures) = re.captures(path) {
|
if let Some(captures) = re.captures(path) {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
|
let mut passed = false;
|
||||||
for capture in captures.iter() {
|
for capture in captures.iter() {
|
||||||
if let Some(ref m) = capture {
|
if let Some(ref m) = capture {
|
||||||
if idx != 0 {
|
if !passed {
|
||||||
params.add(names[idx - 1].as_str(), m.as_str());
|
passed = true;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
segments[idx] = ParamItem::UrlSegment(
|
||||||
|
(plen + m.start()) as u16,
|
||||||
|
(plen + m.end()) as u16,
|
||||||
|
);
|
||||||
idx += 1;
|
idx += 1;
|
||||||
pos = m.end();
|
pos = m.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(pos + len)
|
(names, idx, pos + len)
|
||||||
} else {
|
} else {
|
||||||
None
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PatternType::Prefix(ref s) => if path == s {
|
PatternType::Prefix(ref s) => {
|
||||||
|
return if path == s {
|
||||||
Some(s.len())
|
Some(s.len())
|
||||||
} else if path.starts_with(s)
|
} else if path.starts_with(s)
|
||||||
&& (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/'))
|
&& (s.ends_with('/')
|
||||||
|
|| path.split_at(s.len()).1.starts_with('/'))
|
||||||
{
|
{
|
||||||
if s.ends_with('/') {
|
if s.ends_with('/') {
|
||||||
Some(s.len() - 1)
|
Some(s.len() - 1)
|
||||||
@ -326,9 +371,19 @@ impl Resource {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let params = req.match_info_mut();
|
||||||
|
params.set_tail(tail_len as u16);
|
||||||
|
for idx in 0..segments_len {
|
||||||
|
let name = unsafe { &*(names[idx].as_str() as *const _) };
|
||||||
|
params.add(name, segments[idx]);
|
||||||
|
}
|
||||||
|
Some(tail_len)
|
||||||
|
}
|
||||||
|
|
||||||
/// Build resource path.
|
/// Build resource path.
|
||||||
pub fn resource_path<U, I>(
|
pub fn resource_path<U, I>(
|
||||||
@ -634,20 +689,22 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_param() {
|
fn test_parse_param() {
|
||||||
let mut req = HttpRequest::default();
|
|
||||||
|
|
||||||
let re = Resource::new("test", "/user/{id}");
|
let re = Resource::new("test", "/user/{id}");
|
||||||
assert!(re.is_match("/user/profile"));
|
assert!(re.is_match("/user/profile"));
|
||||||
assert!(re.is_match("/user/2345"));
|
assert!(re.is_match("/user/2345"));
|
||||||
assert!(!re.is_match("/user/2345/"));
|
assert!(!re.is_match("/user/2345/"));
|
||||||
assert!(!re.is_match("/user/2345/sdg"));
|
assert!(!re.is_match("/user/2345/sdg"));
|
||||||
|
|
||||||
req.match_info_mut().clear();
|
let mut req = TestRequest::with_uri("/user/profile").finish();
|
||||||
assert!(re.match_with_params("/user/profile", req.match_info_mut()));
|
let url = req.url().clone();
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
|
assert!(re.match_with_params(&mut req, 0, true));
|
||||||
assert_eq!(req.match_info().get("id").unwrap(), "profile");
|
assert_eq!(req.match_info().get("id").unwrap(), "profile");
|
||||||
|
|
||||||
req.match_info_mut().clear();
|
let mut req = TestRequest::with_uri("/user/1245125").finish();
|
||||||
assert!(re.match_with_params("/user/1245125", req.match_info_mut()));
|
let url = req.url().clone();
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
|
assert!(re.match_with_params(&mut req, 0, true));
|
||||||
assert_eq!(req.match_info().get("id").unwrap(), "1245125");
|
assert_eq!(req.match_info().get("id").unwrap(), "1245125");
|
||||||
|
|
||||||
let re = Resource::new("test", "/v{version}/resource/{id}");
|
let re = Resource::new("test", "/v{version}/resource/{id}");
|
||||||
@ -655,8 +712,10 @@ mod tests {
|
|||||||
assert!(!re.is_match("/v/resource/1"));
|
assert!(!re.is_match("/v/resource/1"));
|
||||||
assert!(!re.is_match("/resource"));
|
assert!(!re.is_match("/resource"));
|
||||||
|
|
||||||
req.match_info_mut().clear();
|
let mut req = TestRequest::with_uri("/v151/resource/adahg32").finish();
|
||||||
assert!(re.match_with_params("/v151/resource/adahg32", req.match_info_mut()));
|
let url = req.url().clone();
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
|
assert!(re.match_with_params(&mut req, 0, true));
|
||||||
assert_eq!(req.match_info().get("version").unwrap(), "151");
|
assert_eq!(req.match_info().get("version").unwrap(), "151");
|
||||||
assert_eq!(req.match_info().get("id").unwrap(), "adahg32");
|
assert_eq!(req.match_info().get("id").unwrap(), "adahg32");
|
||||||
}
|
}
|
||||||
@ -684,15 +743,16 @@ mod tests {
|
|||||||
assert!(!re.is_match("/name"));
|
assert!(!re.is_match("/name"));
|
||||||
|
|
||||||
let mut req = TestRequest::with_uri("/test2/").finish();
|
let mut req = TestRequest::with_uri("/test2/").finish();
|
||||||
assert!(re.match_with_params("/test2/", req.match_info_mut()));
|
let url = req.url().clone();
|
||||||
|
req.match_info_mut().set_url(url);
|
||||||
|
assert!(re.match_with_params(&mut req, 0, true));
|
||||||
assert_eq!(&req.match_info()["name"], "test2");
|
assert_eq!(&req.match_info()["name"], "test2");
|
||||||
|
|
||||||
let mut req =
|
let mut req =
|
||||||
TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish();
|
TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish();
|
||||||
assert!(re.match_with_params(
|
let url = req.url().clone();
|
||||||
"/test2/subpath1/subpath2/index.html",
|
req.match_info_mut().set_url(url);
|
||||||
req.match_info_mut()
|
assert!(re.match_with_params(&mut req, 0, true));
|
||||||
));
|
|
||||||
assert_eq!(&req.match_info()["name"], "test2");
|
assert_eq!(&req.match_info()["name"], "test2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
22
src/scope.rs
22
src/scope.rs
@ -322,14 +322,13 @@ impl<S: 'static> Scope<S> {
|
|||||||
|
|
||||||
impl<S: 'static> RouteHandler<S> for Scope<S> {
|
impl<S: 'static> RouteHandler<S> for Scope<S> {
|
||||||
fn handle(&mut self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
fn handle(&mut self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||||
let path = unsafe { &*(&req.match_info()["tail"] as *const _) };
|
let tail = req.match_info().tail as usize;
|
||||||
|
|
||||||
// recognize resources
|
// recognize resources
|
||||||
for &(ref pattern, ref resource) in self.resources.iter() {
|
for &(ref pattern, ref resource) in self.resources.iter() {
|
||||||
if pattern.match_with_params(path, req.match_info_mut()) {
|
if pattern.match_with_params(&mut req, tail, false) {
|
||||||
let default = unsafe { &mut *self.default.as_ref().get() };
|
let default = unsafe { &mut *self.default.as_ref().get() };
|
||||||
|
|
||||||
req.match_info_mut().remove("tail");
|
|
||||||
if self.middlewares.borrow().is_empty() {
|
if self.middlewares.borrow().is_empty() {
|
||||||
let resource = unsafe { &mut *resource.get() };
|
let resource = unsafe { &mut *resource.get() };
|
||||||
return resource.handle(req, Some(default));
|
return resource.handle(req, Some(default));
|
||||||
@ -346,23 +345,18 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
|||||||
|
|
||||||
// nested scopes
|
// nested scopes
|
||||||
let len = req.prefix_len() as usize;
|
let len = req.prefix_len() as usize;
|
||||||
let path: &'static str = unsafe { &*(&req.path()[len..] as *const _) };
|
|
||||||
|
|
||||||
'outer: for &(ref prefix, ref handler, ref filters) in &self.nested {
|
'outer: for &(ref prefix, ref handler, ref filters) in &self.nested {
|
||||||
if let Some(prefix_len) =
|
if let Some(prefix_len) = prefix.match_prefix_with_params(&mut req, len) {
|
||||||
prefix.match_prefix_with_params(path, req.match_info_mut())
|
|
||||||
{
|
|
||||||
for filter in filters {
|
for filter in filters {
|
||||||
if !filter.check(&mut req) {
|
if !filter.check(&mut req) {
|
||||||
continue 'outer;
|
continue 'outer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let prefix_len = len + prefix_len;
|
let url = req.url().clone();
|
||||||
let path: &'static str =
|
let prefix_len = (len + prefix_len) as u16;
|
||||||
unsafe { &*(&req.path()[prefix_len..] as *const _) };
|
req.set_prefix_len(prefix_len);
|
||||||
|
req.match_info_mut().set_tail(prefix_len);
|
||||||
req.set_prefix_len(prefix_len as u16);
|
req.match_info_mut().set_url(url);
|
||||||
req.match_info_mut().set("tail", path);
|
|
||||||
|
|
||||||
let hnd: &mut RouteHandler<_> =
|
let hnd: &mut RouteHandler<_> =
|
||||||
unsafe { (&mut *(handler.get())).as_mut() };
|
unsafe { (&mut *(handler.get())).as_mut() };
|
||||||
|
@ -408,7 +408,7 @@ pub struct TestRequest<S> {
|
|||||||
method: Method,
|
method: Method,
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
headers: HeaderMap,
|
headers: HeaderMap,
|
||||||
params: Params<'static>,
|
params: Params,
|
||||||
cookies: Option<Vec<Cookie<'static>>>,
|
cookies: Option<Vec<Cookie<'static>>>,
|
||||||
payload: Option<Payload>,
|
payload: Option<Payload>,
|
||||||
}
|
}
|
||||||
@ -508,7 +508,7 @@ impl<S: 'static> TestRequest<S> {
|
|||||||
|
|
||||||
/// Set request path pattern parameter
|
/// Set request path pattern parameter
|
||||||
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
|
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
|
||||||
self.params.add(name, value);
|
self.params.add_static(name, value);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use http::Uri;
|
use http::Uri;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const GEN_DELIMS: &[u8] = b":/?#[]@";
|
const GEN_DELIMS: &[u8] = b":/?#[]@";
|
||||||
@ -34,10 +35,10 @@ lazy_static! {
|
|||||||
static ref DEFAULT_QUOTER: Quoter = { Quoter::new(b"@:", b"/+") };
|
static ref DEFAULT_QUOTER: Quoter = { Quoter::new(b"@:", b"/+") };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default, Clone, Debug)]
|
||||||
pub(crate) struct Url {
|
pub(crate) struct Url {
|
||||||
uri: Uri,
|
uri: Uri,
|
||||||
path: Option<String>,
|
path: Option<Rc<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Url {
|
impl Url {
|
||||||
@ -95,7 +96,7 @@ impl Quoter {
|
|||||||
q
|
q
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requote(&self, val: &[u8]) -> Option<String> {
|
pub fn requote(&self, val: &[u8]) -> Option<Rc<String>> {
|
||||||
let mut has_pct = 0;
|
let mut has_pct = 0;
|
||||||
let mut pct = [b'%', 0, 0];
|
let mut pct = [b'%', 0, 0];
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
@ -145,7 +146,7 @@ impl Quoter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(data) = cloned {
|
if let Some(data) = cloned {
|
||||||
Some(unsafe { String::from_utf8_unchecked(data) })
|
Some(unsafe { Rc::new(String::from_utf8_unchecked(data)) })
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user