1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-24 16:02:59 +01:00

Merge branch 'master' into remove-some-uses-of-unsafe-from-frame-message

This commit is contained in:
Nikolay Kim 2018-06-19 19:20:40 +06:00 committed by GitHub
commit 3b1124c56c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 263 additions and 176 deletions

View File

@ -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);
} }
} }

View File

@ -294,7 +294,7 @@ impl ClientConnector {
/// With `with_connector` method it is possible to use a custom /// With `with_connector` method it is possible to use a custom
/// `SslConnector` object. /// `SslConnector` object.
/// ///
/// ```rust /// ```rust,ignore
/// # #![cfg(feature="alpn")] /// # #![cfg(feature="alpn")]
/// # extern crate actix_web; /// # extern crate actix_web;
/// # extern crate futures; /// # extern crate futures;

View File

@ -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),
} }
} }

View File

@ -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();

View File

@ -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() {

View File

@ -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,72 +18,78 @@ 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)])
}
};
} }
} }
None if key == "tail" {
Some(&self.url.path()[(self.tail as usize)..])
} else {
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)],
}
} }
} }

View File

@ -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,73 +261,128 @@ 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 {
match self.tp { let mut segments: [ParamItem; 24] = unsafe { mem::uninitialized() };
PatternType::Static(ref s) => s == path,
PatternType::Dynamic(ref re, ref names, _) => { let (names, segments_len) = {
if let Some(captures) = re.captures(path) { let path = &req.path()[plen..];
let mut idx = 0; if insert {
for capture in captures.iter() { if path.is_empty() {
if let Some(ref m) = capture { "/"
if idx != 0 {
params.add(names[idx - 1].as_str(), m.as_str());
}
idx += 1;
}
}
true
} else { } else {
false path
} }
} else {
path
};
match self.tp {
PatternType::Static(ref s) => return s == path,
PatternType::Dynamic(ref re, ref names, _) => {
if let Some(captures) = re.captures(path) {
let mut idx = 0;
let mut passed = false;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
segments[idx] = ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
);
idx += 1;
}
}
(names, idx)
} else {
return false;
}
}
PatternType::Prefix(ref s) => return path.starts_with(s),
} }
PatternType::Prefix(ref s) => 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> {
match self.tp { let mut segments: [ParamItem; 24] = unsafe { mem::uninitialized() };
PatternType::Static(ref s) => if s == path {
Some(s.len()) let (names, segments_len, tail_len) = {
} else { let path = &req.path()[plen..];
None let path = if path.is_empty() { "/" } else { path };
},
PatternType::Dynamic(ref re, ref names, len) => { match self.tp {
if let Some(captures) = re.captures(path) { PatternType::Static(ref s) => if s == path {
let mut idx = 0; return Some(s.len());
let mut pos = 0;
for capture in captures.iter() {
if let Some(ref m) = capture {
if idx != 0 {
params.add(names[idx - 1].as_str(), m.as_str());
}
idx += 1;
pos = m.end();
}
}
Some(pos + len)
} else { } else {
None return None;
},
PatternType::Dynamic(ref re, ref names, len) => {
if let Some(captures) = re.captures(path) {
let mut idx = 0;
let mut pos = 0;
let mut passed = false;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
segments[idx] = ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
);
idx += 1;
pos = m.end();
}
}
(names, idx, pos + len)
} else {
return None;
}
}
PatternType::Prefix(ref s) => {
return if path == s {
Some(s.len())
} else if path.starts_with(s)
&& (s.ends_with('/')
|| path.split_at(s.len()).1.starts_with('/'))
{
if s.ends_with('/') {
Some(s.len() - 1)
} else {
Some(s.len())
}
} else {
None
}
} }
} }
PatternType::Prefix(ref s) => if path == s { };
Some(s.len())
} else if path.starts_with(s) let params = req.match_info_mut();
&& (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/')) params.set_tail(tail_len as u16);
{ for idx in 0..segments_len {
if s.ends_with('/') { let name = unsafe { &*(names[idx].as_str() as *const _) };
Some(s.len() - 1) params.add(name, segments[idx]);
} else {
Some(s.len())
}
} else {
None
},
} }
Some(tail_len)
} }
/// Build resource path. /// Build resource path.
@ -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");
} }

View File

@ -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() };

View File

@ -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
} }

View File

@ -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
} }