mirror of
https://github.com/actix/actix-web.git
synced 2024-12-23 00:26:34 +00:00
Allow to use path without traling slashes for scope registration #241
This commit is contained in:
parent
72757887c9
commit
68eb2f26c9
11 changed files with 107 additions and 59 deletions
|
@ -2,6 +2,10 @@
|
|||
|
||||
## [0.6.10] - 2018-05-xx
|
||||
|
||||
### Added
|
||||
|
||||
* Allow to use path without traling slashes for scope registration #241
|
||||
|
||||
### Fixed
|
||||
|
||||
* `TestServer::post()` actually sends `GET` request #240
|
||||
|
|
|
@ -107,16 +107,12 @@ impl<S: 'static> HttpApplication<S> {
|
|||
}
|
||||
}
|
||||
|
||||
let prefix_len = inner.prefix + prefix_len - 1;
|
||||
let prefix_len = inner.prefix + prefix_len;
|
||||
let path: &'static str =
|
||||
unsafe { &*(&req.path()[prefix_len..] as *const _) };
|
||||
|
||||
req.set_prefix_len(prefix_len as u16);
|
||||
if path.is_empty() {
|
||||
req.match_info_mut().set("tail", "/");
|
||||
} else {
|
||||
req.match_info_mut().set("tail", path);
|
||||
}
|
||||
req.match_info_mut().set("tail", path);
|
||||
return HandlerType::Handler(idx);
|
||||
}
|
||||
}
|
||||
|
@ -369,14 +365,6 @@ where
|
|||
{
|
||||
{
|
||||
let mut scope = Box::new(f(Scope::new()));
|
||||
|
||||
let mut path = path.trim().trim_right_matches('/').to_owned();
|
||||
if !path.is_empty() && !path.starts_with('/') {
|
||||
path.insert(0, '/')
|
||||
}
|
||||
if !path.ends_with('/') {
|
||||
path.push('/');
|
||||
}
|
||||
let parts = self.parts.as_mut().expect("Use after finish");
|
||||
|
||||
let filters = scope.take_filters();
|
||||
|
|
10
src/fs.rs
10
src/fs.rs
|
@ -20,7 +20,7 @@ use mime_guess::{get_mime_type, guess_mime_type};
|
|||
use error::Error;
|
||||
use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler};
|
||||
use header;
|
||||
use http::{HttpRange, Method, StatusCode, ContentEncoding};
|
||||
use http::{ContentEncoding, HttpRange, Method, StatusCode};
|
||||
use httpmessage::HttpMessage;
|
||||
use httprequest::HttpRequest;
|
||||
use httpresponse::HttpResponse;
|
||||
|
@ -304,11 +304,11 @@ impl Responder for NamedFile {
|
|||
resp.header(
|
||||
header::CONTENT_RANGE,
|
||||
format!(
|
||||
"bytes {}-{}/{}",
|
||||
offset,
|
||||
offset + length - 1,
|
||||
"bytes {}-{}/{}",
|
||||
offset,
|
||||
offset + length - 1,
|
||||
self.md.len()
|
||||
)
|
||||
),
|
||||
);
|
||||
} else {
|
||||
resp.header(header::CONTENT_RANGE, format!("bytes */{}", length));
|
||||
|
|
|
@ -58,12 +58,11 @@ impl<'a> Params<'a> {
|
|||
self.0.push((name, value));
|
||||
}
|
||||
|
||||
pub(crate) fn remove(&mut self, name: &str)
|
||||
{
|
||||
pub(crate) fn remove(&mut self, name: &str) {
|
||||
for idx in (0..self.0.len()).rev() {
|
||||
if self.0[idx].0 == name {
|
||||
self.0.remove(idx);
|
||||
return
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,8 +311,16 @@ impl Resource {
|
|||
None
|
||||
}
|
||||
}
|
||||
PatternType::Prefix(ref s) => if path.starts_with(s) {
|
||||
PatternType::Prefix(ref s) => 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
|
||||
},
|
||||
|
|
102
src/scope.rs
102
src/scope.rs
|
@ -137,14 +137,6 @@ impl<S: 'static> Scope<S> {
|
|||
};
|
||||
let mut scope = f(scope);
|
||||
|
||||
let mut path = path.trim().trim_right_matches('/').to_owned();
|
||||
if !path.is_empty() && !path.starts_with('/') {
|
||||
path.insert(0, '/')
|
||||
}
|
||||
if !path.ends_with('/') {
|
||||
path.push('/');
|
||||
}
|
||||
|
||||
let state = Rc::new(state);
|
||||
let filters: Vec<Box<Predicate<S>>> = vec![Box::new(FiltersWrapper {
|
||||
state: Rc::clone(&state),
|
||||
|
@ -191,14 +183,6 @@ impl<S: 'static> Scope<S> {
|
|||
};
|
||||
let mut scope = f(scope);
|
||||
|
||||
let mut path = path.trim().trim_right_matches('/').to_owned();
|
||||
if !path.is_empty() && !path.starts_with('/') {
|
||||
path.insert(0, '/')
|
||||
}
|
||||
if !path.ends_with('/') {
|
||||
path.push('/');
|
||||
}
|
||||
|
||||
let filters = scope.take_filters();
|
||||
self.nested.push((
|
||||
Resource::prefix("", &path),
|
||||
|
@ -253,7 +237,12 @@ impl<S: 'static> Scope<S> {
|
|||
|
||||
let mut handler = ResourceHandler::default();
|
||||
handler.method(method).with(f);
|
||||
let pattern = Resource::new(handler.get_name(), path);
|
||||
let pattern = Resource::with_prefix(
|
||||
handler.get_name(),
|
||||
path,
|
||||
if path.is_empty() { "" } else { "/" },
|
||||
false,
|
||||
);
|
||||
Rc::get_mut(&mut self.resources)
|
||||
.expect("Can not use after configuration")
|
||||
.push((pattern, Rc::new(UnsafeCell::new(handler))));
|
||||
|
@ -293,7 +282,12 @@ impl<S: 'static> Scope<S> {
|
|||
let mut handler = ResourceHandler::default();
|
||||
f(&mut handler);
|
||||
|
||||
let pattern = Resource::new(handler.get_name(), path);
|
||||
let pattern = Resource::with_prefix(
|
||||
handler.get_name(),
|
||||
path,
|
||||
if path.is_empty() { "" } else { "/" },
|
||||
false,
|
||||
);
|
||||
Rc::get_mut(&mut self.resources)
|
||||
.expect("Can not use after configuration")
|
||||
.push((pattern, Rc::new(UnsafeCell::new(handler))));
|
||||
|
@ -329,7 +323,6 @@ impl<S: 'static> Scope<S> {
|
|||
impl<S: 'static> RouteHandler<S> for Scope<S> {
|
||||
fn handle(&mut self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> {
|
||||
let path = unsafe { &*(&req.match_info()["tail"] as *const _) };
|
||||
let path = if path == "" { "/" } else { path };
|
||||
|
||||
// recognize resources
|
||||
for &(ref pattern, ref resource) in self.resources.iter() {
|
||||
|
@ -364,16 +357,12 @@ impl<S: 'static> RouteHandler<S> for Scope<S> {
|
|||
continue 'outer;
|
||||
}
|
||||
}
|
||||
let prefix_len = len + prefix_len - 1;
|
||||
let prefix_len = len + prefix_len;
|
||||
let path: &'static str =
|
||||
unsafe { &*(&req.path()[prefix_len..] as *const _) };
|
||||
|
||||
req.set_prefix_len(prefix_len as u16);
|
||||
if path.is_empty() {
|
||||
req.match_info_mut().set("tail", "/");
|
||||
} else {
|
||||
req.match_info_mut().set("tail", path);
|
||||
}
|
||||
req.match_info_mut().set("tail", path);
|
||||
|
||||
let hnd: &mut RouteHandler<_> =
|
||||
unsafe { (&mut *(handler.get())).as_mut() };
|
||||
|
@ -784,6 +773,25 @@ mod tests {
|
|||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_root() {
|
||||
let mut app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope
|
||||
.resource("", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Created()))
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_route() {
|
||||
let mut app = App::new()
|
||||
|
@ -885,6 +893,29 @@ mod tests {
|
|||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_with_state_root() {
|
||||
struct State;
|
||||
|
||||
let mut app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope.with_state("/t1", State, |scope| {
|
||||
scope
|
||||
.resource("", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Created()))
|
||||
})
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_scope_with_state_filter() {
|
||||
struct State;
|
||||
|
@ -927,6 +958,27 @@ mod tests {
|
|||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_scope_root() {
|
||||
let mut app = App::new()
|
||||
.scope("/app", |scope| {
|
||||
scope.nested("/t1", |scope| {
|
||||
scope
|
||||
.resource("", |r| r.f(|_| HttpResponse::Ok()))
|
||||
.resource("/", |r| r.f(|_| HttpResponse::Created()))
|
||||
})
|
||||
})
|
||||
.finish();
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::OK);
|
||||
|
||||
let req = TestRequest::with_uri("/app/t1/").finish();
|
||||
let resp = app.run(req);
|
||||
assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_scope_filter() {
|
||||
let mut app = App::new()
|
||||
|
|
|
@ -506,7 +506,7 @@ impl ContentEncoder {
|
|||
TransferEncoding::eof(buf)
|
||||
} else {
|
||||
if !(encoding == ContentEncoding::Identity
|
||||
|| encoding == ContentEncoding::Auto)
|
||||
|| encoding == ContentEncoding::Auto)
|
||||
{
|
||||
resp.headers_mut().remove(CONTENT_LENGTH);
|
||||
}
|
||||
|
|
|
@ -273,7 +273,7 @@ where
|
|||
Ok(Async::Ready(_)) => {
|
||||
// non consumed payload in that case close connection
|
||||
if self.payload.is_some() && self.tasks.is_empty() {
|
||||
return Ok(Async::Ready(false))
|
||||
return Ok(Async::Ready(false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#![cfg_attr(feature = "cargo-clippy", allow(redundant_field_names))]
|
||||
|
||||
use bytes::{BytesMut, BufMut};
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures::{Async, Poll};
|
||||
use std::io;
|
||||
use std::rc::Rc;
|
||||
|
|
|
@ -136,7 +136,7 @@ impl HttpHandler for Box<HttpHandler> {
|
|||
#[doc(hidden)]
|
||||
pub trait HttpHandlerTask {
|
||||
/// Poll task, this method is used before or after *io* object is available
|
||||
fn poll(&mut self) -> Poll<(), Error>{
|
||||
fn poll(&mut self) -> Poll<(), Error> {
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
|
||||
|
|
|
@ -374,9 +374,7 @@ fn test_scope_and_path_extractor() {
|
|||
App::new().scope("/sc", |scope| {
|
||||
scope.resource("/{num}/index.html", |r| {
|
||||
r.route()
|
||||
.with(|p: Path<(usize,)>| {
|
||||
format!("Welcome {}!", p.0)
|
||||
})
|
||||
.with(|p: Path<(usize,)>| format!("Welcome {}!", p.0))
|
||||
})
|
||||
})
|
||||
});
|
||||
|
@ -410,10 +408,9 @@ fn test_nested_scope_and_path_extractor() {
|
|||
App::new().scope("/sc", |scope| {
|
||||
scope.nested("/{num}", |scope| {
|
||||
scope.resource("/{num}/index.html", |r| {
|
||||
r.route()
|
||||
.with(|p: Path<(usize, usize)>| {
|
||||
format!("Welcome {} {}!", p.0, p.1)
|
||||
})
|
||||
r.route().with(|p: Path<(usize, usize)>| {
|
||||
format!("Welcome {} {}!", p.0, p.1)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
Loading…
Reference in a new issue