1
0
Fork 0
mirror of https://github.com/actix/actix-web.git synced 2024-10-03 08:41:55 +00:00

refactor HttpRequest mutability

This commit is contained in:
Nikolay Kim 2018-06-25 10:58:04 +06:00
parent 445ea043dd
commit fec6047ddc
51 changed files with 2239 additions and 2156 deletions

View file

@ -7,12 +7,13 @@ use http::{Method, StatusCode};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::Middleware; use middleware::Middleware;
use param::Params;
use pipeline::{HandlerType, Pipeline, PipelineHandler}; use pipeline::{HandlerType, Pipeline, PipelineHandler};
use pred::Predicate; use pred::Predicate;
use resource::ResourceHandler; use resource::ResourceHandler;
use router::{Resource, Router}; use router::{Resource, RouteInfo, Router};
use scope::Scope; use scope::Scope;
use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, ServerSettings}; use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, Request, ServerSettings};
/// Application /// Application
pub struct HttpApplication<S = ()> { pub struct HttpApplication<S = ()> {
@ -46,36 +47,34 @@ impl<S: 'static> PipelineHandler<S> for Inner<S> {
} }
fn handle( fn handle(
&self, req: HttpRequest<S>, htype: HandlerType, &self, req: &HttpRequest<S>, htype: HandlerType,
) -> AsyncResult<HttpResponse> { ) -> AsyncResult<HttpResponse> {
match htype { match htype {
HandlerType::Normal(idx) => match self.resources[idx].handle(req) { HandlerType::Normal(idx) => {
Ok(result) => result, if let Some(id) = self.resources[idx].get_route_id(req) {
Err(req) => match self.default.handle(req) { return self.resources[idx].handle(id, req);
Ok(result) => result, }
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), }
},
},
HandlerType::Handler(idx) => match self.handlers[idx] { HandlerType::Handler(idx) => match self.handlers[idx] {
PrefixHandlerType::Handler(_, ref hnd) => hnd.handle(req), PrefixHandlerType::Handler(_, ref hnd) => return hnd.handle(req),
PrefixHandlerType::Scope(_, ref hnd, _) => hnd.handle(req), PrefixHandlerType::Scope(_, ref hnd, _) => return hnd.handle(req),
},
HandlerType::Default => match self.default.handle(req) {
Ok(result) => result,
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)),
}, },
_ => (),
}
if let Some(id) = self.default.get_route_id(req) {
self.default.handle(id, req)
} else {
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
} }
} }
} }
impl<S: 'static> HttpApplication<S> { impl<S: 'static> HttpApplication<S> {
#[inline] #[inline]
fn get_handler(&self, req: &mut HttpRequest<S>) -> HandlerType { fn get_handler(&self, req: &Request) -> (RouteInfo, HandlerType) {
if let Some(idx) = self.router.recognize(req) { if let Some((idx, info)) = self.router.recognize(req) {
HandlerType::Normal(idx) (info, HandlerType::Normal(idx))
} else { } else {
req.match_info_mut().set_tail(0);
'outer: for idx in 0..self.inner.handlers.len() { 'outer: for idx in 0..self.inner.handlers.len() {
match self.inner.handlers[idx] { match self.inner.handlers[idx] {
PrefixHandlerType::Handler(ref prefix, _) => { PrefixHandlerType::Handler(ref prefix, _) => {
@ -90,77 +89,68 @@ impl<S: 'static> HttpApplication<S> {
if m { if m {
let prefix_len = (self.inner.prefix + prefix.len()) as u16; let prefix_len = (self.inner.prefix + prefix.len()) as u16;
let url = req.url().clone(); let info = self.router.route_info(req, prefix_len);
req.set_prefix_len(prefix_len); return (info, HandlerType::Handler(idx));
req.match_info_mut().set_url(url);
req.match_info_mut().set_tail(prefix_len);
return HandlerType::Handler(idx);
} }
} }
PrefixHandlerType::Scope(ref pattern, _, ref filters) => { PrefixHandlerType::Scope(ref pattern, _, ref filters) => {
if let Some(prefix_len) = if let Some(params) =
pattern.match_prefix_with_params(req, self.inner.prefix) pattern.match_prefix_with_params(req, self.inner.prefix)
{ {
for filter in filters { for filter in filters {
if !filter.check(req) { if !filter.check(req, &self.state) {
continue 'outer; continue 'outer;
} }
} }
let info = self
let prefix_len = (self.inner.prefix + prefix_len) as u16; .router
let url = req.url().clone(); .route_info_params(params, self.inner.prefix as u16);
req.set_prefix_len(prefix_len); return (info, HandlerType::Handler(idx));
let params = req.match_info_mut();
params.set_tail(prefix_len);
params.set_url(url);
return HandlerType::Handler(idx);
} }
} }
} }
} }
HandlerType::Default (
self.router.default_route_info(self.inner.prefix as u16),
HandlerType::Default,
)
} }
} }
#[cfg(test)] #[cfg(test)]
pub(crate) fn run(&self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> { pub(crate) fn run(&self, mut req: Request) -> AsyncResult<HttpResponse> {
let tp = self.get_handler(&mut req); let (info, tp) = self.get_handler(&req);
self.inner.handle(req, tp) let req = HttpRequest::new(req, Rc::clone(&self.state), info);
}
#[cfg(test)] self.inner.handle(&req, tp)
pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest<S> {
req.with_state(Rc::clone(&self.state), self.router.clone())
} }
} }
impl<S: 'static> HttpHandler for HttpApplication<S> { impl<S: 'static> HttpHandler for HttpApplication<S> {
type Task = Pipeline<S, Inner<S>>; type Task = Pipeline<S, Inner<S>>;
fn handle(&self, req: HttpRequest) -> Result<Pipeline<S, Inner<S>>, HttpRequest> { fn handle(&self, mut msg: Request) -> Result<Pipeline<S, Inner<S>>, Request> {
let m = { let m = {
let path = req.path(); let path = msg.path();
path.starts_with(&self.prefix) path.starts_with(&self.prefix)
&& (path.len() == self.prefix_len && (path.len() == self.prefix_len
|| path.split_at(self.prefix_len).1.starts_with('/')) || path.split_at(self.prefix_len).1.starts_with('/'))
}; };
if m { if m {
let mut req2 =
req.clone_with_state(Rc::clone(&self.state), self.router.clone());
if let Some(ref filters) = self.filters { if let Some(ref filters) = self.filters {
for filter in filters { for filter in filters {
if !filter.check(&mut req2) { if !filter.check(&msg, &self.state) {
return Err(req); return Err(msg);
} }
} }
} }
let tp = self.get_handler(&mut req2); let (info, tp) = self.get_handler(&msg);
let inner = Rc::clone(&self.inner); let inner = Rc::clone(&self.inner);
Ok(Pipeline::new(req2, Rc::clone(&self.middlewares), inner, tp)) let req = HttpRequest::new(msg, Rc::clone(&self.state), info);
Ok(Pipeline::new(req, Rc::clone(&self.middlewares), inner, tp))
} else { } else {
Err(req) Err(msg)
} }
} }
} }
@ -168,7 +158,6 @@ impl<S: 'static> HttpHandler for HttpApplication<S> {
struct ApplicationParts<S> { struct ApplicationParts<S> {
state: S, state: S,
prefix: String, prefix: String,
settings: ServerSettings,
default: Rc<ResourceHandler<S>>, default: Rc<ResourceHandler<S>>,
resources: Vec<(Resource, Option<ResourceHandler<S>>)>, resources: Vec<(Resource, Option<ResourceHandler<S>>)>,
handlers: Vec<PrefixHandlerType<S>>, handlers: Vec<PrefixHandlerType<S>>,
@ -219,7 +208,6 @@ where
parts: Some(ApplicationParts { parts: Some(ApplicationParts {
state, state,
prefix: "/".to_owned(), prefix: "/".to_owned(),
settings: ServerSettings::default(),
default: Rc::new(ResourceHandler::default_not_found()), default: Rc::new(ResourceHandler::default_not_found()),
resources: Vec::new(), resources: Vec::new(),
handlers: Vec::new(), handlers: Vec::new(),
@ -498,7 +486,7 @@ where
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{App, HttpRequest, HttpResponse, Result}; /// use actix_web::{App, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(mut req: HttpRequest) -> Result<HttpResponse> { /// fn index(req: &HttpRequest) -> Result<HttpResponse> {
/// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?; /// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?;
/// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0"); /// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0");
/// Ok(HttpResponse::Ok().into()) /// Ok(HttpResponse::Ok().into())
@ -544,7 +532,7 @@ where
/// use actix_web::{http, App, HttpRequest, HttpResponse}; /// use actix_web::{http, App, HttpRequest, HttpResponse};
/// ///
/// fn main() { /// fn main() {
/// let app = App::new().handler("/app", |req: HttpRequest| match *req.method() { /// let app = App::new().handler("/app", |req: &HttpRequest| match *req.method() {
/// http::Method::GET => HttpResponse::Ok(), /// http::Method::GET => HttpResponse::Ok(),
/// http::Method::POST => HttpResponse::MethodNotAllowed(), /// http::Method::POST => HttpResponse::MethodNotAllowed(),
/// _ => HttpResponse::NotFound(), /// _ => HttpResponse::NotFound(),
@ -636,8 +624,7 @@ where
}; };
} }
let (router, resources) = let (router, resources) = Router::new(&prefix, resources);
Router::new(&prefix, parts.settings.clone(), resources);
let inner = Rc::new(Inner { let inner = Rc::new(Inner {
prefix: prefix_len, prefix: prefix_len,
@ -708,7 +695,7 @@ struct BoxedApplication<S> {
impl<S: 'static> HttpHandler for BoxedApplication<S> { impl<S: 'static> HttpHandler for BoxedApplication<S> {
type Task = Box<HttpHandlerTask>; type Task = Box<HttpHandlerTask>;
fn handle(&self, req: HttpRequest) -> Result<Self::Task, HttpRequest> { fn handle(&self, req: Request) -> Result<Self::Task, Request> {
self.app.handle(req).map(|t| { self.app.handle(req).map(|t| {
let task: Self::Task = Box::new(t); let task: Self::Task = Box::new(t);
task task
@ -719,11 +706,7 @@ impl<S: 'static> HttpHandler for BoxedApplication<S> {
impl<S: 'static> IntoHttpHandler for App<S> { impl<S: 'static> IntoHttpHandler for App<S> {
type Handler = HttpApplication<S>; type Handler = HttpApplication<S>;
fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> { fn into_handler(mut self) -> HttpApplication<S> {
{
let parts = self.parts.as_mut().expect("Use after finish");
parts.settings = settings;
}
self.finish() self.finish()
} }
} }
@ -731,11 +714,7 @@ impl<S: 'static> IntoHttpHandler for App<S> {
impl<'a, S: 'static> IntoHttpHandler for &'a mut App<S> { impl<'a, S: 'static> IntoHttpHandler for &'a mut App<S> {
type Handler = HttpApplication<S>; type Handler = HttpApplication<S>;
fn into_handler(self, settings: ServerSettings) -> HttpApplication<S> { fn into_handler(self) -> HttpApplication<S> {
{
let parts = self.parts.as_mut().expect("Use after finish");
parts.settings = settings;
}
self.finish() self.finish()
} }
} }
@ -769,18 +748,18 @@ mod tests {
.resource("/test", |r| r.f(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
.finish(); .finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let app = App::new() let app = App::new()
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
.finish(); .finish();
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
} }
@ -791,7 +770,8 @@ mod tests {
.prefix("/test") .prefix("/test")
.resource("/test", |r| r.f(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
.finish(); .finish();
assert!(app.handle(HttpRequest::default()).is_err()); let ctx = TestRequest::default().request();
assert!(app.handle(ctx).is_err());
} }
#[test] #[test]
@ -799,8 +779,7 @@ mod tests {
let app = App::with_state(10) let app = App::with_state(10)
.resource("/", |r| r.f(|_| HttpResponse::Ok())) .resource("/", |r| r.f(|_| HttpResponse::Ok()))
.finish(); .finish();
let req = let req = TestRequest::with_state(10).request();
HttpRequest::default().with_state(Rc::clone(&app.state), app.router.clone());
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -811,69 +790,73 @@ mod tests {
.prefix("/test") .prefix("/test")
.resource("/blah", |r| r.f(|_| HttpResponse::Ok())) .resource("/blah", |r| r.f(|_| HttpResponse::Ok()))
.finish(); .finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").request();
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_ok()); assert!(resp.is_ok());
let req = TestRequest::with_uri("/test/").finish(); let req = TestRequest::with_uri("/test/").request();
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_ok()); assert!(resp.is_ok());
let req = TestRequest::with_uri("/test/blah").finish(); let req = TestRequest::with_uri("/test/blah").request();
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_ok()); assert!(resp.is_ok());
let req = TestRequest::with_uri("/testing").finish(); let req = TestRequest::with_uri("/testing").request();
let resp = app.handle(req); let resp = app.handle(req);
assert!(resp.is_err()); assert!(resp.is_err());
} }
#[test] #[test]
fn test_handler() { fn test_handler() {
let app = App::new().handler("/test", |_| HttpResponse::Ok()).finish(); let app = App::new()
.handler("/test", |_: &_| HttpResponse::Ok())
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/").finish(); let req = TestRequest::with_uri("/test/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/app").finish(); let req = TestRequest::with_uri("/test/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/testapp").finish(); let req = TestRequest::with_uri("/testapp").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
#[test] #[test]
fn test_handler2() { fn test_handler2() {
let app = App::new().handler("test", |_| HttpResponse::Ok()).finish(); let app = App::new()
.handler("test", |_: &_| HttpResponse::Ok())
.finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/").finish(); let req = TestRequest::with_uri("/test/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test/app").finish(); let req = TestRequest::with_uri("/test/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/testapp").finish(); let req = TestRequest::with_uri("/testapp").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/blah").finish(); let req = TestRequest::with_uri("/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -882,26 +865,26 @@ mod tests {
fn test_handler_with_prefix() { fn test_handler_with_prefix() {
let app = App::new() let app = App::new()
.prefix("prefix") .prefix("prefix")
.handler("/test", |_| HttpResponse::Ok()) .handler("/test", |_: &_| HttpResponse::Ok())
.finish(); .finish();
let req = TestRequest::with_uri("/prefix/test").finish(); let req = TestRequest::with_uri("/prefix/test").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/prefix/test/").finish(); let req = TestRequest::with_uri("/prefix/test/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/prefix/test/app").finish(); let req = TestRequest::with_uri("/prefix/test/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/prefix/testapp").finish(); let req = TestRequest::with_uri("/prefix/testapp").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/prefix/blah").finish(); let req = TestRequest::with_uri("/prefix/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -915,15 +898,19 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/test").method(Method::GET).finish(); let req = TestRequest::with_uri("/test").method(Method::GET).request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/test").method(Method::POST).finish(); let req = TestRequest::with_uri("/test")
.method(Method::POST)
.request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
let req = TestRequest::with_uri("/test").method(Method::HEAD).finish(); let req = TestRequest::with_uri("/test")
.method(Method::HEAD)
.request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -932,31 +919,30 @@ mod tests {
fn test_handler_prefix() { fn test_handler_prefix() {
let app = App::new() let app = App::new()
.prefix("/app") .prefix("/app")
.handler("/test", |_| HttpResponse::Ok()) .handler("/test", |_: &_| HttpResponse::Ok())
.finish(); .finish();
let req = TestRequest::with_uri("/test").finish(); let req = TestRequest::with_uri("/test").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/test").finish(); let req = TestRequest::with_uri("/app/test").request();
let resp = app.run(req.clone());
assert_eq!(resp.as_msg().status(), StatusCode::OK);
assert_eq!(req.prefix_len(), 9);
let req = TestRequest::with_uri("/app/test/").finish();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/test/app").finish(); let req = TestRequest::with_uri("/app/test/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/testapp").finish(); let req = TestRequest::with_uri("/app/test/app").request();
let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/testapp").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/blah").finish(); let req = TestRequest::with_uri("/app/blah").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -966,7 +952,7 @@ mod tests {
let mut srv = TestServer::with_factory(|| { let mut srv = TestServer::with_factory(|| {
App::new() App::new()
.filter(pred::Get()) .filter(pred::Get())
.handler("/test", |_| HttpResponse::Ok()) .handler("/test", |_: &_| HttpResponse::Ok())
}); });
let request = srv.get().uri(srv.url("/test")).finish().unwrap(); let request = srv.get().uri(srv.url("/test")).finish().unwrap();
@ -985,11 +971,11 @@ mod tests {
.resource("/some", |r| r.f(|_| Some("some"))) .resource("/some", |r| r.f(|_| Some("some")))
.finish(); .finish();
let req = TestRequest::with_uri("/none").finish(); let req = TestRequest::with_uri("/none").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/some").finish(); let req = TestRequest::with_uri("/some").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some"))); assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some")));

View file

@ -35,6 +35,7 @@ pub use self::connector::{
Pause, Resume, Pause, Resume,
}; };
pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError}; pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError};
pub(crate) use self::pipeline::Pipeline;
pub use self::pipeline::{SendRequest, SendRequestError}; pub use self::pipeline::{SendRequest, SendRequestError};
pub use self::request::{ClientRequest, ClientRequestBuilder}; pub use self::request::{ClientRequest, ClientRequestBuilder};
pub use self::response::ClientResponse; pub use self::response::ClientResponse;

View file

@ -1,6 +1,6 @@
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use futures::sync::oneshot; use futures::sync::oneshot;
use futures::{Async, Future, Poll}; use futures::{Async, Future, Poll, Stream};
use http::header::CONTENT_ENCODING; use http::header::CONTENT_ENCODING;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use std::{io, mem}; use std::{io, mem};
@ -230,7 +230,7 @@ impl Future for SendRequest {
} }
} }
pub(crate) struct Pipeline { pub struct Pipeline {
body: IoBody, body: IoBody,
body_completed: bool, body_completed: bool,
conn: Option<Connection>, conn: Option<Connection>,
@ -315,7 +315,7 @@ impl Pipeline {
} }
#[inline] #[inline]
pub fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> { pub(crate) fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
if self.conn.is_none() { if self.conn.is_none() {
return Ok(Async::Ready(None)); return Ok(Async::Ready(None));
} }
@ -522,3 +522,13 @@ impl Drop for Pipeline {
} }
} }
} }
/// Future that resolves to a complete request body.
impl Stream for Box<Pipeline> {
type Item = Bytes;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
Pipeline::poll(self)
}
}

View file

@ -1,3 +1,4 @@
use std::cell::RefCell;
use std::{fmt, str}; use std::{fmt, str};
use bytes::Bytes; use bytes::Bytes;
@ -30,23 +31,33 @@ impl Default for ClientMessage {
} }
/// An HTTP Client response /// An HTTP Client response
pub struct ClientResponse(ClientMessage, Option<Box<Pipeline>>); pub struct ClientResponse(ClientMessage, RefCell<Option<Box<Pipeline>>>);
impl HttpMessage for ClientResponse { impl HttpMessage for ClientResponse {
type Stream = Box<Pipeline>;
/// Get the headers from the response. /// Get the headers from the response.
#[inline] #[inline]
fn headers(&self) -> &HeaderMap { fn headers(&self) -> &HeaderMap {
&self.0.headers &self.0.headers
} }
#[inline]
fn payload(&self) -> Box<Pipeline> {
self.1
.borrow_mut()
.take()
.expect("Payload is already consumed.")
}
} }
impl ClientResponse { impl ClientResponse {
pub(crate) fn new(msg: ClientMessage) -> ClientResponse { pub(crate) fn new(msg: ClientMessage) -> ClientResponse {
ClientResponse(msg, None) ClientResponse(msg, RefCell::new(None))
} }
pub(crate) fn set_pipeline(&mut self, pl: Box<Pipeline>) { pub(crate) fn set_pipeline(&mut self, pl: Box<Pipeline>) {
self.1 = Some(pl); *self.1.borrow_mut() = Some(pl);
} }
/// Get the HTTP version of this response. /// Get the HTTP version of this response.
@ -95,20 +106,6 @@ impl fmt::Debug for ClientResponse {
} }
} }
/// Future that resolves to a complete request body.
impl Stream for ClientResponse {
type Item = Bytes;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
if let Some(ref mut pl) = self.1 {
pl.poll()
} else {
Ok(Async::Ready(None))
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View file

@ -612,9 +612,6 @@ pub enum UrlGenerationError {
/// Not all path pattern covered /// Not all path pattern covered
#[fail(display = "Not all path pattern covered")] #[fail(display = "Not all path pattern covered")]
NotEnoughElements, NotEnoughElements,
/// Router is not available
#[fail(display = "Router is not available")]
RouterNotAvailable,
/// URL parse error /// URL parse error
#[fail(display = "{}", _0)] #[fail(display = "{}", _0)]
ParseError(#[cause] UrlParseError), ParseError(#[cause] UrlParseError),

View file

@ -267,12 +267,12 @@ where
#[inline] #[inline]
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result { fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
let req = req.clone(); let req2 = req.clone();
let err = Rc::clone(&cfg.ehandler); let err = Rc::clone(&cfg.ehandler);
Box::new( Box::new(
UrlEncoded::new(req.clone()) UrlEncoded::new(req)
.limit(cfg.limit) .limit(cfg.limit)
.map_err(move |e| (*err)(e, req)) .map_err(move |e| (*err)(e, &req2))
.map(Form), .map(Form),
) )
} }
@ -321,7 +321,7 @@ impl<T: fmt::Display> fmt::Display for Form<T> {
/// ``` /// ```
pub struct FormConfig<S> { pub struct FormConfig<S> {
limit: usize, limit: usize,
ehandler: Rc<Fn(UrlencodedError, HttpRequest<S>) -> Error>, ehandler: Rc<Fn(UrlencodedError, &HttpRequest<S>) -> Error>,
} }
impl<S> FormConfig<S> { impl<S> FormConfig<S> {
@ -334,7 +334,7 @@ impl<S> FormConfig<S> {
/// Set custom error handler /// Set custom error handler
pub fn error_handler<F>(&mut self, f: F) -> &mut Self pub fn error_handler<F>(&mut self, f: F) -> &mut Self
where where
F: Fn(UrlencodedError, HttpRequest<S>) -> Error + 'static, F: Fn(UrlencodedError, &HttpRequest<S>) -> Error + 'static,
{ {
self.ehandler = Rc::new(f); self.ehandler = Rc::new(f);
self self
@ -383,9 +383,7 @@ impl<S: 'static> FromRequest<S> for Bytes {
// check content-type // check content-type
cfg.check_mimetype(req)?; cfg.check_mimetype(req)?;
Ok(Box::new( Ok(Box::new(MessageBody::new(req).limit(cfg.limit).from_err()))
MessageBody::new(req.clone()).limit(cfg.limit).from_err(),
))
} }
} }
@ -429,7 +427,7 @@ impl<S: 'static> FromRequest<S> for String {
let encoding = req.encoding()?; let encoding = req.encoding()?;
Ok(Box::new( Ok(Box::new(
MessageBody::new(req.clone()) MessageBody::new(req)
.limit(cfg.limit) .limit(cfg.limit)
.from_err() .from_err()
.and_then(move |body| { .and_then(move |body| {
@ -617,7 +615,6 @@ mod tests {
use mime; use mime;
use resource::ResourceHandler; use resource::ResourceHandler;
use router::{Resource, Router}; use router::{Resource, Router};
use server::ServerSettings;
use test::TestRequest; use test::TestRequest;
#[derive(Deserialize, Debug, PartialEq)] #[derive(Deserialize, Debug, PartialEq)]
@ -628,9 +625,9 @@ mod tests {
#[test] #[test]
fn test_bytes() { fn test_bytes() {
let cfg = PayloadConfig::default(); let cfg = PayloadConfig::default();
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); let req = TestRequest::with_header(header::CONTENT_LENGTH, "11")
req.payload_mut() .set_payload(Bytes::from_static(b"hello=world"))
.unread_data(Bytes::from_static(b"hello=world")); .finish();
match Bytes::from_request(&req, &cfg).unwrap().poll().unwrap() { match Bytes::from_request(&req, &cfg).unwrap().poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
@ -643,9 +640,9 @@ mod tests {
#[test] #[test]
fn test_string() { fn test_string() {
let cfg = PayloadConfig::default(); let cfg = PayloadConfig::default();
let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); let req = TestRequest::with_header(header::CONTENT_LENGTH, "11")
req.payload_mut() .set_payload(Bytes::from_static(b"hello=world"))
.unread_data(Bytes::from_static(b"hello=world")); .finish();
match String::from_request(&req, &cfg).unwrap().poll().unwrap() { match String::from_request(&req, &cfg).unwrap().poll().unwrap() {
Async::Ready(s) => { Async::Ready(s) => {
@ -657,13 +654,12 @@ mod tests {
#[test] #[test]
fn test_form() { fn test_form() {
let mut req = TestRequest::with_header( let req = TestRequest::with_header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
"application/x-www-form-urlencoded", "application/x-www-form-urlencoded",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world"))
.finish(); .finish();
req.payload_mut()
.unread_data(Bytes::from_static(b"hello=world"));
let mut cfg = FormConfig::default(); let mut cfg = FormConfig::default();
cfg.limit(4096); cfg.limit(4096);
@ -677,7 +673,7 @@ mod tests {
#[test] #[test]
fn test_payload_config() { fn test_payload_config() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
let mut cfg = PayloadConfig::default(); let mut cfg = PayloadConfig::default();
cfg.mimetype(mime::APPLICATION_JSON); cfg.mimetype(mime::APPLICATION_JSON);
assert!(cfg.check_mimetype(&req).is_err()); assert!(cfg.check_mimetype(&req).is_err());
@ -712,14 +708,15 @@ mod tests {
#[test] #[test]
fn test_request_extract() { fn test_request_extract() {
let mut req = TestRequest::with_uri("/name/user1/?id=test").finish(); let req = TestRequest::with_uri("/name/user1/?id=test").finish();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let mut routes = Vec::new(); let mut routes = Vec::new();
routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource))); routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", routes);
assert!(router.recognize(&mut req).is_some()); let info = router.recognize(&req).unwrap().1;
let req = req.with_route_info(info);
let s = Path::<MyStruct>::from_request(&req, &()).unwrap(); let s = Path::<MyStruct>::from_request(&req, &()).unwrap();
assert_eq!(s.key, "name"); assert_eq!(s.key, "name");
@ -732,8 +729,9 @@ mod tests {
let s = Query::<Id>::from_request(&req, &()).unwrap(); let s = Query::<Id>::from_request(&req, &()).unwrap();
assert_eq!(s.id, "test"); assert_eq!(s.id, "test");
let mut req = TestRequest::with_uri("/name/32/").finish(); let req = TestRequest::with_uri("/name/32/").finish_with_router(router.clone());
assert!(router.recognize(&mut req).is_some()); let info = router.recognize(&req).unwrap().1;
let req = req.with_route_info(info);
let s = Path::<Test2>::from_request(&req, &()).unwrap(); let s = Path::<Test2>::from_request(&req, &()).unwrap();
assert_eq!(s.as_ref().key, "name"); assert_eq!(s.as_ref().key, "name");
@ -754,24 +752,26 @@ mod tests {
resource.name("index"); resource.name("index");
let mut routes = Vec::new(); let mut routes = Vec::new();
routes.push((Resource::new("index", "/{value}/"), Some(resource))); routes.push((Resource::new("index", "/{value}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", routes);
let mut req = TestRequest::with_uri("/32/").finish(); let req = TestRequest::with_uri("/32/").finish_with_router(router.clone());
assert!(router.recognize(&mut req).is_some()); let info = router.recognize(&req).unwrap().1;
let req = req.with_route_info(info);
assert_eq!(*Path::<i8>::from_request(&mut req, &()).unwrap(), 32); assert_eq!(*Path::<i8>::from_request(&req, &()).unwrap(), 32);
} }
#[test] #[test]
fn test_tuple_extract() { fn test_tuple_extract() {
let mut req = TestRequest::with_uri("/name/user1/?id=test").finish();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let mut routes = Vec::new(); let mut routes = Vec::new();
routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource))); routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", routes);
assert!(router.recognize(&mut req).is_some());
let mut req = TestRequest::with_uri("/name/user1/?id=test")
.finish_with_router(router.clone());
let info = router.recognize(&req).unwrap().1;
let req = req.with_route_info(info);
let res = match <(Path<(String, String)>,)>::extract(&req).poll() { let res = match <(Path<(String, String)>,)>::extract(&req).poll() {
Ok(Async::Ready(res)) => res, Ok(Async::Ready(res)) => res,

View file

@ -2,6 +2,7 @@
use std::fmt::Write; use std::fmt::Write;
use std::fs::{DirEntry, File, Metadata}; use std::fs::{DirEntry, File, Metadata};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};
@ -19,7 +20,9 @@ use mime_guess::{get_mime_type, guess_mime_type};
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET}; use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use error::Error; use error::Error;
use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler}; use handler::{
AsyncResult, AsyncResultItem, Handler, Responder, RouteHandler, WrapHandler,
};
use header; use header;
use http::{ContentEncoding, Method, StatusCode}; use http::{ContentEncoding, Method, StatusCode};
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
@ -610,7 +613,7 @@ impl<S: 'static> StaticFiles<S> {
index: None, index: None,
show_index: false, show_index: false,
cpu_pool: pool, cpu_pool: pool,
default: Box::new(WrapHandler::new(|_| { default: Box::new(WrapHandler::new(|_: &_| {
HttpResponse::new(StatusCode::NOT_FOUND) HttpResponse::new(StatusCode::NOT_FOUND)
})), })),
renderer: Box::new(directory_listing), renderer: Box::new(directory_listing),
@ -657,7 +660,7 @@ impl<S: 'static> StaticFiles<S> {
impl<S: 'static> Handler<S> for StaticFiles<S> { impl<S: 'static> Handler<S> for StaticFiles<S> {
type Result = Result<AsyncResult<HttpResponse>, Error>; type Result = Result<AsyncResult<HttpResponse>, Error>;
fn handle(&self, req: HttpRequest<S>) -> Self::Result { fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
if !self.accessible { if !self.accessible {
Ok(self.default.handle(req)) Ok(self.default.handle(req))
} else { } else {
@ -667,7 +670,9 @@ impl<S: 'static> Handler<S> for StaticFiles<S> {
.map(|tail| PathBuf::from_param(tail.trim_left_matches('/'))) .map(|tail| PathBuf::from_param(tail.trim_left_matches('/')))
{ {
Some(Ok(path)) => path, Some(Ok(path)) => path,
_ => return Ok(self.default.handle(req)), _ => {
return Ok(self.default.handle(req));
}
}; };
// full filepath // full filepath
@ -833,7 +838,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml" "text/x-toml"
@ -858,7 +864,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/xml" "text/xml"
@ -882,7 +889,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"image/png" "image/png"
@ -916,7 +924,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"image/png" "image/png"
@ -940,7 +949,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"application/octet-stream" "application/octet-stream"
@ -965,7 +975,8 @@ mod tests {
let _f: &mut File = &mut file; let _f: &mut File = &mut file;
} }
let resp = file.respond_to(&HttpRequest::default()).unwrap(); let req = TestRequest::default().finish();
let resp = file.respond_to(&req).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"text/x-toml" "text/x-toml"
@ -1136,7 +1147,6 @@ mod tests {
.unwrap(); .unwrap();
assert_eq!(te, "chunked"); assert_eq!(te, "chunked");
} }
let bytes = srv.execute(response.body()).unwrap(); let bytes = srv.execute(response.body()).unwrap();
let data = Bytes::from(fs::read("tests/test.binary").unwrap()); let data = Bytes::from(fs::read("tests/test.binary").unwrap());
assert_eq!(bytes, data); assert_eq!(bytes, data);
@ -1178,27 +1188,22 @@ mod tests {
fn test_static_files() { fn test_static_files() {
let mut st = StaticFiles::new(".").show_files_listing(); let mut st = StaticFiles::new(".").show_files_listing();
st.accessible = false; st.accessible = false;
let resp = st let req = TestRequest::default().finish();
.handle(HttpRequest::default()) let resp = st.handle(&req).respond_to(&req).unwrap();
.respond_to(&HttpRequest::default())
.unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
st.accessible = true; st.accessible = true;
st.show_index = false; st.show_index = false;
let resp = st let req = TestRequest::default().finish();
.handle(HttpRequest::default()) let resp = st.handle(&req).respond_to(&req).unwrap();
.respond_to(&HttpRequest::default())
.unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!(resp.status(), StatusCode::NOT_FOUND); assert_eq!(resp.status(), StatusCode::NOT_FOUND);
let mut req = HttpRequest::default(); let req = TestRequest::default().param("tail", "").finish();
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(&req).unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
@ -1213,7 +1218,7 @@ mod tests {
let st = StaticFiles::new(".").index_file("index.html"); let st = StaticFiles::new(".").index_file("index.html");
let req = TestRequest::default().uri("/tests").finish(); let req = TestRequest::default().uri("/tests").finish();
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap(); let resp = st.handle(&req).respond_to(&req).unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(
@ -1222,8 +1227,7 @@ mod tests {
); );
let req = TestRequest::default().uri("/tests/").finish(); let req = TestRequest::default().uri("/tests/").finish();
let resp = st.handle(&req).respond_to(&req).unwrap();
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(
@ -1234,15 +1238,14 @@ mod tests {
#[test] #[test]
fn test_redirect_to_index_nested() { fn test_redirect_to_index_nested() {
let st = StaticFiles::new(".").index_file("Cargo.toml"); let st = StaticFiles::new(".").index_file("mod.rs");
let req = TestRequest::default().uri("/tools/wsload").finish(); let req = TestRequest::default().uri("/src/client").finish();
let resp = st.handle(&req).respond_to(&req).unwrap();
let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap();
let resp = resp.as_msg(); let resp = resp.as_msg();
assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!(resp.status(), StatusCode::FOUND);
assert_eq!( assert_eq!(
resp.headers().get(header::LOCATION).unwrap(), resp.headers().get(header::LOCATION).unwrap(),
"/tools/wsload/Cargo.toml" "/src/client/mod.rs"
); );
} }

View file

@ -10,6 +10,7 @@ use http::StatusCode;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use resource::ResourceHandler; use resource::ResourceHandler;
use server::Request;
/// Trait defines object that could be registered as route handler /// Trait defines object that could be registered as route handler
#[allow(unused_variables)] #[allow(unused_variables)]
@ -18,7 +19,7 @@ pub trait Handler<S>: 'static {
type Result: Responder; type Result: Responder;
/// Handle request /// Handle request
fn handle(&self, req: HttpRequest<S>) -> Self::Result; fn handle(&self, req: &HttpRequest<S>) -> Self::Result;
} }
/// Trait implemented by types that generate responses for clients. /// Trait implemented by types that generate responses for clients.
@ -203,12 +204,12 @@ where
/// Handler<S> for Fn() /// Handler<S> for Fn()
impl<F, R, S> Handler<S> for F impl<F, R, S> Handler<S> for F
where where
F: Fn(HttpRequest<S>) -> R + 'static, F: Fn(&HttpRequest<S>) -> R + 'static,
R: Responder + 'static, R: Responder + 'static,
{ {
type Result = R; type Result = R;
fn handle(&self, req: HttpRequest<S>) -> R { fn handle(&self, req: &HttpRequest<S>) -> R {
(self)(req) (self)(req)
} }
} }
@ -402,9 +403,8 @@ where
} }
} }
// /// Trait defines object that could be registered as resource route
pub(crate) trait RouteHandler<S>: 'static { pub(crate) trait RouteHandler<S>: 'static {
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse>; fn handle(&self, &HttpRequest<S>) -> AsyncResult<HttpResponse>;
fn has_default_resource(&self) -> bool { fn has_default_resource(&self) -> bool {
false false
@ -443,8 +443,8 @@ where
R: Responder + 'static, R: Responder + 'static,
S: 'static, S: 'static,
{ {
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> { fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
match self.h.handle(req.clone()).respond_to(&req) { match self.h.handle(req).respond_to(req) {
Ok(reply) => reply.into(), Ok(reply) => reply.into(),
Err(err) => AsyncResult::err(err.into()), Err(err) => AsyncResult::err(err.into()),
} }
@ -454,7 +454,7 @@ where
/// Async route handler /// Async route handler
pub(crate) struct AsyncHandler<S, H, F, R, E> pub(crate) struct AsyncHandler<S, H, F, R, E>
where where
H: Fn(HttpRequest<S>) -> F + 'static, H: Fn(&HttpRequest<S>) -> F + 'static,
F: Future<Item = R, Error = E> + 'static, F: Future<Item = R, Error = E> + 'static,
R: Responder + 'static, R: Responder + 'static,
E: Into<Error> + 'static, E: Into<Error> + 'static,
@ -466,7 +466,7 @@ where
impl<S, H, F, R, E> AsyncHandler<S, H, F, R, E> impl<S, H, F, R, E> AsyncHandler<S, H, F, R, E>
where where
H: Fn(HttpRequest<S>) -> F + 'static, H: Fn(&HttpRequest<S>) -> F + 'static,
F: Future<Item = R, Error = E> + 'static, F: Future<Item = R, Error = E> + 'static,
R: Responder + 'static, R: Responder + 'static,
E: Into<Error> + 'static, E: Into<Error> + 'static,
@ -482,14 +482,15 @@ where
impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E> impl<S, H, F, R, E> RouteHandler<S> for AsyncHandler<S, H, F, R, E>
where where
H: Fn(HttpRequest<S>) -> F + 'static, H: Fn(&HttpRequest<S>) -> F + 'static,
F: Future<Item = R, Error = E> + 'static, F: Future<Item = R, Error = E> + 'static,
R: Responder + 'static, R: Responder + 'static,
E: Into<Error> + 'static, E: Into<Error> + 'static,
S: 'static, S: 'static,
{ {
fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> { fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
let fut = (self.h)(req.clone()).map_err(|e| e.into()).then(move |r| { let req = req.clone();
let fut = (self.h)(&req).map_err(|e| e.into()).then(move |r| {
match r.respond_to(&req) { match r.respond_to(&req) {
Ok(reply) => match reply.into().into() { Ok(reply) => match reply.into().into() {
AsyncResultItem::Ok(resp) => Either::A(ok(resp)), AsyncResultItem::Ok(resp) => Either::A(ok(resp)),

View file

@ -36,7 +36,7 @@ use httpresponse::HttpResponse;
/// # use actix_web::*; /// # use actix_web::*;
/// use actix_web::http::NormalizePath; /// use actix_web::http::NormalizePath;
/// ///
/// # fn index(req: HttpRequest) -> HttpResponse { /// # fn index(req: &HttpRequest) -> HttpResponse {
/// # HttpResponse::Ok().into() /// # HttpResponse::Ok().into()
/// # } /// # }
/// fn main() { /// fn main() {
@ -86,57 +86,41 @@ impl NormalizePath {
impl<S> Handler<S> for NormalizePath { impl<S> Handler<S> for NormalizePath {
type Result = HttpResponse; type Result = HttpResponse;
fn handle(&self, req: HttpRequest<S>) -> Self::Result { fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
if let Some(router) = req.router() { let query = req.query_string();
let query = req.query_string(); if self.merge {
if self.merge { // merge slashes
// merge slashes let p = self.re_merge.replace_all(req.path(), "/");
let p = self.re_merge.replace_all(req.path(), "/"); if p.len() != req.path().len() {
if p.len() != req.path().len() { if req.route().has_route(p.as_ref()) {
if router.has_route(p.as_ref()) { let p = if !query.is_empty() {
p + "?" + query
} else {
p
};
return HttpResponse::build(self.redirect)
.header(header::LOCATION, p.as_ref())
.finish();
}
// merge slashes and append trailing slash
if self.append && !p.ends_with('/') {
let p = p.as_ref().to_owned() + "/";
if req.route().has_route(&p) {
let p = if !query.is_empty() { let p = if !query.is_empty() {
p + "?" + query p + "?" + query
} else { } else {
p p
}; };
return HttpResponse::build(self.redirect) return HttpResponse::build(self.redirect)
.header(header::LOCATION, p.as_ref()) .header(header::LOCATION, p.as_str())
.finish(); .finish();
} }
// merge slashes and append trailing slash }
if self.append && !p.ends_with('/') {
let p = p.as_ref().to_owned() + "/";
if router.has_route(&p) {
let p = if !query.is_empty() {
p + "?" + query
} else {
p
};
return HttpResponse::build(self.redirect)
.header(header::LOCATION, p.as_str())
.finish();
}
}
// try to remove trailing slash // try to remove trailing slash
if p.ends_with('/') { if p.ends_with('/') {
let p = p.as_ref().trim_right_matches('/');
if router.has_route(p) {
let mut req = HttpResponse::build(self.redirect);
return if !query.is_empty() {
req.header(
header::LOCATION,
(p.to_owned() + "?" + query).as_str(),
)
} else {
req.header(header::LOCATION, p)
}.finish();
}
}
} else if p.ends_with('/') {
// try to remove trailing slash
let p = p.as_ref().trim_right_matches('/'); let p = p.as_ref().trim_right_matches('/');
if router.has_route(p) { if req.route().has_route(p) {
let mut req = HttpResponse::build(self.redirect); let mut req = HttpResponse::build(self.redirect);
return if !query.is_empty() { return if !query.is_empty() {
req.header( req.header(
@ -148,22 +132,36 @@ impl<S> Handler<S> for NormalizePath {
}.finish(); }.finish();
} }
} }
} } else if p.ends_with('/') {
// append trailing slash // try to remove trailing slash
if self.append && !req.path().ends_with('/') { let p = p.as_ref().trim_right_matches('/');
let p = req.path().to_owned() + "/"; if req.route().has_route(p) {
if router.has_route(&p) { let mut req = HttpResponse::build(self.redirect);
let p = if !query.is_empty() { return if !query.is_empty() {
p + "?" + query req.header(
header::LOCATION,
(p.to_owned() + "?" + query).as_str(),
)
} else { } else {
p req.header(header::LOCATION, p)
}; }.finish();
return HttpResponse::build(self.redirect)
.header(header::LOCATION, p.as_str())
.finish();
} }
} }
} }
// append trailing slash
if self.append && !req.path().ends_with('/') {
let p = req.path().to_owned() + "/";
if req.route().has_route(&p) {
let p = if !query.is_empty() {
p + "?" + query
} else {
p
};
return HttpResponse::build(self.redirect)
.header(header::LOCATION, p.as_str())
.finish();
}
}
HttpResponse::new(self.not_found) HttpResponse::new(self.not_found)
} }
} }
@ -175,7 +173,7 @@ mod tests {
use http::{header, Method}; use http::{header, Method};
use test::TestRequest; use test::TestRequest;
fn index(_req: HttpRequest) -> HttpResponse { fn index(_req: &HttpRequest) -> HttpResponse {
HttpResponse::new(StatusCode::OK) HttpResponse::new(StatusCode::OK)
} }
@ -207,9 +205,9 @@ mod tests {
("/resource2/?p1=1&p2=2", "", StatusCode::OK), ("/resource2/?p1=1&p2=2", "", StatusCode::OK),
]; ];
for (path, target, code) in params { for (path, target, code) in params {
let req = app.prepare_request(TestRequest::with_uri(path).finish()); let req = TestRequest::with_uri(path).request();
let resp = app.run(req); let resp = app.run(req);
let r = resp.as_msg(); let r = &resp.as_msg();
assert_eq!(r.status(), code); assert_eq!(r.status(), code);
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(
@ -246,9 +244,9 @@ mod tests {
("/resource2/?p1=1&p2=2", StatusCode::OK), ("/resource2/?p1=1&p2=2", StatusCode::OK),
]; ];
for (path, code) in params { for (path, code) in params {
let req = app.prepare_request(TestRequest::with_uri(path).finish()); let req = TestRequest::with_uri(path).request();
let resp = app.run(req); let resp = app.run(req);
let r = resp.as_msg(); let r = &resp.as_msg();
assert_eq!(r.status(), code); assert_eq!(r.status(), code);
} }
} }
@ -329,9 +327,9 @@ mod tests {
), ),
]; ];
for (path, target, code) in params { for (path, target, code) in params {
let req = app.prepare_request(TestRequest::with_uri(path).finish()); let req = TestRequest::with_uri(path).request();
let resp = app.run(req); let resp = app.run(req);
let r = resp.as_msg(); let r = &resp.as_msg();
assert_eq!(r.status(), code); assert_eq!(r.status(), code);
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(
@ -509,9 +507,9 @@ mod tests {
), ),
]; ];
for (path, target, code) in params { for (path, target, code) in params {
let req = app.prepare_request(TestRequest::with_uri(path).finish()); let req = TestRequest::with_uri(path).request();
let resp = app.run(req); let resp = app.run(req);
let r = resp.as_msg(); let r = &resp.as_msg();
assert_eq!(r.status(), code); assert_eq!(r.status(), code);
if !target.is_empty() { if !target.is_empty() {
assert_eq!( assert_eq!(

View file

@ -16,12 +16,19 @@ use error::{
use header::Header; use header::Header;
use json::JsonBody; use json::JsonBody;
use multipart::Multipart; use multipart::Multipart;
use payload::Payload;
/// Trait that implements general purpose operations on http messages /// Trait that implements general purpose operations on http messages
pub trait HttpMessage { pub trait HttpMessage: Sized {
/// Type of message payload stream
type Stream: Stream<Item = Bytes, Error = PayloadError> + Sized;
/// Read the message headers. /// Read the message headers.
fn headers(&self) -> &HeaderMap; fn headers(&self) -> &HeaderMap;
/// Message payload stream
fn payload(&self) -> Self::Stream;
#[doc(hidden)] #[doc(hidden)]
/// Get a header /// Get a header
fn get_header<H: Header>(&self) -> Option<H> fn get_header<H: Header>(&self) -> Option<H>
@ -123,10 +130,7 @@ pub trait HttpMessage {
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
fn body(self) -> MessageBody<Self> fn body(&self) -> MessageBody<Self> {
where
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
{
MessageBody::new(self) MessageBody::new(self)
} }
@ -160,10 +164,7 @@ pub trait HttpMessage {
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
fn urlencoded<T: DeserializeOwned>(self) -> UrlEncoded<Self, T> fn urlencoded<T: DeserializeOwned>(&self) -> UrlEncoded<Self, T> {
where
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
{
UrlEncoded::new(self) UrlEncoded::new(self)
} }
@ -199,10 +200,7 @@ pub trait HttpMessage {
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
fn json<T: DeserializeOwned>(self) -> JsonBody<Self, T> fn json<T: DeserializeOwned>(&self) -> JsonBody<Self, T> {
where
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
{
JsonBody::new(self) JsonBody::new(self)
} }
@ -241,45 +239,42 @@ pub trait HttpMessage {
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
fn multipart(self) -> Multipart<Self> fn multipart(&self) -> Multipart<Self::Stream> {
where
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
{
let boundary = Multipart::boundary(self.headers()); let boundary = Multipart::boundary(self.headers());
Multipart::new(boundary, self) Multipart::new(boundary, self.payload())
} }
/// Return stream of lines. /// Return stream of lines.
fn readlines(self) -> Readlines<Self> fn readlines(&self) -> Readlines<Self> {
where
Self: Stream<Item = Bytes, Error = PayloadError> + Sized,
{
Readlines::new(self) Readlines::new(self)
} }
} }
/// Stream to read request line by line. /// Stream to read request line by line.
pub struct Readlines<T> pub struct Readlines<T: HttpMessage> {
where stream: T::Stream,
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
{
req: T,
buff: BytesMut, buff: BytesMut,
limit: usize, limit: usize,
checked_buff: bool, checked_buff: bool,
encoding: EncodingRef,
err: Option<ReadlinesError>,
} }
impl<T> Readlines<T> impl<T: HttpMessage> Readlines<T> {
where
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
{
/// Create a new stream to read request line by line. /// Create a new stream to read request line by line.
fn new(req: T) -> Self { fn new(req: &T) -> Self {
let encoding = match req.encoding() {
Ok(enc) => enc,
Err(err) => return Self::err(req, err.into()),
};
Readlines { Readlines {
req, stream: req.payload(),
buff: BytesMut::with_capacity(262_144), buff: BytesMut::with_capacity(262_144),
limit: 262_144, limit: 262_144,
checked_buff: true, checked_buff: true,
err: None,
encoding,
} }
} }
@ -288,17 +283,28 @@ where
self.limit = limit; self.limit = limit;
self self
} }
fn err(req: &T, err: ReadlinesError) -> Self {
Readlines {
stream: req.payload(),
buff: BytesMut::with_capacity(262_144),
limit: 262_144,
checked_buff: true,
encoding: UTF_8,
err: Some(err),
}
}
} }
impl<T> Stream for Readlines<T> impl<T: HttpMessage + 'static> Stream for Readlines<T> {
where
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
{
type Item = String; type Item = String;
type Error = ReadlinesError; type Error = ReadlinesError;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> { fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let encoding = self.req.encoding()?; if let Some(err) = self.err.take() {
return Err(err);
}
// check if there is a newline in the buffer // check if there is a newline in the buffer
if !self.checked_buff { if !self.checked_buff {
let mut found: Option<usize> = None; let mut found: Option<usize> = None;
@ -313,13 +319,13 @@ where
if ind + 1 > self.limit { if ind + 1 > self.limit {
return Err(ReadlinesError::LimitOverflow); return Err(ReadlinesError::LimitOverflow);
} }
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&self.buff.split_to(ind + 1)) str::from_utf8(&self.buff.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding self.encoding
.decode(&self.buff.split_to(ind + 1), DecoderTrap::Strict) .decode(&self.buff.split_to(ind + 1), DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
}; };
@ -328,7 +334,7 @@ where
self.checked_buff = true; self.checked_buff = true;
} }
// poll req for more bytes // poll req for more bytes
match self.req.poll() { match self.stream.poll() {
Ok(Async::Ready(Some(mut bytes))) => { Ok(Async::Ready(Some(mut bytes))) => {
// check if there is a newline in bytes // check if there is a newline in bytes
let mut found: Option<usize> = None; let mut found: Option<usize> = None;
@ -343,13 +349,13 @@ where
if ind + 1 > self.limit { if ind + 1 > self.limit {
return Err(ReadlinesError::LimitOverflow); return Err(ReadlinesError::LimitOverflow);
} }
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&bytes.split_to(ind + 1)) str::from_utf8(&bytes.split_to(ind + 1))
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding self.encoding
.decode(&bytes.split_to(ind + 1), DecoderTrap::Strict) .decode(&bytes.split_to(ind + 1), DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
}; };
@ -369,13 +375,13 @@ where
if self.buff.len() > self.limit { if self.buff.len() > self.limit {
return Err(ReadlinesError::LimitOverflow); return Err(ReadlinesError::LimitOverflow);
} }
let enc: *const Encoding = encoding as *const Encoding; let enc: *const Encoding = self.encoding as *const Encoding;
let line = if enc == UTF_8 { let line = if enc == UTF_8 {
str::from_utf8(&self.buff) str::from_utf8(&self.buff)
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
.to_owned() .to_owned()
} else { } else {
encoding self.encoding
.decode(&self.buff, DecoderTrap::Strict) .decode(&self.buff, DecoderTrap::Strict)
.map_err(|_| ReadlinesError::EncodingError)? .map_err(|_| ReadlinesError::EncodingError)?
}; };
@ -388,19 +394,36 @@ where
} }
/// Future that resolves to a complete http message body. /// Future that resolves to a complete http message body.
pub struct MessageBody<T> { pub struct MessageBody<T: HttpMessage> {
limit: usize, limit: usize,
req: Option<T>, length: Option<usize>,
stream: Option<T::Stream>,
err: Option<PayloadError>,
fut: Option<Box<Future<Item = Bytes, Error = PayloadError>>>, fut: Option<Box<Future<Item = Bytes, Error = PayloadError>>>,
} }
impl<T> MessageBody<T> { impl<T: HttpMessage> MessageBody<T> {
/// Create `RequestBody` for request. /// Create `MessageBody` for request.
pub fn new(req: T) -> MessageBody<T> { pub fn new(req: &T) -> MessageBody<T> {
let mut len = None;
if let Some(l) = req.headers().get(header::CONTENT_LENGTH) {
if let Ok(s) = l.to_str() {
if let Ok(l) = s.parse::<usize>() {
len = Some(l)
} else {
return Self::err(PayloadError::UnknownLength);
}
} else {
return Self::err(PayloadError::UnknownLength);
}
}
MessageBody { MessageBody {
limit: 262_144, limit: 262_144,
req: Some(req), length: len,
stream: Some(req.payload()),
fut: None, fut: None,
err: None,
} }
} }
@ -409,68 +432,114 @@ impl<T> MessageBody<T> {
self.limit = limit; self.limit = limit;
self self
} }
fn err(e: PayloadError) -> Self {
MessageBody {
stream: None,
limit: 262_144,
fut: None,
err: Some(e),
length: None,
}
}
} }
impl<T> Future for MessageBody<T> impl<T> Future for MessageBody<T>
where where
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static, T: HttpMessage + 'static,
{ {
type Item = Bytes; type Item = Bytes;
type Error = PayloadError; type Error = PayloadError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(req) = self.req.take() { if let Some(ref mut fut) = self.fut {
if let Some(len) = req.headers().get(header::CONTENT_LENGTH) { return fut.poll();
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<usize>() {
if len > self.limit {
return Err(PayloadError::Overflow);
}
} else {
return Err(PayloadError::UnknownLength);
}
} else {
return Err(PayloadError::UnknownLength);
}
}
// future
let limit = self.limit;
self.fut = Some(Box::new(
req.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(PayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.map(|body| body.freeze()),
));
} }
self.fut if let Some(err) = self.err.take() {
.as_mut() return Err(err);
.expect("UrlEncoded could not be used second time") }
.poll()
if let Some(len) = self.length.take() {
if len > self.limit {
return Err(PayloadError::Overflow);
}
}
// future
let limit = self.limit;
self.fut = Some(Box::new(
self.stream
.take()
.expect("Can not be used second time")
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(PayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.map(|body| body.freeze()),
));
self.poll()
} }
} }
/// Future that resolves to a parsed urlencoded values. /// Future that resolves to a parsed urlencoded values.
pub struct UrlEncoded<T, U> { pub struct UrlEncoded<T: HttpMessage, U> {
req: Option<T>, stream: Option<T::Stream>,
limit: usize, limit: usize,
length: Option<usize>,
encoding: EncodingRef,
err: Option<UrlencodedError>,
fut: Option<Box<Future<Item = U, Error = UrlencodedError>>>, fut: Option<Box<Future<Item = U, Error = UrlencodedError>>>,
} }
impl<T, U> UrlEncoded<T, U> { impl<T: HttpMessage, U> UrlEncoded<T, U> {
/// Create a new future to URL encode a request /// Create a new future to URL encode a request
pub fn new(req: T) -> UrlEncoded<T, U> { pub fn new(req: &T) -> UrlEncoded<T, U> {
// check content type
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
return Self::err(UrlencodedError::ContentType);
}
let encoding = match req.encoding() {
Ok(enc) => enc,
Err(_) => return Self::err(UrlencodedError::ContentType),
};
let mut len = None;
if let Some(l) = req.headers().get(header::CONTENT_LENGTH) {
if let Ok(s) = l.to_str() {
if let Ok(l) = s.parse::<usize>() {
len = Some(l)
} else {
return Self::err(UrlencodedError::UnknownLength);
}
} else {
return Self::err(UrlencodedError::UnknownLength);
}
};
UrlEncoded { UrlEncoded {
req: Some(req), encoding,
stream: Some(req.payload()),
limit: 262_144,
length: len,
fut: None,
err: None,
}
}
fn err(e: UrlencodedError) -> Self {
UrlEncoded {
stream: None,
limit: 262_144, limit: 262_144,
fut: None, fut: None,
err: Some(e),
length: None,
encoding: UTF_8,
} }
} }
@ -483,66 +552,58 @@ impl<T, U> UrlEncoded<T, U> {
impl<T, U> Future for UrlEncoded<T, U> impl<T, U> Future for UrlEncoded<T, U>
where where
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static, T: HttpMessage + 'static,
U: DeserializeOwned + 'static, U: DeserializeOwned + 'static,
{ {
type Item = U; type Item = U;
type Error = UrlencodedError; type Error = UrlencodedError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(req) = self.req.take() { if let Some(ref mut fut) = self.fut {
if let Some(len) = req.headers().get(header::CONTENT_LENGTH) { return fut.poll();
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<u64>() {
if len > 262_144 {
return Err(UrlencodedError::Overflow);
}
} else {
return Err(UrlencodedError::UnknownLength);
}
} else {
return Err(UrlencodedError::UnknownLength);
}
}
// check content type
if req.content_type().to_lowercase() != "application/x-www-form-urlencoded" {
return Err(UrlencodedError::ContentType);
}
let encoding = req.encoding().map_err(|_| UrlencodedError::ContentType)?;
// future
let limit = self.limit;
let fut = req
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(UrlencodedError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(move |body| {
let enc: *const Encoding = encoding as *const Encoding;
if enc == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
} else {
let body = encoding
.decode(&body, DecoderTrap::Strict)
.map_err(|_| UrlencodedError::Parse)?;
serde_urlencoded::from_str::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
}
});
self.fut = Some(Box::new(fut));
} }
self.fut if let Some(err) = self.err.take() {
.as_mut() return Err(err);
}
// payload size
let limit = self.limit;
if let Some(len) = self.length.take() {
if len > limit {
return Err(UrlencodedError::Overflow);
}
}
// future
let encoding = self.encoding;
let fut = self
.stream
.take()
.expect("UrlEncoded could not be used second time") .expect("UrlEncoded could not be used second time")
.poll() .from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(UrlencodedError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(move |body| {
if (encoding as *const Encoding) == UTF_8 {
serde_urlencoded::from_bytes::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
} else {
let body = encoding
.decode(&body, DecoderTrap::Strict)
.map_err(|_| UrlencodedError::Parse)?;
serde_urlencoded::from_str::<U>(&body)
.map_err(|_| UrlencodedError::Parse)
}
});
self.fut = Some(Box::new(fut));
self.poll()
} }
} }
@ -566,7 +627,7 @@ mod tests {
TestRequest::with_header("content-type", "application/json; charset=utf=8") TestRequest::with_header("content-type", "application/json; charset=utf=8")
.finish(); .finish();
assert_eq!(req.content_type(), "application/json"); assert_eq!(req.content_type(), "application/json");
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert_eq!(req.content_type(), ""); assert_eq!(req.content_type(), "");
} }
@ -574,7 +635,7 @@ mod tests {
fn test_mime_type() { fn test_mime_type() {
let req = TestRequest::with_header("content-type", "application/json").finish(); let req = TestRequest::with_header("content-type", "application/json").finish();
assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON)); assert_eq!(req.mime_type().unwrap(), Some(mime::APPLICATION_JSON));
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert_eq!(req.mime_type().unwrap(), None); assert_eq!(req.mime_type().unwrap(), None);
let req = let req =
TestRequest::with_header("content-type", "application/json; charset=utf-8") TestRequest::with_header("content-type", "application/json; charset=utf-8")
@ -596,7 +657,7 @@ mod tests {
#[test] #[test]
fn test_encoding() { fn test_encoding() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert_eq!(UTF_8.name(), req.encoding().unwrap().name()); assert_eq!(UTF_8.name(), req.encoding().unwrap().name());
let req = TestRequest::with_header("content-type", "application/json").finish(); let req = TestRequest::with_header("content-type", "application/json").finish();
@ -626,27 +687,19 @@ mod tests {
#[test] #[test]
fn test_chunked() { fn test_chunked() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert!(!req.chunked().unwrap()); assert!(!req.chunked().unwrap());
let req = let req =
TestRequest::with_header(header::TRANSFER_ENCODING, "chunked").finish(); TestRequest::with_header(header::TRANSFER_ENCODING, "chunked").finish();
assert!(req.chunked().unwrap()); assert!(req.chunked().unwrap());
let mut headers = HeaderMap::new(); let req = TestRequest::default()
let hdr = Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"); .header(
header::TRANSFER_ENCODING,
headers.insert( Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"),
header::TRANSFER_ENCODING, )
header::HeaderValue::from_shared(hdr).unwrap(), .finish();
);
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert!(req.chunked().is_err()); assert!(req.chunked().is_err());
} }
@ -716,9 +769,8 @@ mod tests {
header::CONTENT_TYPE, header::CONTENT_TYPE,
"application/x-www-form-urlencoded", "application/x-www-form-urlencoded",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world"))
.finish(); .finish();
req.payload_mut()
.unread_data(Bytes::from_static(b"hello=world"));
let result = req.urlencoded::<Info>().poll().ok().unwrap(); let result = req.urlencoded::<Info>().poll().ok().unwrap();
assert_eq!( assert_eq!(
@ -732,9 +784,8 @@ mod tests {
header::CONTENT_TYPE, header::CONTENT_TYPE,
"application/x-www-form-urlencoded; charset=utf-8", "application/x-www-form-urlencoded; charset=utf-8",
).header(header::CONTENT_LENGTH, "11") ).header(header::CONTENT_LENGTH, "11")
.set_payload(Bytes::from_static(b"hello=world"))
.finish(); .finish();
req.payload_mut()
.unread_data(Bytes::from_static(b"hello=world"));
let result = req.urlencoded().poll().ok().unwrap(); let result = req.urlencoded().poll().ok().unwrap();
assert_eq!( assert_eq!(
@ -759,16 +810,17 @@ mod tests {
_ => unreachable!("error"), _ => unreachable!("error"),
} }
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.payload_mut().unread_data(Bytes::from_static(b"test")); .set_payload(Bytes::from_static(b"test"))
.finish();
match req.body().poll().ok().unwrap() { match req.body().poll().ok().unwrap() {
Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")), Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")),
_ => unreachable!("error"), _ => unreachable!("error"),
} }
let mut req = HttpRequest::default(); let mut req = TestRequest::default()
req.payload_mut() .set_payload(Bytes::from_static(b"11111111111111"))
.unread_data(Bytes::from_static(b"11111111111111")); .finish();
match req.body().limit(5).poll().err().unwrap() { match req.body().limit(5).poll().err().unwrap() {
PayloadError::Overflow => (), PayloadError::Overflow => (),
_ => unreachable!("error"), _ => unreachable!("error"),
@ -777,13 +829,14 @@ mod tests {
#[test] #[test]
fn test_readlines() { fn test_readlines() {
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.payload_mut().unread_data(Bytes::from_static( .set_payload(Bytes::from_static(
b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\
industry. Lorem Ipsum has been the industry's standard dummy\n\ industry. Lorem Ipsum has been the industry's standard dummy\n\
Contrary to popular belief, Lorem Ipsum is not simply random text.", Contrary to popular belief, Lorem Ipsum is not simply random text.",
)); ))
let mut r = Readlines::new(req); .finish();
let mut r = Readlines::new(&req);
match r.poll().ok().unwrap() { match r.poll().ok().unwrap() {
Async::Ready(Some(s)) => assert_eq!( Async::Ready(Some(s)) => assert_eq!(
s, s,

View file

@ -1,6 +1,8 @@
//! HTTP Request message related code. //! HTTP Request message related code.
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::HashMap; use std::collections::HashMap;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
use std::{cmp, fmt, io, str}; use std::{cmp, fmt, io, str};
@ -22,261 +24,158 @@ use httpresponse::{HttpResponse, HttpResponseBuilder};
use info::ConnectionInfo; use info::ConnectionInfo;
use param::Params; use param::Params;
use payload::Payload; use payload::Payload;
use router::{Resource, Router}; use router::{Resource, RouteInfo, Router};
use server::helpers::SharedHttpInnerMessage; use server::message::{MessageFlags, Request};
use uri::Url as InnerUrl; use uri::Url as InnerUrl;
bitflags! {
pub(crate) struct MessageFlags: u8 {
const KEEPALIVE = 0b0000_0010;
}
}
pub struct HttpInnerMessage {
pub version: Version,
pub method: Method,
pub(crate) url: InnerUrl,
pub(crate) flags: MessageFlags,
pub headers: HeaderMap,
pub extensions: Extensions,
pub params: Params,
pub addr: Option<SocketAddr>,
pub payload: Option<Payload>,
pub prefix: u16,
resource: RouterResource,
}
struct Query(HashMap<String, String>); struct Query(HashMap<String, String>);
struct Cookies(Vec<Cookie<'static>>); struct Cookies(Vec<Cookie<'static>>);
struct Info(ConnectionInfo);
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
enum RouterResource { pub(crate) enum RouterResource {
Notset, Notset,
Normal(u16), Normal(u16),
} }
impl Default for HttpInnerMessage { /// An HTTP Request
fn default() -> HttpInnerMessage { pub struct HttpRequest<S = ()> {
HttpInnerMessage { req: Rc<Request>,
method: Method::GET, state: Rc<S>,
url: InnerUrl::default(), route: RouteInfo,
version: Version::HTTP_11, }
headers: HeaderMap::with_capacity(16),
flags: MessageFlags::empty(), impl<S> HttpMessage for HttpRequest<S> {
params: Params::new(), type Stream = Payload;
addr: None,
payload: None, #[inline]
extensions: Extensions::new(), fn headers(&self) -> &HeaderMap {
prefix: 0, self.req.headers()
resource: RouterResource::Notset, }
#[inline]
fn payload(&self) -> Payload {
if let Some(payload) = self.req.inner.payload.borrow_mut().take() {
payload
} else {
Payload::empty()
} }
} }
} }
impl HttpInnerMessage { impl<S> Deref for HttpRequest<S> {
/// Checks if a connection should be kept alive. type Target = Request;
#[inline]
pub fn keep_alive(&self) -> bool {
self.flags.contains(MessageFlags::KEEPALIVE)
}
#[inline] fn deref(&self) -> &Request {
pub(crate) fn reset(&mut self) { self.req.as_ref()
self.headers.clear();
self.extensions.clear();
self.params.clear();
self.addr = None;
self.flags = MessageFlags::empty();
self.payload = None;
self.prefix = 0;
self.resource = RouterResource::Notset;
}
}
/// An HTTP Request
pub struct HttpRequest<S = ()>(SharedHttpInnerMessage, Option<Rc<S>>, Option<Router>);
impl HttpRequest<()> {
/// Construct a new Request.
#[inline]
pub(crate) fn new(
method: Method, uri: Uri, version: Version, headers: HeaderMap,
payload: Option<Payload>,
) -> HttpRequest {
let url = InnerUrl::new(uri);
HttpRequest(
SharedHttpInnerMessage::from_message(HttpInnerMessage {
method,
url,
version,
headers,
payload,
params: Params::new(),
extensions: Extensions::new(),
addr: None,
prefix: 0,
flags: MessageFlags::empty(),
resource: RouterResource::Notset,
}),
None,
None,
)
}
#[inline(always)]
#[cfg_attr(feature = "cargo-clippy", allow(inline_always))]
pub(crate) fn from_message(msg: SharedHttpInnerMessage) -> HttpRequest {
HttpRequest(msg, None, None)
}
#[inline]
/// Construct new http request with state.
pub fn with_state<S>(self, state: Rc<S>, router: Router) -> HttpRequest<S> {
HttpRequest(self.0, Some(state), Some(router))
}
pub(crate) fn clone_with_state<S>(
&self, state: Rc<S>, router: Router,
) -> HttpRequest<S> {
HttpRequest(self.0.clone(), Some(state), Some(router))
}
}
impl<S> HttpMessage for HttpRequest<S> {
#[inline]
fn headers(&self) -> &HeaderMap {
&self.as_ref().headers
} }
} }
impl<S> HttpRequest<S> { impl<S> HttpRequest<S> {
#[inline]
pub(crate) fn new(req: Request, state: Rc<S>, route: RouteInfo) -> HttpRequest<S> {
HttpRequest {
state,
route,
req: Rc::new(req),
}
}
#[inline] #[inline]
/// Construct new http request with state. /// Construct new http request with state.
pub fn change_state<NS>(&self, state: Rc<NS>) -> HttpRequest<NS> { pub(crate) fn with_state<NS>(&self, state: Rc<NS>) -> HttpRequest<NS> {
HttpRequest(self.0.clone(), Some(state), self.2.clone()) HttpRequest {
state,
req: self.req.clone(),
route: self.route.clone(),
}
} }
#[inline] #[inline]
/// Construct new http request without state. /// Construct new http request with new RouteInfo.
pub fn drop_state(&self) -> HttpRequest { pub(crate) fn with_route_info(&self, route: RouteInfo) -> HttpRequest<S> {
HttpRequest(self.0.clone(), None, self.2.clone()) HttpRequest {
} route,
req: self.req.clone(),
/// get mutable reference for inner message state: self.state.clone(),
/// mutable reference should not be returned as result for request's method }
#[inline]
pub(crate) fn as_mut(&mut self) -> &mut HttpInnerMessage {
self.0.get_mut()
}
#[inline]
fn as_ref(&self) -> &HttpInnerMessage {
self.0.get_ref()
} }
/// Shared application state /// Shared application state
#[inline] #[inline]
pub fn state(&self) -> &S { pub fn state(&self) -> &S {
self.1.as_ref().unwrap() &self.state
}
#[inline]
/// Server request
pub fn request(&self) -> &Request {
&self.req
} }
/// Request extensions /// Request extensions
#[inline] #[inline]
pub fn extensions(&self) -> &Extensions { pub fn extensions(&self) -> Ref<Extensions> {
&self.as_ref().extensions self.req.extensions()
} }
/// Mutable reference to a the request's extensions /// Mutable reference to a the request's extensions
#[inline] #[inline]
pub fn extensions_mut(&mut self) -> &mut Extensions { pub fn extensions_mut(&self) -> RefMut<Extensions> {
&mut self.as_mut().extensions self.req.extensions_mut()
} }
/// Default `CpuPool` /// Default `CpuPool`
#[inline] #[inline]
#[doc(hidden)] #[doc(hidden)]
pub fn cpu_pool(&self) -> &CpuPool { pub fn cpu_pool(&self) -> &CpuPool {
self.router() self.req.server_settings().cpu_pool()
.expect("HttpRequest has to have Router instance")
.server_settings()
.cpu_pool()
} }
#[inline]
/// Create http response /// Create http response
pub fn response(&self, status: StatusCode, body: Body) -> HttpResponse { pub fn response(&self, status: StatusCode, body: Body) -> HttpResponse {
if let Some(router) = self.router() { self.req.server_settings().get_response(status, body)
router.server_settings().get_response(status, body)
} else {
HttpResponse::with_body(status, body)
}
} }
#[inline]
/// Create http response builder /// Create http response builder
pub fn build_response(&self, status: StatusCode) -> HttpResponseBuilder { pub fn build_response(&self, status: StatusCode) -> HttpResponseBuilder {
if let Some(router) = self.router() { self.req.server_settings().get_response_builder(status)
router.server_settings().get_response_builder(status)
} else {
HttpResponse::build(status)
}
}
#[doc(hidden)]
pub fn prefix_len(&self) -> u16 {
self.as_ref().prefix as u16
}
#[doc(hidden)]
pub fn set_prefix_len(&mut self, len: u16) {
self.as_mut().prefix = len;
} }
/// Read the Request Uri. /// Read the Request Uri.
#[inline] #[inline]
pub fn uri(&self) -> &Uri { pub fn uri(&self) -> &Uri {
self.as_ref().url.uri() self.request().inner.url.uri()
} }
/// Read the Request method. /// Read the Request method.
#[inline] #[inline]
pub fn method(&self) -> &Method { pub fn method(&self) -> &Method {
&self.as_ref().method &self.request().inner.method
} }
/// Read the Request Version. /// Read the Request Version.
#[inline] #[inline]
pub fn version(&self) -> Version { pub fn version(&self) -> Version {
self.as_ref().version self.request().inner.version
}
///Returns mutable Request's headers.
///
///This is intended to be used by middleware.
#[cfg(test)]
pub(crate) fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.as_mut().headers
} }
/// The target path of this Request. /// The target path of this Request.
#[inline] #[inline]
pub fn path(&self) -> &str { pub fn path(&self) -> &str {
self.as_ref().url.path() self.request().inner.url.path()
} }
#[inline] #[inline]
pub(crate) fn url(&self) -> &InnerUrl { pub(crate) fn url(&self) -> &InnerUrl {
&self.as_ref().url &self.request().inner.url
} }
/// Get *ConnectionInfo* for correct request. /// Get *ConnectionInfo* for the correct request.
pub fn connection_info(&self) -> &ConnectionInfo { #[inline]
if self.extensions().get::<Info>().is_none() { pub fn connection_info(&self) -> Ref<ConnectionInfo> {
let mut req = self.clone(); self.request().connection_info()
req.as_mut()
.extensions
.insert(Info(ConnectionInfo::new(self)));
}
&self.extensions().get::<Info>().unwrap().0
} }
/// Generate url for named resource /// Generate url for named resource
@ -306,22 +205,7 @@ impl<S> HttpRequest<S> {
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
I: AsRef<str>, I: AsRef<str>,
{ {
if self.router().is_none() { self.route.url_for(&self, name, elements)
Err(UrlGenerationError::RouterNotAvailable)
} else {
let path = self.router().unwrap().resource_path(name, elements)?;
if path.starts_with('/') {
let conn = self.connection_info();
Ok(Url::parse(&format!(
"{}://{}{}",
conn.scheme(),
conn.host(),
path
))?)
} else {
Ok(Url::parse(&path)?)
}
}
} }
/// Generate url for named resource /// Generate url for named resource
@ -333,25 +217,16 @@ impl<S> HttpRequest<S> {
self.url_for(name, &NO_PARAMS) self.url_for(name, &NO_PARAMS)
} }
/// This method returns reference to current `Router` object. /// This method returns reference to current `RouteInfo` object.
#[inline] #[inline]
pub fn router(&self) -> Option<&Router> { pub fn route(&self) -> &RouteInfo {
self.2.as_ref() &self.route
} }
/// This method returns reference to matched `Resource` object. /// This method returns reference to matched `Resource` object.
#[inline] #[inline]
pub fn resource(&self) -> Option<&Resource> { pub fn resource(&self) -> Option<&Resource> {
if let Some(ref router) = self.2 { self.route.resource()
if let RouterResource::Normal(idx) = self.as_ref().resource {
return Some(router.get_resource(idx as usize));
}
}
None
}
pub(crate) fn set_resource(&mut self, res: usize) {
self.as_mut().resource = RouterResource::Normal(res as u16);
} }
/// Peer socket address /// Peer socket address
@ -363,25 +238,20 @@ impl<S> HttpRequest<S> {
/// be used. /// be used.
#[inline] #[inline]
pub fn peer_addr(&self) -> Option<SocketAddr> { pub fn peer_addr(&self) -> Option<SocketAddr> {
self.as_ref().addr self.request().inner.addr
}
#[inline]
pub(crate) fn set_peer_addr(&mut self, addr: Option<SocketAddr>) {
self.as_mut().addr = addr;
} }
/// url query parameters. /// url query parameters.
pub fn query(&self) -> &HashMap<String, String> { pub fn query(&self) -> Ref<HashMap<String, String>> {
if self.extensions().get::<Query>().is_none() { if self.extensions().get::<Query>().is_none() {
let mut query = HashMap::new(); let mut query = HashMap::new();
for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) { for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) {
query.insert(key.as_ref().to_string(), val.to_string()); query.insert(key.as_ref().to_string(), val.to_string());
} }
let mut req = self.clone(); let mut req = self.clone();
req.as_mut().extensions.insert(Query(query)); self.extensions_mut().insert(Query(query));
} }
&self.extensions().get::<Query>().unwrap().0 Ref::map(self.extensions(), |ext| &ext.get::<Query>().unwrap().0)
} }
/// The query string in the URL. /// The query string in the URL.
@ -397,12 +267,12 @@ impl<S> HttpRequest<S> {
} }
/// Load request cookies. /// Load request cookies.
pub fn cookies(&self) -> Result<&Vec<Cookie<'static>>, CookieParseError> { #[inline]
if self.extensions().get::<Query>().is_none() { pub fn cookies(&self) -> Result<Ref<Vec<Cookie<'static>>>, CookieParseError> {
if self.extensions().get::<Cookies>().is_none() {
let mut req = self.clone(); let mut req = self.clone();
let msg = req.as_mut();
let mut cookies = Vec::new(); let mut cookies = Vec::new();
for hdr in msg.headers.get_all(header::COOKIE) { for hdr in self.request().inner.headers.get_all(header::COOKIE) {
let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?; let s = str::from_utf8(hdr.as_bytes()).map_err(CookieParseError::from)?;
for cookie_str in s.split(';').map(|s| s.trim()) { for cookie_str in s.split(';').map(|s| s.trim()) {
if !cookie_str.is_empty() { if !cookie_str.is_empty() {
@ -410,17 +280,20 @@ impl<S> HttpRequest<S> {
} }
} }
} }
msg.extensions.insert(Cookies(cookies)); self.extensions_mut().insert(Cookies(cookies));
} }
Ok(&self.extensions().get::<Cookies>().unwrap().0) Ok(Ref::map(self.extensions(), |ext| {
&ext.get::<Cookies>().unwrap().0
}))
} }
/// Return request cookie. /// Return request cookie.
pub fn cookie(&self, name: &str) -> Option<&Cookie> { #[inline]
pub fn cookie(&self, name: &str) -> Option<Cookie<'static>> {
if let Ok(cookies) = self.cookies() { if let Ok(cookies) = self.cookies() {
for cookie in cookies { for cookie in cookies.iter() {
if cookie.name() == name { if cookie.name() == name {
return Some(cookie); return Some(cookie.to_owned());
} }
} }
} }
@ -441,68 +314,31 @@ impl<S> HttpRequest<S> {
/// access the matched value for that segment. /// access the matched value for that segment.
#[inline] #[inline]
pub fn match_info(&self) -> &Params { pub fn match_info(&self) -> &Params {
&self.as_ref().params &self.route.match_info()
}
/// Get mutable reference to request's Params.
#[inline]
pub(crate) fn match_info_mut(&mut self) -> &mut Params {
&mut self.as_mut().params
}
/// Checks if a connection should be kept alive.
pub fn keep_alive(&self) -> bool {
self.as_ref().flags.contains(MessageFlags::KEEPALIVE)
} }
/// Check if request requires connection upgrade /// Check if request requires connection upgrade
pub(crate) fn upgrade(&self) -> bool { pub(crate) fn upgrade(&self) -> bool {
if let Some(conn) = self.as_ref().headers.get(header::CONNECTION) { self.request().upgrade()
if let Ok(s) = conn.to_str() {
return s.to_lowercase().contains("upgrade");
}
}
self.as_ref().method == Method::CONNECT
} }
/// Set read buffer capacity /// Set read buffer capacity
/// ///
/// Default buffer capacity is 32Kb. /// Default buffer capacity is 32Kb.
pub fn set_read_buffer_capacity(&mut self, cap: usize) { pub fn set_read_buffer_capacity(&mut self, cap: usize) {
if let Some(ref mut payload) = self.as_mut().payload { if let Some(payload) = self.request().inner.payload.borrow_mut().as_mut() {
payload.set_read_buffer_capacity(cap) payload.set_read_buffer_capacity(cap)
} }
} }
#[cfg(test)]
pub(crate) fn payload(&mut self) -> &Payload {
let msg = self.as_mut();
if msg.payload.is_none() {
msg.payload = Some(Payload::empty());
}
msg.payload.as_ref().unwrap()
}
#[cfg(test)]
pub(crate) fn payload_mut(&mut self) -> &mut Payload {
let msg = self.as_mut();
if msg.payload.is_none() {
msg.payload = Some(Payload::empty());
}
msg.payload.as_mut().unwrap()
}
}
impl Default for HttpRequest<()> {
/// Construct default request
fn default() -> HttpRequest {
HttpRequest(SharedHttpInnerMessage::default(), None, None)
}
} }
impl<S> Clone for HttpRequest<S> { impl<S> Clone for HttpRequest<S> {
fn clone(&self) -> HttpRequest<S> { fn clone(&self) -> HttpRequest<S> {
HttpRequest(self.0.clone(), self.1.clone(), self.2.clone()) HttpRequest {
req: self.req.clone(),
state: self.state.clone(),
route: self.route.clone(),
}
} }
} }
@ -516,76 +352,23 @@ impl<S> FromRequest<S> for HttpRequest<S> {
} }
} }
impl<S> Stream for HttpRequest<S> {
type Item = Bytes;
type Error = PayloadError;
fn poll(&mut self) -> Poll<Option<Bytes>, PayloadError> {
let msg = self.as_mut();
if msg.payload.is_none() {
Ok(Async::Ready(None))
} else {
msg.payload.as_mut().unwrap().poll()
}
}
}
impl<S> io::Read for HttpRequest<S> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.as_mut().payload.is_some() {
match self.as_mut().payload.as_mut().unwrap().poll() {
Ok(Async::Ready(Some(mut b))) => {
let i = cmp::min(b.len(), buf.len());
buf.copy_from_slice(&b.split_to(i)[..i]);
if !b.is_empty() {
self.as_mut().payload.as_mut().unwrap().unread_data(b);
}
if i < buf.len() {
match self.read(&mut buf[i..]) {
Ok(n) => Ok(i + n),
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => Ok(i),
Err(e) => Err(e),
}
} else {
Ok(i)
}
}
Ok(Async::Ready(None)) => Ok(0),
Ok(Async::NotReady) => {
Err(io::Error::new(io::ErrorKind::WouldBlock, "Not ready"))
}
Err(e) => Err(io::Error::new(
io::ErrorKind::Other,
failure::Error::from(e).compat(),
)),
}
} else {
Ok(0)
}
}
}
impl<S> AsyncRead for HttpRequest<S> {}
impl<S> fmt::Debug for HttpRequest<S> { impl<S> fmt::Debug for HttpRequest<S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let res = writeln!( let res = writeln!(
f, f,
"\nHttpRequest {:?} {}:{}", "\nHttpRequest {:?} {}:{}",
self.as_ref().version, self.version(),
self.as_ref().method, self.method(),
self.path() self.path()
); );
if !self.query_string().is_empty() { if !self.query_string().is_empty() {
let _ = writeln!(f, " query: ?{:?}", self.query_string()); let _ = writeln!(f, " query: ?{:?}", self.query_string());
} }
if !self.match_info().is_empty() { if !self.match_info().is_empty() {
let _ = writeln!(f, " params: {:?}", self.as_ref().params); let _ = writeln!(f, " params: {:?}", self.match_info());
} }
let _ = writeln!(f, " headers:"); let _ = writeln!(f, " headers:");
for (key, val) in self.as_ref().headers.iter() { for (key, val) in self.headers().iter() {
let _ = writeln!(f, " {:?}: {:?}", key, val); let _ = writeln!(f, " {:?}: {:?}", key, val);
} }
res res
@ -597,7 +380,6 @@ mod tests {
use super::*; use super::*;
use resource::ResourceHandler; use resource::ResourceHandler;
use router::Resource; use router::Resource;
use server::ServerSettings;
use test::TestRequest; use test::TestRequest;
#[test] #[test]
@ -609,7 +391,7 @@ mod tests {
#[test] #[test]
fn test_no_request_cookies() { fn test_no_request_cookies() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert!(req.cookies().unwrap().is_empty()); assert!(req.cookies().unwrap().is_empty());
} }
@ -648,33 +430,27 @@ mod tests {
#[test] #[test]
fn test_request_match_info() { fn test_request_match_info() {
let mut req = TestRequest::with_uri("/value/?id=test").finish();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let mut routes = Vec::new(); let mut routes = Vec::new();
routes.push((Resource::new("index", "/{key}/"), Some(resource))); routes.push((Resource::new("index", "/{key}/"), Some(resource)));
let (router, _) = Router::new("", ServerSettings::default(), routes); let (router, _) = Router::new("", routes);
assert!(router.recognize(&mut req).is_some());
assert_eq!(req.match_info().get("key"), Some("value")); let req = TestRequest::with_uri("/value/?id=test").finish();
let info = router.recognize(&req).unwrap().1;
assert_eq!(info.match_info().get("key"), Some("value"));
} }
#[test] #[test]
fn test_url_for() { fn test_url_for() {
let req2 = HttpRequest::default();
assert_eq!(
req2.url_for("unknown", &["test"]),
Err(UrlGenerationError::RouterNotAvailable)
);
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = let routes =
vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))]; vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))];
let (router, _) = Router::new("/", ServerSettings::default(), routes); let (router, _) = Router::new("/", routes);
assert!(router.has_route("/user/test.html")); let info = router.default_route_info(0);
assert!(!router.has_route("/test/unknown")); assert!(info.has_route("/user/test.html"));
assert!(!info.has_route("/test/unknown"));
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org") let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
.finish_with_router(router); .finish_with_router(router);
@ -696,16 +472,16 @@ mod tests {
#[test] #[test]
fn test_url_for_with_prefix() { fn test_url_for_with_prefix() {
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![(Resource::new("index", "/user/{name}.html"), Some(resource))]; let routes = vec![(Resource::new("index", "/user/{name}.html"), Some(resource))];
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); let (router, _) = Router::new("/prefix/", routes);
assert!(router.has_route("/user/test.html")); let info = router.default_route_info(0);
assert!(!router.has_route("/prefix/user/test.html")); assert!(info.has_route("/user/test.html"));
assert!(!info.has_route("/prefix/user/test.html"));
let req = req.with_state(Rc::new(()), router); let req = TestRequest::with_header(header::HOST, "www.rust-lang.org")
.finish_with_router(router);
let url = req.url_for("index", &["test"]); let url = req.url_for("index", &["test"]);
assert_eq!( assert_eq!(
url.ok().unwrap().as_str(), url.ok().unwrap().as_str(),
@ -715,16 +491,17 @@ mod tests {
#[test] #[test]
fn test_url_for_static() { fn test_url_for_static() {
let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![(Resource::new("index", "/index.html"), Some(resource))]; let routes = vec![(Resource::new("index", "/index.html"), Some(resource))];
let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); let (router, _) = Router::new("/prefix/", routes);
assert!(router.has_route("/index.html")); let info = router.default_route_info(0);
assert!(!router.has_route("/prefix/index.html")); assert!(info.has_route("/index.html"));
assert!(!info.has_route("/prefix/index.html"));
let req = req.with_state(Rc::new(()), router); let req = TestRequest::default()
.header(header::HOST, "www.rust-lang.org")
.finish_with_router(router);
let url = req.url_for_static("index"); let url = req.url_for_static("index");
assert_eq!( assert_eq!(
url.ok().unwrap().as_str(), url.ok().unwrap().as_str(),
@ -734,18 +511,17 @@ mod tests {
#[test] #[test]
fn test_url_for_external() { fn test_url_for_external() {
let req = HttpRequest::default();
let mut resource = ResourceHandler::<()>::default(); let mut resource = ResourceHandler::<()>::default();
resource.name("index"); resource.name("index");
let routes = vec![( let routes = vec![(
Resource::external("youtube", "https://youtube.com/watch/{video_id}"), Resource::external("youtube", "https://youtube.com/watch/{video_id}"),
None, None,
)]; )];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); let router = Router::new::<()>("", routes).0;
assert!(!router.has_route("https://youtube.com/watch/unknown")); let info = router.default_route_info(0);
assert!(!info.has_route("https://youtube.com/watch/unknown"));
let req = req.with_state(Rc::new(()), router); let req = TestRequest::default().finish_with_router(router);
let url = req.url_for("youtube", &["oHg5SJYRHA0"]); let url = req.url_for("youtube", &["oHg5SJYRHA0"]);
assert_eq!( assert_eq!(
url.ok().unwrap().as_str(), url.ok().unwrap().as_str(),

View file

@ -553,10 +553,10 @@ impl HttpResponseBuilder {
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::{http, HttpRequest, HttpResponse, Result}; /// use actix_web::{http, HttpRequest, HttpResponse, Result};
/// ///
/// fn index(req: HttpRequest) -> HttpResponse { /// fn index(req: &HttpRequest) -> HttpResponse {
/// let mut builder = HttpResponse::Ok(); /// let mut builder = HttpResponse::Ok();
/// ///
/// if let Some(cookie) = req.cookie("name") { /// if let Some(ref cookie) = req.cookie("name") {
/// builder.del_cookie(cookie); /// builder.del_cookie(cookie);
/// } /// }
/// ///
@ -860,13 +860,9 @@ impl<'a> From<&'a ClientResponse> for HttpResponseBuilder {
impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder { impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder { fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder {
if let Some(router) = req.router() { req.request()
router .server_settings()
.server_settings() .get_response_builder(StatusCode::OK)
.get_response_builder(StatusCode::OK)
} else {
HttpResponse::Ok()
}
} }
} }
@ -1050,6 +1046,8 @@ mod tests {
use std::str::FromStr; use std::str::FromStr;
use time::Duration; use time::Duration;
use test::TestRequest;
#[test] #[test]
fn test_debug() { fn test_debug() {
let resp = HttpResponse::Ok() let resp = HttpResponse::Ok()
@ -1062,17 +1060,10 @@ mod tests {
#[test] #[test]
fn test_response_cookies() { fn test_response_cookies() {
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert(COOKIE, HeaderValue::from_static("cookie1=value1")); .header(COOKIE, "cookie1=value1")
headers.insert(COOKIE, HeaderValue::from_static("cookie2=value2")); .header(COOKIE, "cookie2=value2")
.finish();
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let cookies = req.cookies().unwrap(); let cookies = req.cookies().unwrap();
let resp = HttpResponse::Ok() let resp = HttpResponse::Ok()
@ -1094,7 +1085,7 @@ mod tests {
.map(|v| v.to_str().unwrap().to_owned()) .map(|v| v.to_str().unwrap().to_owned())
.collect(); .collect();
val.sort(); val.sort();
assert!(val[0].starts_with("cookie2=; Max-Age=0;")); assert!(val[0].starts_with("cookie1=; Max-Age=0;"));
assert_eq!( assert_eq!(
val[1], val[1],
"name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400" "name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400"
@ -1208,7 +1199,7 @@ mod tests {
#[test] #[test]
fn test_into_response() { fn test_into_response() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
let resp: HttpResponse = "test".into(); let resp: HttpResponse = "test".into();
assert_eq!(resp.status(), StatusCode::OK); assert_eq!(resp.status(), StatusCode::OK);

View file

@ -1,14 +1,17 @@
use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use http::header::{self, HeaderName}; use http::header::{self, HeaderName};
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use server::Request;
const X_FORWARDED_FOR: &str = "X-FORWARDED-FOR"; const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for";
const X_FORWARDED_HOST: &str = "X-FORWARDED-HOST"; const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host";
const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO"; const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto";
/// `HttpRequest` connection information /// `HttpRequest` connection information
#[derive(Clone, Default)]
pub struct ConnectionInfo { pub struct ConnectionInfo {
scheme: String, scheme: String,
host: String, host: String,
@ -19,7 +22,7 @@ pub struct ConnectionInfo {
impl ConnectionInfo { impl ConnectionInfo {
/// Create *ConnectionInfo* instance for a request. /// Create *ConnectionInfo* instance for a request.
#[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
pub fn new<S>(req: &HttpRequest<S>) -> ConnectionInfo { pub fn update(&mut self, req: &Request) {
let mut host = None; let mut host = None;
let mut scheme = None; let mut scheme = None;
let mut remote = None; let mut remote = None;
@ -56,7 +59,7 @@ impl ConnectionInfo {
if scheme.is_none() { if scheme.is_none() {
if let Some(h) = req if let Some(h) = req
.headers() .headers()
.get(HeaderName::from_str(X_FORWARDED_PROTO).unwrap()) .get(HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
scheme = h.split(',').next().map(|v| v.trim()); scheme = h.split(',').next().map(|v| v.trim());
@ -64,12 +67,8 @@ impl ConnectionInfo {
} }
if scheme.is_none() { if scheme.is_none() {
scheme = req.uri().scheme_part().map(|a| a.as_str()); scheme = req.uri().scheme_part().map(|a| a.as_str());
if scheme.is_none() { if scheme.is_none() && req.server_settings().secure() {
if let Some(router) = req.router() { scheme = Some("https")
if router.server_settings().secure() {
scheme = Some("https")
}
}
} }
} }
} }
@ -78,7 +77,7 @@ impl ConnectionInfo {
if host.is_none() { if host.is_none() {
if let Some(h) = req if let Some(h) = req
.headers() .headers()
.get(HeaderName::from_str(X_FORWARDED_HOST).unwrap()) .get(HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
host = h.split(',').next().map(|v| v.trim()); host = h.split(',').next().map(|v| v.trim());
@ -91,9 +90,7 @@ impl ConnectionInfo {
if host.is_none() { if host.is_none() {
host = req.uri().authority_part().map(|a| a.as_str()); host = req.uri().authority_part().map(|a| a.as_str());
if host.is_none() { if host.is_none() {
if let Some(router) = req.router() { host = Some(req.server_settings().host());
host = Some(router.server_settings().host());
}
} }
} }
} }
@ -103,7 +100,7 @@ impl ConnectionInfo {
if remote.is_none() { if remote.is_none() {
if let Some(h) = req if let Some(h) = req
.headers() .headers()
.get(HeaderName::from_str(X_FORWARDED_FOR).unwrap()) .get(HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap())
{ {
if let Ok(h) = h.to_str() { if let Ok(h) = h.to_str() {
remote = h.split(',').next().map(|v| v.trim()); remote = h.split(',').next().map(|v| v.trim());
@ -115,12 +112,10 @@ impl ConnectionInfo {
} }
} }
ConnectionInfo { self.scheme = scheme.unwrap_or("http").to_owned();
scheme: scheme.unwrap_or("http").to_owned(), self.host = host.unwrap_or("localhost").to_owned();
host: host.unwrap_or("localhost").to_owned(), self.remote = remote.map(|s| s.to_owned());
remote: remote.map(|s| s.to_owned()), self.peer = peer;
peer,
}
} }
/// Scheme of the request. /// Scheme of the request.
@ -171,59 +166,59 @@ impl ConnectionInfo {
mod tests { mod tests {
use super::*; use super::*;
use http::header::HeaderValue; use http::header::HeaderValue;
use test::TestRequest;
#[test] #[test]
fn test_forwarded() { fn test_forwarded() {
let req = HttpRequest::default(); let req = TestRequest::default().request();
let info = ConnectionInfo::new(&req); let mut info = ConnectionInfo::default();
info.update(&req);
assert_eq!(info.scheme(), "http"); assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "localhost"); assert_eq!(info.host(), "localhost:8080");
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(
header::FORWARDED, header::FORWARDED,
HeaderValue::from_static(
"for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org", "for=192.0.2.60; proto=https; by=203.0.113.43; host=rust-lang.org",
), )
); .request();
let info = ConnectionInfo::new(&req); let mut info = ConnectionInfo::default();
info.update(&req);
assert_eq!(info.scheme(), "https"); assert_eq!(info.scheme(), "https");
assert_eq!(info.host(), "rust-lang.org"); assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), Some("192.0.2.60")); assert_eq!(info.remote(), Some("192.0.2.60"));
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut() .header(header::HOST, "rust-lang.org")
.insert(header::HOST, HeaderValue::from_static("rust-lang.org")); .request();
let info = ConnectionInfo::new(&req); let mut info = ConnectionInfo::default();
info.update(&req);
assert_eq!(info.scheme(), "http"); assert_eq!(info.scheme(), "http");
assert_eq!(info.host(), "rust-lang.org"); assert_eq!(info.host(), "rust-lang.org");
assert_eq!(info.remote(), None); assert_eq!(info.remote(), None);
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(X_FORWARDED_FOR, "192.0.2.60")
HeaderName::from_str(X_FORWARDED_FOR).unwrap(), .request();
HeaderValue::from_static("192.0.2.60"), let mut info = ConnectionInfo::default();
); info.update(&req);
let info = ConnectionInfo::new(&req);
assert_eq!(info.remote(), Some("192.0.2.60")); assert_eq!(info.remote(), Some("192.0.2.60"));
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(X_FORWARDED_HOST, "192.0.2.60")
HeaderName::from_str(X_FORWARDED_HOST).unwrap(), .request();
HeaderValue::from_static("192.0.2.60"), let mut info = ConnectionInfo::default();
); info.update(&req);
let info = ConnectionInfo::new(&req);
assert_eq!(info.host(), "192.0.2.60"); assert_eq!(info.host(), "192.0.2.60");
assert_eq!(info.remote(), None); assert_eq!(info.remote(), None);
let mut req = HttpRequest::default(); let mut req = TestRequest::default()
req.headers_mut().insert( .header(X_FORWARDED_PROTO, "https")
HeaderName::from_str(X_FORWARDED_PROTO).unwrap(), .request();
HeaderValue::from_static("https"), let mut info = ConnectionInfo::default();
); info.update(&req);
let info = ConnectionInfo::new(&req);
assert_eq!(info.scheme(), "https"); assert_eq!(info.scheme(), "https");
} }
} }

View file

@ -140,12 +140,12 @@ where
#[inline] #[inline]
fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result { fn from_request(req: &HttpRequest<S>, cfg: &Self::Config) -> Self::Result {
let req = req.clone(); let req2 = req.clone();
let err = Rc::clone(&cfg.ehandler); let err = Rc::clone(&cfg.ehandler);
Box::new( Box::new(
JsonBody::new(req.clone()) JsonBody::new(req)
.limit(cfg.limit) .limit(cfg.limit)
.map_err(move |e| (*err)(e, req)) .map_err(move |e| (*err)(e, &req2))
.map(Json), .map(Json),
) )
} }
@ -183,7 +183,7 @@ where
/// ``` /// ```
pub struct JsonConfig<S> { pub struct JsonConfig<S> {
limit: usize, limit: usize,
ehandler: Rc<Fn(JsonPayloadError, HttpRequest<S>) -> Error>, ehandler: Rc<Fn(JsonPayloadError, &HttpRequest<S>) -> Error>,
} }
impl<S> JsonConfig<S> { impl<S> JsonConfig<S> {
@ -196,7 +196,7 @@ impl<S> JsonConfig<S> {
/// Set custom error handler /// Set custom error handler
pub fn error_handler<F>(&mut self, f: F) -> &mut Self pub fn error_handler<F>(&mut self, f: F) -> &mut Self
where where
F: Fn(JsonPayloadError, HttpRequest<S>) -> Error + 'static, F: Fn(JsonPayloadError, &HttpRequest<S>) -> Error + 'static,
{ {
self.ehandler = Rc::new(f); self.ehandler = Rc::new(f);
self self
@ -243,19 +243,48 @@ impl<S> Default for JsonConfig<S> {
/// } /// }
/// # fn main() {} /// # fn main() {}
/// ``` /// ```
pub struct JsonBody<T, U: DeserializeOwned> { pub struct JsonBody<T: HttpMessage, U: DeserializeOwned> {
limit: usize, limit: usize,
req: Option<T>, length: Option<usize>,
stream: Option<T::Stream>,
err: Option<JsonPayloadError>,
fut: Option<Box<Future<Item = U, Error = JsonPayloadError>>>, fut: Option<Box<Future<Item = U, Error = JsonPayloadError>>>,
} }
impl<T, U: DeserializeOwned> JsonBody<T, U> { impl<T: HttpMessage, U: DeserializeOwned> JsonBody<T, U> {
/// Create `JsonBody` for request. /// Create `JsonBody` for request.
pub fn new(req: T) -> Self { pub fn new(req: &T) -> Self {
// check content-type
let json = if let Ok(Some(mime)) = req.mime_type() {
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
} else {
false
};
if !json {
return JsonBody {
limit: 262_144,
length: None,
stream: None,
fut: None,
err: Some(JsonPayloadError::ContentType),
};
}
let mut len = None;
if let Some(l) = req.headers().get(CONTENT_LENGTH) {
if let Ok(s) = l.to_str() {
if let Ok(l) = s.parse::<usize>() {
len = Some(l)
}
}
}
JsonBody { JsonBody {
limit: 262_144, limit: 262_144,
req: Some(req), length: len,
stream: Some(req.payload()),
fut: None, fut: None,
err: None,
} }
} }
@ -266,56 +295,42 @@ impl<T, U: DeserializeOwned> JsonBody<T, U> {
} }
} }
impl<T, U: DeserializeOwned + 'static> Future for JsonBody<T, U> impl<T: HttpMessage + 'static, U: DeserializeOwned + 'static> Future for JsonBody<T, U> {
where
T: HttpMessage + Stream<Item = Bytes, Error = PayloadError> + 'static,
{
type Item = U; type Item = U;
type Error = JsonPayloadError; type Error = JsonPayloadError;
fn poll(&mut self) -> Poll<U, JsonPayloadError> { fn poll(&mut self) -> Poll<U, JsonPayloadError> {
if let Some(req) = self.req.take() { if let Some(ref mut fut) = self.fut {
if let Some(len) = req.headers().get(CONTENT_LENGTH) { return fut.poll();
if let Ok(s) = len.to_str() {
if let Ok(len) = s.parse::<usize>() {
if len > self.limit {
return Err(JsonPayloadError::Overflow);
}
} else {
return Err(JsonPayloadError::Overflow);
}
}
}
// check content-type
let json = if let Ok(Some(mime)) = req.mime_type() {
mime.subtype() == mime::JSON || mime.suffix() == Some(mime::JSON)
} else {
false
};
if !json {
return Err(JsonPayloadError::ContentType);
}
let limit = self.limit;
let fut = req
.from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(JsonPayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
self.fut = Some(Box::new(fut));
} }
self.fut if let Some(err) = self.err.take() {
.as_mut() return Err(err);
}
let limit = self.limit;
if let Some(len) = self.length.take() {
if len > limit {
return Err(JsonPayloadError::Overflow);
}
}
let fut = self
.stream
.take()
.expect("JsonBody could not be used second time") .expect("JsonBody could not be used second time")
.poll() .from_err()
.fold(BytesMut::new(), move |mut body, chunk| {
if (body.len() + chunk.len()) > limit {
Err(JsonPayloadError::Overflow)
} else {
body.extend_from_slice(&chunk);
Ok(body)
}
})
.and_then(|body| Ok(serde_json::from_slice::<U>(&body)?));
self.fut = Some(Box::new(fut));
self.poll()
} }
} }
@ -327,6 +342,7 @@ mod tests {
use http::header; use http::header;
use handler::Handler; use handler::Handler;
use test::TestRequest;
use with::With; use with::With;
impl PartialEq for JsonPayloadError { impl PartialEq for JsonPayloadError {
@ -355,7 +371,7 @@ mod tests {
let json = Json(MyObject { let json = Json(MyObject {
name: "test".to_owned(), name: "test".to_owned(),
}); });
let resp = json.respond_to(&HttpRequest::default()).unwrap(); let resp = json.respond_to(&TestRequest::default().finish()).unwrap();
assert_eq!( assert_eq!(
resp.headers().get(header::CONTENT_TYPE).unwrap(), resp.headers().get(header::CONTENT_TYPE).unwrap(),
"application/json" "application/json"
@ -364,41 +380,44 @@ mod tests {
#[test] #[test]
fn test_json_body() { fn test_json_body() {
let req = HttpRequest::default(); let req = TestRequest::default().finish();
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/text"), header::HeaderValue::from_static("application/text"),
); )
.finish();
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType);
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); )
req.headers_mut().insert( .header(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("10000"), header::HeaderValue::from_static("10000"),
); )
.finish();
let mut json = req.json::<MyObject>().limit(100); let mut json = req.json::<MyObject>().limit(100);
assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow);
let mut req = HttpRequest::default(); let req = TestRequest::default()
req.headers_mut().insert( .header(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); )
req.headers_mut().insert( .header(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
); )
req.payload_mut() .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}")); .finish();
let mut json = req.json::<MyObject>(); let mut json = req.json::<MyObject>();
assert_eq!( assert_eq!(
json.poll().ok().unwrap(), json.poll().ok().unwrap(),
@ -414,20 +433,18 @@ mod tests {
cfg.limit(4096); cfg.limit(4096);
let handler = With::new(|data: Json<MyObject>| data, cfg); let handler = With::new(|data: Json<MyObject>| data, cfg);
let req = HttpRequest::default(); let req = TestRequest::default().finish();
assert!(handler.handle(req).as_err().is_some()); assert!(handler.handle(&req).as_err().is_some());
let mut req = HttpRequest::default(); let req = TestRequest::with_header(
req.headers_mut().insert(
header::CONTENT_TYPE, header::CONTENT_TYPE,
header::HeaderValue::from_static("application/json"), header::HeaderValue::from_static("application/json"),
); ).header(
req.headers_mut().insert(
header::CONTENT_LENGTH, header::CONTENT_LENGTH,
header::HeaderValue::from_static("16"), header::HeaderValue::from_static("16"),
); )
req.payload_mut() .set_payload(Bytes::from_static(b"{\"name\": \"test\"}"))
.unread_data(Bytes::from_static(b"{\"name\": \"test\"}")); .finish();
assert!(handler.handle(req).as_err().is_none()) assert!(handler.handle(&req).as_err().is_none())
} }
} }

View file

@ -84,6 +84,7 @@
allow(decimal_literal_representation, suspicious_arithmetic_impl) allow(decimal_literal_representation, suspicious_arithmetic_impl)
)] )]
#![warn(missing_docs)] #![warn(missing_docs)]
#![allow(unused_mut, unused_imports, unused_variables, dead_code)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
@ -199,6 +200,7 @@ pub use httprequest::HttpRequest;
pub use httpresponse::HttpResponse; pub use httpresponse::HttpResponse;
pub use json::Json; pub use json::Json;
pub use scope::Scope; pub use scope::Scope;
pub use server::Request;
pub mod actix { pub mod actix {
//! Re-exports [actix's](https://docs.rs/actix/) prelude //! Re-exports [actix's](https://docs.rs/actix/) prelude

View file

@ -60,6 +60,7 @@ use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Middleware, Response, Started}; use middleware::{Middleware, Response, Started};
use resource::ResourceHandler; use resource::ResourceHandler;
use server::Request;
/// A set of errors that can occur during processing CORS /// A set of errors that can occur during processing CORS
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
@ -279,11 +280,13 @@ impl Cors {
/// `ResourceHandler::middleware()` method, but in that case *Cors* /// `ResourceHandler::middleware()` method, but in that case *Cors*
/// middleware wont be able to handle *OPTIONS* requests. /// middleware wont be able to handle *OPTIONS* requests.
pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) { pub fn register<S: 'static>(self, resource: &mut ResourceHandler<S>) {
resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok()); resource
.method(Method::OPTIONS)
.h(|_: &_| HttpResponse::Ok());
resource.middleware(self); resource.middleware(self);
} }
fn validate_origin<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CorsError> { fn validate_origin(&self, req: &Request) -> Result<(), CorsError> {
if let Some(hdr) = req.headers().get(header::ORIGIN) { if let Some(hdr) = req.headers().get(header::ORIGIN) {
if let Ok(origin) = hdr.to_str() { if let Ok(origin) = hdr.to_str() {
return match self.inner.origins { return match self.inner.origins {
@ -303,9 +306,7 @@ impl Cors {
} }
} }
fn validate_allowed_method<S>( fn validate_allowed_method(&self, req: &Request) -> Result<(), CorsError> {
&self, req: &mut HttpRequest<S>,
) -> Result<(), CorsError> {
if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) { if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) {
if let Ok(meth) = hdr.to_str() { if let Ok(meth) = hdr.to_str() {
if let Ok(method) = Method::try_from(meth) { if let Ok(method) = Method::try_from(meth) {
@ -323,9 +324,7 @@ impl Cors {
} }
} }
fn validate_allowed_headers<S>( fn validate_allowed_headers(&self, req: &Request) -> Result<(), CorsError> {
&self, req: &mut HttpRequest<S>,
) -> Result<(), CorsError> {
match self.inner.headers { match self.inner.headers {
AllOrSome::All => Ok(()), AllOrSome::All => Ok(()),
AllOrSome::Some(ref allowed_headers) => { AllOrSome::Some(ref allowed_headers) => {
@ -356,11 +355,11 @@ impl Cors {
} }
impl<S> Middleware<S> for Cors { impl<S> Middleware<S> for Cors {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
if self.inner.preflight && Method::OPTIONS == *req.method() { if self.inner.preflight && Method::OPTIONS == *req.method() {
self.validate_origin(req)?; self.validate_origin(req)?;
self.validate_allowed_method(req)?; self.validate_allowed_method(&req)?;
self.validate_allowed_headers(req)?; self.validate_allowed_headers(&req)?;
// allowed headers // allowed headers
let headers = if let Some(headers) = self.inner.headers.as_ref() { let headers = if let Some(headers) = self.inner.headers.as_ref() {
@ -434,7 +433,7 @@ impl<S> Middleware<S> for Cors {
} }
fn response( fn response(
&self, req: &mut HttpRequest<S>, mut resp: HttpResponse, &self, req: &HttpRequest<S>, mut resp: HttpResponse,
) -> Result<Response> { ) -> Result<Response> {
match self.inner.origins { match self.inner.origins {
AllOrSome::All => { AllOrSome::All => {
@ -945,10 +944,9 @@ mod tests {
#[test] #[test]
fn validate_origin_allows_all_origins() { fn validate_origin_allows_all_origins() {
let cors = Cors::default(); let cors = Cors::default();
let mut req = let req = TestRequest::with_header("Origin", "https://www.example.com").finish();
TestRequest::with_header("Origin", "https://www.example.com").finish();
assert!(cors.start(&mut req).ok().unwrap().is_done()) assert!(cors.start(&req).ok().unwrap().is_done())
} }
#[test] #[test]
@ -961,20 +959,20 @@ mod tests {
.allowed_header(header::CONTENT_TYPE) .allowed_header(header::CONTENT_TYPE)
.finish(); .finish();
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
assert!(cors.start(&mut req).is_err()); assert!(cors.start(&req).is_err());
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "put") .header(header::ACCESS_CONTROL_REQUEST_METHOD, "put")
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
assert!(cors.start(&mut req).is_err()); assert!(cors.start(&req).is_err());
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST") .header(header::ACCESS_CONTROL_REQUEST_METHOD, "POST")
.header( .header(
header::ACCESS_CONTROL_REQUEST_HEADERS, header::ACCESS_CONTROL_REQUEST_HEADERS,
@ -983,7 +981,7 @@ mod tests {
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
let resp = cors.start(&mut req).unwrap().response(); let resp = cors.start(&req).unwrap().response();
assert_eq!( assert_eq!(
&b"*"[..], &b"*"[..],
resp.headers() resp.headers()
@ -1007,7 +1005,7 @@ mod tests {
// as_bytes()); // as_bytes());
Rc::get_mut(&mut cors.inner).unwrap().preflight = false; Rc::get_mut(&mut cors.inner).unwrap().preflight = false;
assert!(cors.start(&mut req).unwrap().is_done()); assert!(cors.start(&req).unwrap().is_done());
} }
// #[test] // #[test]
@ -1017,7 +1015,7 @@ mod tests {
// .allowed_origin("https://www.example.com") // .allowed_origin("https://www.example.com")
// .finish(); // .finish();
// let mut req = HttpRequest::default(); // let mut req = HttpRequest::default();
// cors.start(&mut req).unwrap(); // cors.start(&req).unwrap();
// } // }
#[test] #[test]
@ -1027,10 +1025,10 @@ mod tests {
.allowed_origin("https://www.example.com") .allowed_origin("https://www.example.com")
.finish(); .finish();
let mut req = TestRequest::with_header("Origin", "https://www.unknown.com") let req = TestRequest::with_header("Origin", "https://www.unknown.com")
.method(Method::GET) .method(Method::GET)
.finish(); .finish();
cors.start(&mut req).unwrap(); cors.start(&req).unwrap();
} }
#[test] #[test]
@ -1039,30 +1037,30 @@ mod tests {
.allowed_origin("https://www.example.com") .allowed_origin("https://www.example.com")
.finish(); .finish();
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::GET) .method(Method::GET)
.finish(); .finish();
assert!(cors.start(&mut req).unwrap().is_done()); assert!(cors.start(&req).unwrap().is_done());
} }
#[test] #[test]
fn test_no_origin_response() { fn test_no_origin_response() {
let cors = Cors::build().finish(); let cors = Cors::build().finish();
let mut req = TestRequest::default().method(Method::GET).finish(); let req = TestRequest::default().method(Method::GET).finish();
let resp: HttpResponse = HttpResponse::Ok().into(); let resp: HttpResponse = HttpResponse::Ok().into();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&req, resp).unwrap().response();
assert!( assert!(
resp.headers() resp.headers()
.get(header::ACCESS_CONTROL_ALLOW_ORIGIN) .get(header::ACCESS_CONTROL_ALLOW_ORIGIN)
.is_none() .is_none()
); );
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"https://www.example.com"[..], &b"https://www.example.com"[..],
resp.headers() resp.headers()
@ -1083,12 +1081,12 @@ mod tests {
.allowed_header(header::CONTENT_TYPE) .allowed_header(header::CONTENT_TYPE)
.finish(); .finish();
let mut req = TestRequest::with_header("Origin", "https://www.example.com") let req = TestRequest::with_header("Origin", "https://www.example.com")
.method(Method::OPTIONS) .method(Method::OPTIONS)
.finish(); .finish();
let resp: HttpResponse = HttpResponse::Ok().into(); let resp: HttpResponse = HttpResponse::Ok().into();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"*"[..], &b"*"[..],
resp.headers() resp.headers()
@ -1103,7 +1101,7 @@ mod tests {
let resp: HttpResponse = let resp: HttpResponse =
HttpResponse::Ok().header(header::VARY, "Accept").finish(); HttpResponse::Ok().header(header::VARY, "Accept").finish();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"Accept, Origin"[..], &b"Accept, Origin"[..],
resp.headers().get(header::VARY).unwrap().as_bytes() resp.headers().get(header::VARY).unwrap().as_bytes()
@ -1114,7 +1112,7 @@ mod tests {
.allowed_origin("https://www.example.com") .allowed_origin("https://www.example.com")
.finish(); .finish();
let resp: HttpResponse = HttpResponse::Ok().into(); let resp: HttpResponse = HttpResponse::Ok().into();
let resp = cors.response(&mut req, resp).unwrap().response(); let resp = cors.response(&req, resp).unwrap().response();
assert_eq!( assert_eq!(
&b"https://www.example.com"[..], &b"https://www.example.com"[..],
resp.headers() resp.headers()

View file

@ -25,7 +25,7 @@
//! use actix_web::middleware::csrf; //! use actix_web::middleware::csrf;
//! use actix_web::{http, App, HttpRequest, HttpResponse}; //! use actix_web::{http, App, HttpRequest, HttpResponse};
//! //!
//! fn handle_post(_: HttpRequest) -> &'static str { //! fn handle_post(_: &HttpRequest) -> &'static str {
//! "This action should only be triggered with requests from the same site" //! "This action should only be triggered with requests from the same site"
//! } //! }
//! //!
@ -54,6 +54,7 @@ use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Middleware, Started}; use middleware::{Middleware, Started};
use server::Request;
/// Potential cross-site request forgery detected. /// Potential cross-site request forgery detected.
#[derive(Debug, Fail)] #[derive(Debug, Fail)]
@ -187,7 +188,7 @@ impl CsrfFilter {
self self
} }
fn validate<S>(&self, req: &mut HttpRequest<S>) -> Result<(), CsrfError> { fn validate(&self, req: &Request) -> Result<(), CsrfError> {
let is_upgrade = req.headers().contains_key(header::UPGRADE); let is_upgrade = req.headers().contains_key(header::UPGRADE);
let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade); let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade);
@ -209,7 +210,7 @@ impl CsrfFilter {
} }
impl<S> Middleware<S> for CsrfFilter { impl<S> Middleware<S> for CsrfFilter {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
self.validate(req)?; self.validate(req)?;
Ok(Started::Done) Ok(Started::Done)
} }
@ -225,35 +226,35 @@ mod tests {
fn test_safe() { fn test_safe() {
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com"); let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
let mut req = TestRequest::with_header("Origin", "https://www.w3.org") let req = TestRequest::with_header("Origin", "https://www.w3.org")
.method(Method::HEAD) .method(Method::HEAD)
.finish(); .finish();
assert!(csrf.start(&mut req).is_ok()); assert!(csrf.start(&req).is_ok());
} }
#[test] #[test]
fn test_csrf() { fn test_csrf() {
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com"); let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
let mut req = TestRequest::with_header("Origin", "https://www.w3.org") let req = TestRequest::with_header("Origin", "https://www.w3.org")
.method(Method::POST) .method(Method::POST)
.finish(); .finish();
assert!(csrf.start(&mut req).is_err()); assert!(csrf.start(&req).is_err());
} }
#[test] #[test]
fn test_referer() { fn test_referer() {
let csrf = CsrfFilter::new().allowed_origin("https://www.example.com"); let csrf = CsrfFilter::new().allowed_origin("https://www.example.com");
let mut req = TestRequest::with_header( let req = TestRequest::with_header(
"Referer", "Referer",
"https://www.example.com/some/path?query=param", "https://www.example.com/some/path?query=param",
).method(Method::POST) ).method(Method::POST)
.finish(); .finish();
assert!(csrf.start(&mut req).is_ok()); assert!(csrf.start(&req).is_ok());
} }
#[test] #[test]
@ -264,13 +265,13 @@ mod tests {
.allowed_origin("https://www.example.com") .allowed_origin("https://www.example.com")
.allow_upgrade(); .allow_upgrade();
let mut req = TestRequest::with_header("Origin", "https://cswsh.com") let req = TestRequest::with_header("Origin", "https://cswsh.com")
.header("Connection", "Upgrade") .header("Connection", "Upgrade")
.header("Upgrade", "websocket") .header("Upgrade", "websocket")
.method(Method::GET) .method(Method::GET)
.finish(); .finish();
assert!(strict_csrf.start(&mut req).is_err()); assert!(strict_csrf.start(&req).is_err());
assert!(lax_csrf.start(&mut req).is_ok()); assert!(lax_csrf.start(&req).is_ok());
} }
} }

View file

@ -74,9 +74,7 @@ impl DefaultHeaders {
} }
impl<S> Middleware<S> for DefaultHeaders { impl<S> Middleware<S> for DefaultHeaders {
fn response( fn response(&self, _: &HttpRequest<S>, mut resp: HttpResponse) -> Result<Response> {
&self, _: &mut HttpRequest<S>, mut resp: HttpResponse,
) -> Result<Response> {
for (key, value) in self.headers.iter() { for (key, value) in self.headers.iter() {
if !resp.headers().contains_key(key) { if !resp.headers().contains_key(key) {
resp.headers_mut().insert(key, value.clone()); resp.headers_mut().insert(key, value.clone());
@ -97,22 +95,23 @@ impl<S> Middleware<S> for DefaultHeaders {
mod tests { mod tests {
use super::*; use super::*;
use http::header::CONTENT_TYPE; use http::header::CONTENT_TYPE;
use test::TestRequest;
#[test] #[test]
fn test_default_headers() { fn test_default_headers() {
let mw = DefaultHeaders::new().header(CONTENT_TYPE, "0001"); let mw = DefaultHeaders::new().header(CONTENT_TYPE, "0001");
let mut req = HttpRequest::default(); let req = TestRequest::default().finish();
let resp = HttpResponse::Ok().finish(); let resp = HttpResponse::Ok().finish();
let resp = match mw.response(&mut req, resp) { let resp = match mw.response(&req, resp) {
Ok(Response::Done(resp)) => resp, Ok(Response::Done(resp)) => resp,
_ => panic!(), _ => panic!(),
}; };
assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001");
let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish(); let resp = HttpResponse::Ok().header(CONTENT_TYPE, "0002").finish();
let resp = match mw.response(&mut req, resp) { let resp = match mw.response(&req, resp) {
Ok(Response::Done(resp)) => resp, Ok(Response::Done(resp)) => resp,
_ => panic!(), _ => panic!(),
}; };

View file

@ -6,7 +6,7 @@ use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Middleware, Response}; use middleware::{Middleware, Response};
type ErrorHandler<S> = Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response>; type ErrorHandler<S> = Fn(&HttpRequest<S>, HttpResponse) -> Result<Response>;
/// `Middleware` for allowing custom handlers for responses. /// `Middleware` for allowing custom handlers for responses.
/// ///
@ -21,7 +21,7 @@ type ErrorHandler<S> = Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response>
/// use actix_web::middleware::{ErrorHandlers, Response}; /// use actix_web::middleware::{ErrorHandlers, Response};
/// use actix_web::{http, App, HttpRequest, HttpResponse, Result}; /// use actix_web::{http, App, HttpRequest, HttpResponse, Result};
/// ///
/// fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> { /// fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
/// let mut builder = resp.into_builder(); /// let mut builder = resp.into_builder();
/// builder.header(http::header::CONTENT_TYPE, "application/json"); /// builder.header(http::header::CONTENT_TYPE, "application/json");
/// Ok(Response::Done(builder.into())) /// Ok(Response::Done(builder.into()))
@ -62,7 +62,7 @@ impl<S> ErrorHandlers<S> {
/// Register error handler for specified status code /// Register error handler for specified status code
pub fn handler<F>(mut self, status: StatusCode, handler: F) -> Self pub fn handler<F>(mut self, status: StatusCode, handler: F) -> Self
where where
F: Fn(&mut HttpRequest<S>, HttpResponse) -> Result<Response> + 'static, F: Fn(&HttpRequest<S>, HttpResponse) -> Result<Response> + 'static,
{ {
self.handlers.insert(status, Box::new(handler)); self.handlers.insert(status, Box::new(handler));
self self
@ -70,9 +70,7 @@ impl<S> ErrorHandlers<S> {
} }
impl<S: 'static> Middleware<S> for ErrorHandlers<S> { impl<S: 'static> Middleware<S> for ErrorHandlers<S> {
fn response( fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> {
if let Some(handler) = self.handlers.get(&resp.status()) { if let Some(handler) = self.handlers.get(&resp.status()) {
handler(req, resp) handler(req, resp)
} else { } else {
@ -91,7 +89,10 @@ mod tests {
use middleware::Started; use middleware::Started;
use test; use test;
fn render_500<S>(_: &mut HttpRequest<S>, resp: HttpResponse) -> Result<Response> { use server::Request;
use test::TestRequest;
fn render_500<S>(_: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
let mut builder = resp.into_builder(); let mut builder = resp.into_builder();
builder.header(CONTENT_TYPE, "0001"); builder.header(CONTENT_TYPE, "0001");
Ok(Response::Done(builder.into())) Ok(Response::Done(builder.into()))
@ -102,7 +103,7 @@ mod tests {
let mw = let mw =
ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500); ErrorHandlers::new().handler(StatusCode::INTERNAL_SERVER_ERROR, render_500);
let mut req = HttpRequest::default(); let mut req = TestRequest::default().finish();
let resp = HttpResponse::InternalServerError().finish(); let resp = HttpResponse::InternalServerError().finish();
let resp = match mw.response(&mut req, resp) { let resp = match mw.response(&mut req, resp) {
Ok(Response::Done(resp)) => resp, Ok(Response::Done(resp)) => resp,
@ -121,7 +122,7 @@ mod tests {
struct MiddlewareOne; struct MiddlewareOne;
impl<S> Middleware<S> for MiddlewareOne { impl<S> Middleware<S> for MiddlewareOne {
fn start(&self, _req: &mut HttpRequest<S>) -> Result<Started, Error> { fn start(&self, _: &HttpRequest<S>) -> Result<Started, Error> {
Err(ErrorInternalServerError("middleware error")) Err(ErrorInternalServerError("middleware error"))
} }
} }

View file

@ -46,6 +46,7 @@
//! )); //! ));
//! } //! }
//! ``` //! ```
use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use cookie::{Cookie, CookieJar, Key}; use cookie::{Cookie, CookieJar, Key};
@ -58,6 +59,7 @@ use http::header::{self, HeaderValue};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Middleware, Response, Started}; use middleware::{Middleware, Response, Started};
use server::Request;
/// The helper trait to obtain your identity from a request. /// The helper trait to obtain your identity from a request.
/// ///
@ -88,32 +90,32 @@ use middleware::{Middleware, Response, Started};
pub trait RequestIdentity { pub trait RequestIdentity {
/// Return the claimed identity of the user associated request or /// Return the claimed identity of the user associated request or
/// ``None`` if no identity can be found associated with the request. /// ``None`` if no identity can be found associated with the request.
fn identity(&self) -> Option<&str>; fn identity(&self) -> Option<String>;
/// Remember identity. /// Remember identity.
fn remember(&mut self, identity: String); fn remember(&self, identity: String);
/// This method is used to 'forget' the current identity on subsequent /// This method is used to 'forget' the current identity on subsequent
/// requests. /// requests.
fn forget(&mut self); fn forget(&self);
} }
impl<S> RequestIdentity for HttpRequest<S> { impl<S> RequestIdentity for HttpRequest<S> {
fn identity(&self) -> Option<&str> { fn identity(&self) -> Option<String> {
if let Some(id) = self.extensions().get::<IdentityBox>() { if let Some(id) = self.extensions().get::<IdentityBox>() {
return id.0.identity(); return id.0.identity().map(|s| s.to_owned());
} }
None None
} }
fn remember(&mut self, identity: String) { fn remember(&self, identity: String) {
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() { if let Some(mut id) = self.extensions_mut().get_mut::<IdentityBox>() {
return id.0.remember(identity); return id.0.as_mut().remember(identity);
} }
} }
fn forget(&mut self) { fn forget(&self) {
if let Some(id) = self.extensions_mut().get_mut::<IdentityBox>() { if let Some(mut id) = self.extensions_mut().get_mut::<IdentityBox>() {
return id.0.forget(); return id.0.forget();
} }
} }
@ -145,7 +147,7 @@ pub trait IdentityPolicy<S>: Sized + 'static {
type Future: Future<Item = Self::Identity, Error = Error>; type Future: Future<Item = Self::Identity, Error = Error>;
/// Parse the session from request and load data from a service identity. /// Parse the session from request and load data from a service identity.
fn from_request(&self, request: &mut HttpRequest<S>) -> Self::Future; fn from_request(&self, request: &HttpRequest<S>) -> Self::Future;
} }
/// Request identity middleware /// Request identity middleware
@ -178,27 +180,21 @@ impl<T> IdentityService<T> {
struct IdentityBox(Box<Identity>); struct IdentityBox(Box<Identity>);
impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> { impl<S: 'static, T: IdentityPolicy<S>> Middleware<S> for IdentityService<T> {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
let mut req = req.clone(); let req = req.clone();
let fut = self.backend.from_request(&req).then(move |res| match res {
let fut = self Ok(id) => {
.backend req.extensions_mut().insert(IdentityBox(Box::new(id)));
.from_request(&mut req) FutOk(None)
.then(move |res| match res { }
Ok(id) => { Err(err) => FutErr(err),
req.extensions_mut().insert(IdentityBox(Box::new(id))); });
FutOk(None)
}
Err(err) => FutErr(err),
});
Ok(Started::Future(Box::new(fut))) Ok(Started::Future(Box::new(fut)))
} }
fn response( fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
&self, req: &mut HttpRequest<S>, resp: HttpResponse, if let Some(ref mut id) = req.extensions_mut().get_mut::<IdentityBox>() {
) -> Result<Response> { id.0.as_mut().write(resp)
if let Some(mut id) = req.extensions_mut().remove::<IdentityBox>() {
id.0.write(resp)
} else { } else {
Ok(Response::Done(resp)) Ok(Response::Done(resp))
} }
@ -291,9 +287,9 @@ impl CookieIdentityInner {
Ok(()) Ok(())
} }
fn load<S>(&self, req: &mut HttpRequest<S>) -> Option<String> { fn load<S>(&self, req: &HttpRequest<S>) -> Option<String> {
if let Ok(cookies) = req.cookies() { if let Ok(cookies) = req.cookies() {
for cookie in cookies { for cookie in cookies.iter() {
if cookie.name() == self.name { if cookie.name() == self.name {
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
jar.add_original(cookie.clone()); jar.add_original(cookie.clone());
@ -382,7 +378,7 @@ impl<S> IdentityPolicy<S> for CookieIdentityPolicy {
type Identity = CookieIdentity; type Identity = CookieIdentity;
type Future = FutureResult<CookieIdentity, Error>; type Future = FutureResult<CookieIdentity, Error>;
fn from_request(&self, req: &mut HttpRequest<S>) -> Self::Future { fn from_request(&self, req: &HttpRequest<S>) -> Self::Future {
let identity = self.0.load(req); let identity = self.0.load(req);
FutOk(CookieIdentity { FutOk(CookieIdentity {
identity, identity,

View file

@ -11,6 +11,7 @@ use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Finished, Middleware, Started}; use middleware::{Finished, Middleware, Started};
use server::Request;
/// `Middleware` for logging request and response info to the terminal. /// `Middleware` for logging request and response info to the terminal.
/// ///
@ -107,7 +108,7 @@ impl Default for Logger {
struct StartTime(time::Tm); struct StartTime(time::Tm);
impl Logger { impl Logger {
fn log<S>(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) { fn log<S>(&self, req: &HttpRequest<S>, resp: &HttpResponse) {
if let Some(entry_time) = req.extensions().get::<StartTime>() { if let Some(entry_time) = req.extensions().get::<StartTime>() {
let render = |fmt: &mut Formatter| { let render = |fmt: &mut Formatter| {
for unit in &self.format.0 { for unit in &self.format.0 {
@ -121,14 +122,14 @@ impl Logger {
} }
impl<S> Middleware<S> for Logger { impl<S> Middleware<S> for Logger {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
if !self.exclude.contains(req.path()) { if !self.exclude.contains(req.path()) {
req.extensions_mut().insert(StartTime(time::now())); req.extensions_mut().insert(StartTime(time::now()));
} }
Ok(Started::Done) Ok(Started::Done)
} }
fn finish(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) -> Finished { fn finish(&self, req: &HttpRequest<S>, resp: &HttpResponse) -> Finished {
self.log(req, resp); self.log(req, resp);
Finished::Done Finished::Done
} }
@ -312,34 +313,27 @@ mod tests {
use http::header::{self, HeaderMap}; use http::header::{self, HeaderMap};
use http::{Method, StatusCode, Uri, Version}; use http::{Method, StatusCode, Uri, Version};
use std::str::FromStr; use std::str::FromStr;
use test::TestRequest;
use time; use time;
#[test] #[test]
fn test_logger() { fn test_logger() {
let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");
let mut headers = HeaderMap::new(); let req = TestRequest::with_header(
headers.insert(
header::USER_AGENT, header::USER_AGENT,
header::HeaderValue::from_static("ACTIX-WEB"), header::HeaderValue::from_static("ACTIX-WEB"),
); ).finish();
let mut req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let resp = HttpResponse::build(StatusCode::OK) let resp = HttpResponse::build(StatusCode::OK)
.header("X-Test", "ttt") .header("X-Test", "ttt")
.force_close() .force_close()
.finish(); .finish();
match logger.start(&mut req) { match logger.start(&req) {
Ok(Started::Done) => (), Ok(Started::Done) => (),
_ => panic!(), _ => panic!(),
}; };
match logger.finish(&mut req, &resp) { match logger.finish(&req, &resp) {
Finished::Done => (), Finished::Done => (),
_ => panic!(), _ => panic!(),
} }
@ -358,18 +352,10 @@ mod tests {
fn test_default_format() { fn test_default_format() {
let format = Format::default(); let format = Format::default();
let mut headers = HeaderMap::new(); let req = TestRequest::with_header(
headers.insert(
header::USER_AGENT, header::USER_AGENT,
header::HeaderValue::from_static("ACTIX-WEB"), header::HeaderValue::from_static("ACTIX-WEB"),
); ).finish();
let req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
let entry_time = time::now(); let entry_time = time::now();
@ -384,13 +370,7 @@ mod tests {
assert!(s.contains("200 0")); assert!(s.contains("200 0"));
assert!(s.contains("ACTIX-WEB")); assert!(s.contains("ACTIX-WEB"));
let req = HttpRequest::new( let req = TestRequest::with_uri("/?test").finish();
Method::GET,
Uri::from_str("/?test").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); let resp = HttpResponse::build(StatusCode::OK).force_close().finish();
let entry_time = time::now(); let entry_time = time::now();

View file

@ -4,6 +4,7 @@ use futures::Future;
use error::{Error, Result}; use error::{Error, Result};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use server::Request;
mod logger; mod logger;
@ -51,20 +52,18 @@ pub enum Finished {
pub trait Middleware<S>: 'static { pub trait Middleware<S>: 'static {
/// Method is called when request is ready. It may return /// Method is called when request is ready. It may return
/// future, which should resolve before next middleware get called. /// future, which should resolve before next middleware get called.
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
Ok(Started::Done) Ok(Started::Done)
} }
/// Method is called when handler returns response, /// Method is called when handler returns response,
/// but before sending http message to peer. /// but before sending http message to peer.
fn response( fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
&self, req: &mut HttpRequest<S>, resp: HttpResponse,
) -> Result<Response> {
Ok(Response::Done(resp)) Ok(Response::Done(resp))
} }
/// Method is called after body stream get sent to peer. /// Method is called after body stream get sent to peer.
fn finish(&self, req: &mut HttpRequest<S>, resp: &HttpResponse) -> Finished { fn finish(&self, req: &HttpRequest<S>, resp: &HttpResponse) -> Finished {
Finished::Done Finished::Done
} }
} }

View file

@ -83,6 +83,7 @@ use handler::FromRequest;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Middleware, Response, Started}; use middleware::{Middleware, Response, Started};
use server::Request;
/// The helper trait to obtain your session data from a request. /// The helper trait to obtain your session data from a request.
/// ///
@ -246,7 +247,7 @@ impl<S, T: SessionBackend<S>> SessionStorage<T, S> {
} }
impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> { impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {
fn start(&self, req: &mut HttpRequest<S>) -> Result<Started> { fn start(&self, req: &HttpRequest<S>) -> Result<Started> {
let mut req = req.clone(); let mut req = req.clone();
let fut = self.0.from_request(&mut req).then(move |res| match res { let fut = self.0.from_request(&mut req).then(move |res| match res {
@ -260,10 +261,8 @@ impl<S: 'static, T: SessionBackend<S>> Middleware<S> for SessionStorage<T, S> {
Ok(Started::Future(Box::new(fut))) Ok(Started::Future(Box::new(fut)))
} }
fn response( fn response(&self, req: &HttpRequest<S>, resp: HttpResponse) -> Result<Response> {
&self, req: &mut HttpRequest<S>, resp: HttpResponse, if let Some(s_box) = req.extensions().get::<Arc<SessionImplCell>>() {
) -> Result<Response> {
if let Some(s_box) = req.extensions_mut().remove::<Arc<SessionImplCell>>() {
s_box.0.borrow_mut().write(resp) s_box.0.borrow_mut().write(resp)
} else { } else {
Ok(Response::Done(resp)) Ok(Response::Done(resp))
@ -421,7 +420,7 @@ impl CookieSessionInner {
fn load<S>(&self, req: &mut HttpRequest<S>) -> HashMap<String, String> { fn load<S>(&self, req: &mut HttpRequest<S>) -> HashMap<String, String> {
if let Ok(cookies) = req.cookies() { if let Ok(cookies) = req.cookies() {
for cookie in cookies { for cookie in cookies.iter() {
if cookie.name() == self.name { if cookie.name() == self.name {
let mut jar = CookieJar::new(); let mut jar = CookieJar::new();
jar.add_original(cookie.clone()); jar.add_original(cookie.clone());

View file

@ -1,7 +1,7 @@
use std; use std;
use std::rc::Rc;
use std::ops::Index; use std::ops::Index;
use std::path::PathBuf; use std::path::PathBuf;
use std::rc::Rc;
use std::str::FromStr; use std::str::FromStr;
use http::StatusCode; use http::StatusCode;
@ -29,11 +29,11 @@ pub(crate) enum ParamItem {
/// 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, Clone)]
pub struct Params { pub struct Params {
url: Url, url: Url,
pub(crate) tail: u16, pub(crate) tail: u16,
segments: SmallVec<[(Rc<String>, ParamItem); 3]>, pub(crate) segments: SmallVec<[(Rc<String>, ParamItem); 3]>,
} }
impl Params { impl Params {
@ -45,6 +45,14 @@ impl Params {
} }
} }
pub(crate) fn with_url(url: &Url) -> Params {
Params {
url: url.clone(),
tail: 0,
segments: SmallVec::new(),
}
}
pub(crate) fn clear(&mut self) { pub(crate) fn clear(&mut self) {
self.segments.clear(); self.segments.clear();
} }
@ -62,7 +70,8 @@ impl Params {
} }
pub(crate) fn add_static(&mut self, name: &str, value: &'static str) { pub(crate) fn add_static(&mut self, name: &str, value: &'static str) {
self.segments.push((Rc::new(name.to_string()), ParamItem::Static(value))); self.segments
.push((Rc::new(name.to_string()), ParamItem::Static(value)));
} }
/// Check if there are any matched patterns /// Check if there are any matched patterns
@ -151,16 +160,16 @@ impl<'a> Iterator for ParamsIter<'a> {
} }
} }
impl<'a, 'b> Index<&'b str> for &'a Params { impl<'a> Index<&'a str> for Params {
type Output = str; type Output = str;
fn index(&self, name: &'b str) -> &str { fn index(&self, name: &'a str) -> &str {
self.get(name) self.get(name)
.expect("Value for parameter is not available") .expect("Value for parameter is not available")
} }
} }
impl<'a> Index<usize> for &'a Params { impl Index<usize> for Params {
type Output = str; type Output = str;
fn index(&self, idx: usize) -> &str { fn index(&self, idx: usize) -> &str {

View file

@ -59,20 +59,14 @@ impl Payload {
} }
} }
/// Indicates EOF of payload
#[inline]
pub fn eof(&self) -> bool {
self.inner.borrow().eof()
}
/// Length of the data in this payload /// Length of the data in this payload
#[inline] #[cfg(test)]
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.inner.borrow().len() self.inner.borrow().len()
} }
/// Is payload empty /// Is payload empty
#[inline] #[cfg(test)]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.inner.borrow().len() == 0 self.inner.borrow().len() == 0
} }
@ -225,12 +219,7 @@ impl Inner {
} }
} }
#[inline] #[cfg(test)]
fn eof(&self) -> bool {
self.items.is_empty() && self.eof
}
#[inline]
fn len(&self) -> usize { fn len(&self) -> usize {
self.len self.len
} }

View file

@ -15,7 +15,7 @@ use header::ContentEncoding;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::{Finished, Middleware, Response, Started}; use middleware::{Finished, Middleware, Response, Started};
use server::{HttpHandlerTask, Writer, WriterState}; use server::{HttpHandlerTask, Request, Writer, WriterState};
#[doc(hidden)] #[doc(hidden)]
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
@ -29,13 +29,11 @@ pub enum HandlerType {
pub trait PipelineHandler<S> { pub trait PipelineHandler<S> {
fn encoding(&self) -> ContentEncoding; fn encoding(&self) -> ContentEncoding;
fn handle( fn handle(&self, &HttpRequest<S>, HandlerType) -> AsyncResult<HttpResponse>;
&self, req: HttpRequest<S>, htype: HandlerType,
) -> AsyncResult<HttpResponse>;
} }
#[doc(hidden)] #[doc(hidden)]
pub struct Pipeline<S, H>( pub struct Pipeline<S: 'static, H>(
PipelineInfo<S>, PipelineInfo<S>,
PipelineState<S, H>, PipelineState<S, H>,
Rc<Vec<Box<Middleware<S>>>>, Rc<Vec<Box<Middleware<S>>>>,
@ -76,7 +74,7 @@ impl<S: 'static, H: PipelineHandler<S>> PipelineState<S, H> {
} }
} }
struct PipelineInfo<S> { struct PipelineInfo<S: 'static> {
req: HttpRequest<S>, req: HttpRequest<S>,
count: u16, count: u16,
context: Option<Box<ActorHttpContext>>, context: Option<Box<ActorHttpContext>>,
@ -85,7 +83,7 @@ struct PipelineInfo<S> {
encoding: ContentEncoding, encoding: ContentEncoding,
} }
impl<S> PipelineInfo<S> { impl<S: 'static> PipelineInfo<S> {
fn new(req: HttpRequest<S>) -> PipelineInfo<S> { fn new(req: HttpRequest<S>) -> PipelineInfo<S> {
PipelineInfo { PipelineInfo {
req, req,
@ -129,16 +127,6 @@ impl<S: 'static, H: PipelineHandler<S>> Pipeline<S, H> {
} }
} }
impl Pipeline<(), Inner<()>> {
pub fn error<R: Into<HttpResponse>>(err: R) -> Box<HttpHandlerTask> {
Box::new(Pipeline::<(), Inner<()>>(
PipelineInfo::new(HttpRequest::default()),
ProcessResponse::init(err.into()),
Rc::new(Vec::new()),
))
}
}
impl<S: 'static, H> Pipeline<S, H> { impl<S: 'static, H> Pipeline<S, H> {
#[inline] #[inline]
fn is_done(&self) -> bool { fn is_done(&self) -> bool {
@ -241,16 +229,16 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
// execute middlewares, we need this stage because middlewares could be // execute middlewares, we need this stage because middlewares could be
// non-async and we can move to next state immediately // non-async and we can move to next state immediately
let len = mws.len() as u16; let len = mws.len() as u16;
loop { loop {
if info.count == len { if info.count == len {
let reply = hnd.handle(info.req.clone(), htype); let reply = hnd.handle(&info.req, htype);
return WaitingResponse::init(info, mws, reply); return WaitingResponse::init(info, mws, reply);
} else { } else {
let state = mws[info.count as usize].start(&mut info.req); match mws[info.count as usize].start(&info.req) {
match state {
Ok(Started::Done) => info.count += 1, Ok(Started::Done) => info.count += 1,
Ok(Started::Response(resp)) => { Ok(Started::Response(resp)) => {
return RunMiddlewares::init(info, mws, resp) return RunMiddlewares::init(info, mws, resp);
} }
Ok(Started::Future(fut)) => { Ok(Started::Future(fut)) => {
return PipelineState::Starting(StartMiddlewares { return PipelineState::Starting(StartMiddlewares {
@ -260,7 +248,9 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
_s: PhantomData, _s: PhantomData,
}) })
} }
Err(err) => return RunMiddlewares::init(info, mws, err.into()), Err(err) => {
return RunMiddlewares::init(info, mws, err.into());
}
} }
} }
} }
@ -270,9 +260,12 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
&mut self, info: &mut PipelineInfo<S>, mws: &[Box<Middleware<S>>], &mut self, info: &mut PipelineInfo<S>, mws: &[Box<Middleware<S>>],
) -> Option<PipelineState<S, H>> { ) -> Option<PipelineState<S, H>> {
let len = mws.len() as u16; let len = mws.len() as u16;
'outer: loop { 'outer: loop {
match self.fut.as_mut().unwrap().poll() { match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None, Ok(Async::NotReady) => {
return None;
}
Ok(Async::Ready(resp)) => { Ok(Async::Ready(resp)) => {
info.count += 1; info.count += 1;
if let Some(resp) = resp { if let Some(resp) = resp {
@ -280,11 +273,11 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
} }
loop { loop {
if info.count == len { if info.count == len {
let reply = self.hnd.handle(info.req.clone(), self.htype); let reply = self.hnd.handle(&info.req, self.htype);
return Some(WaitingResponse::init(info, mws, reply)); return Some(WaitingResponse::init(info, mws, reply));
} else { } else {
let state = mws[info.count as usize].start(&mut info.req); let res = mws[info.count as usize].start(&info.req);
match state { match res {
Ok(Started::Done) => info.count += 1, Ok(Started::Done) => info.count += 1,
Ok(Started::Response(resp)) => { Ok(Started::Response(resp)) => {
return Some(RunMiddlewares::init(info, mws, resp)); return Some(RunMiddlewares::init(info, mws, resp));
@ -298,13 +291,15 @@ impl<S: 'static, H: PipelineHandler<S>> StartMiddlewares<S, H> {
info, info,
mws, mws,
err.into(), err.into(),
)) ));
} }
} }
} }
} }
} }
Err(err) => return Some(RunMiddlewares::init(info, mws, err.into())), Err(err) => {
return Some(RunMiddlewares::init(info, mws, err.into()));
}
} }
} }
} }
@ -324,8 +319,8 @@ impl<S: 'static, H> WaitingResponse<S, H> {
reply: AsyncResult<HttpResponse>, reply: AsyncResult<HttpResponse>,
) -> PipelineState<S, H> { ) -> PipelineState<S, H> {
match reply.into() { match reply.into() {
AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()),
AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, mws, resp), AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, mws, resp),
AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()),
AsyncResultItem::Future(fut) => PipelineState::Handler(WaitingResponse { AsyncResultItem::Future(fut) => PipelineState::Handler(WaitingResponse {
fut, fut,
_s: PhantomData, _s: PhantomData,
@ -339,9 +334,7 @@ impl<S: 'static, H> WaitingResponse<S, H> {
) -> Option<PipelineState<S, H>> { ) -> Option<PipelineState<S, H>> {
match self.fut.poll() { match self.fut.poll() {
Ok(Async::NotReady) => None, Ok(Async::NotReady) => None,
Ok(Async::Ready(response)) => { Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, mws, resp)),
Some(RunMiddlewares::init(info, mws, response))
}
Err(err) => Some(RunMiddlewares::init(info, mws, err.into())), Err(err) => Some(RunMiddlewares::init(info, mws, err.into())),
} }
} }
@ -367,7 +360,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
let len = mws.len(); let len = mws.len();
loop { loop {
let state = mws[curr].response(&mut info.req, resp); let state = mws[curr].response(&info.req, resp);
resp = match state { resp = match state {
Err(err) => { Err(err) => {
info.count = (curr + 1) as u16; info.count = (curr + 1) as u16;
@ -387,7 +380,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
fut: Some(fut), fut: Some(fut),
_s: PhantomData, _s: PhantomData,
_h: PhantomData, _h: PhantomData,
}) });
} }
}; };
} }
@ -413,7 +406,7 @@ impl<S: 'static, H> RunMiddlewares<S, H> {
if self.curr == len { if self.curr == len {
return Some(ProcessResponse::init(resp)); return Some(ProcessResponse::init(resp));
} else { } else {
let state = mws[self.curr].response(&mut info.req, resp); let state = mws[self.curr].response(&info.req, resp);
match state { match state {
Err(err) => return Some(ProcessResponse::init(err.into())), Err(err) => return Some(ProcessResponse::init(err.into())),
Ok(Response::Done(r)) => { Ok(Response::Done(r)) => {
@ -495,19 +488,16 @@ impl<S: 'static, H> ProcessResponse<S, H> {
let encoding = let encoding =
self.resp.content_encoding().unwrap_or(info.encoding); self.resp.content_encoding().unwrap_or(info.encoding);
let result = match io.start( let result =
info.req.as_mut(), match io.start(&info.req, &mut self.resp, encoding) {
&mut self.resp, Ok(res) => res,
encoding, Err(err) => {
) { info.error = Some(err.into());
Ok(res) => res, return Ok(FinishingMiddlewares::init(
Err(err) => { info, mws, self.resp,
info.error = Some(err.into()); ));
return Ok(FinishingMiddlewares::init( }
info, mws, self.resp, };
));
}
};
if let Some(err) = self.resp.error() { if let Some(err) = self.resp.error() {
if self.resp.status().is_server_error() { if self.resp.status().is_server_error() {
@ -747,8 +737,8 @@ impl<S: 'static, H> FinishingMiddlewares<S, H> {
} }
info.count -= 1; info.count -= 1;
let state = mws[info.count as usize] let state =
.finish(&mut info.req, self.resp.as_ref().unwrap()); mws[info.count as usize].finish(&info.req, self.resp.as_ref().unwrap());
match state { match state {
Finished::Done => { Finished::Done => {
if info.count == 0 { if info.count == 0 {
@ -797,9 +787,10 @@ mod tests {
use actix::*; use actix::*;
use context::HttpContext; use context::HttpContext;
use futures::future::{lazy, result}; use futures::future::{lazy, result};
use http::StatusCode;
use tokio::runtime::current_thread::Runtime; use tokio::runtime::current_thread::Runtime;
use test::TestRequest;
impl<S, H> PipelineState<S, H> { impl<S, H> PipelineState<S, H> {
fn is_none(&self) -> Option<bool> { fn is_none(&self) -> Option<bool> {
if let PipelineState::None = *self { if let PipelineState::None = *self {
@ -827,12 +818,13 @@ mod tests {
Runtime::new() Runtime::new()
.unwrap() .unwrap()
.block_on(lazy(|| { .block_on(lazy(|| {
let mut info = PipelineInfo::new(HttpRequest::default()); let req = TestRequest::default().finish();
let mut info = PipelineInfo::new(req);
Completed::<(), Inner<()>>::init(&mut info) Completed::<(), Inner<()>>::init(&mut info)
.is_none() .is_none()
.unwrap(); .unwrap();
let req = HttpRequest::default(); let req = TestRequest::default().finish();
let ctx = HttpContext::new(req.clone(), MyActor); let ctx = HttpContext::new(req.clone(), MyActor);
let addr = ctx.address(); let addr = ctx.address();
let mut info = PipelineInfo::new(req); let mut info = PipelineInfo::new(req);

View file

@ -1,10 +1,12 @@
//! Route match predicates //! Route match predicates
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::marker::PhantomData;
use http; use http;
use http::{header, HttpTryFrom}; use http::{header, HttpTryFrom};
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use std::marker::PhantomData; use server::message::Request;
/// Trait defines resource route predicate. /// Trait defines resource route predicate.
/// Predicate can modify request object. It is also possible to /// Predicate can modify request object. It is also possible to
@ -12,7 +14,7 @@ use std::marker::PhantomData;
/// Extensions container available via `HttpRequest::extensions()` method. /// Extensions container available via `HttpRequest::extensions()` method.
pub trait Predicate<S> { pub trait Predicate<S> {
/// Check if request matches predicate /// Check if request matches predicate
fn check(&self, &mut HttpRequest<S>) -> bool; fn check(&self, &Request, &S) -> bool;
} }
/// Return predicate that matches if any of supplied predicate matches. /// Return predicate that matches if any of supplied predicate matches.
@ -45,9 +47,9 @@ impl<S> AnyPredicate<S> {
} }
impl<S: 'static> Predicate<S> for AnyPredicate<S> { impl<S: 'static> Predicate<S> for AnyPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, state: &S) -> bool {
for p in &self.0 { for p in &self.0 {
if p.check(req) { if p.check(req, state) {
return true; return true;
} }
} }
@ -88,9 +90,9 @@ impl<S> AllPredicate<S> {
} }
impl<S: 'static> Predicate<S> for AllPredicate<S> { impl<S: 'static> Predicate<S> for AllPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, state: &S) -> bool {
for p in &self.0 { for p in &self.0 {
if !p.check(req) { if !p.check(req, state) {
return false; return false;
} }
} }
@ -107,8 +109,8 @@ pub fn Not<S: 'static, P: Predicate<S> + 'static>(pred: P) -> NotPredicate<S> {
pub struct NotPredicate<S>(Box<Predicate<S>>); pub struct NotPredicate<S>(Box<Predicate<S>>);
impl<S: 'static> Predicate<S> for NotPredicate<S> { impl<S: 'static> Predicate<S> for NotPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, state: &S) -> bool {
!self.0.check(req) !self.0.check(req, state)
} }
} }
@ -117,7 +119,7 @@ impl<S: 'static> Predicate<S> for NotPredicate<S> {
pub struct MethodPredicate<S>(http::Method, PhantomData<S>); pub struct MethodPredicate<S>(http::Method, PhantomData<S>);
impl<S: 'static> Predicate<S> for MethodPredicate<S> { impl<S: 'static> Predicate<S> for MethodPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, _: &S) -> bool {
*req.method() == self.0 *req.method() == self.0
} }
} }
@ -188,7 +190,7 @@ pub fn Header<S: 'static>(
pub struct HeaderPredicate<S>(header::HeaderName, header::HeaderValue, PhantomData<S>); pub struct HeaderPredicate<S>(header::HeaderName, header::HeaderValue, PhantomData<S>);
impl<S: 'static> Predicate<S> for HeaderPredicate<S> { impl<S: 'static> Predicate<S> for HeaderPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, _: &S) -> bool {
if let Some(val) = req.headers().get(&self.0) { if let Some(val) = req.headers().get(&self.0) {
return val == self.1; return val == self.1;
} }
@ -225,7 +227,7 @@ impl<S> HostPredicate<S> {
} }
impl<S: 'static> Predicate<S> for HostPredicate<S> { impl<S: 'static> Predicate<S> for HostPredicate<S> {
fn check(&self, req: &mut HttpRequest<S>) -> bool { fn check(&self, req: &Request, _: &S) -> bool {
let info = req.connection_info(); let info = req.connection_info();
if let Some(ref scheme) = self.1 { if let Some(ref scheme) = self.1 {
self.0 == info.host() && scheme == info.scheme() self.0 == info.host() && scheme == info.scheme()
@ -237,168 +239,96 @@ impl<S: 'static> Predicate<S> for HostPredicate<S> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use super::*; use super::*;
use http::header::{self, HeaderMap}; use http::header::{self, HeaderMap};
use http::{Method, Uri, Version}; use http::{Method, Uri, Version};
use std::str::FromStr; use test::TestRequest;
#[test] #[test]
fn test_header() { fn test_header() {
let mut headers = HeaderMap::new(); let req = TestRequest::with_header(
headers.insert(
header::TRANSFER_ENCODING, header::TRANSFER_ENCODING,
header::HeaderValue::from_static("chunked"), header::HeaderValue::from_static("chunked"),
); ).finish();
let mut req = HttpRequest::new(
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let pred = Header("transfer-encoding", "chunked"); let pred = Header("transfer-encoding", "chunked");
assert!(pred.check(&mut req)); assert!(pred.check(&req, req.state()));
let pred = Header("transfer-encoding", "other"); let pred = Header("transfer-encoding", "other");
assert!(!pred.check(&mut req)); assert!(!pred.check(&req, req.state()));
let pred = Header("content-type", "other"); let pred = Header("content-type", "other");
assert!(!pred.check(&mut req)); assert!(!pred.check(&req, req.state()));
} }
#[test] #[test]
fn test_host() { fn test_host() {
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::HOST, header::HOST,
header::HeaderValue::from_static("www.rust-lang.org"), header::HeaderValue::from_static("www.rust-lang.org"),
); )
let mut req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
let pred = Host("www.rust-lang.org"); let pred = Host("www.rust-lang.org");
assert!(pred.check(&mut req)); assert!(pred.check(&req, req.state()));
let pred = Host("localhost"); let pred = Host("localhost");
assert!(!pred.check(&mut req)); assert!(!pred.check(&req, req.state()));
} }
#[test] #[test]
fn test_methods() { fn test_methods() {
let mut req = HttpRequest::new( let req = TestRequest::default().finish();
Method::GET, let req2 = TestRequest::default().method(Method::POST).finish();
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
let mut req2 = HttpRequest::new(
Method::POST,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Get().check(&mut req)); assert!(Get().check(&req, req.state()));
assert!(!Get().check(&mut req2)); assert!(!Get().check(&req2, req2.state()));
assert!(Post().check(&mut req2)); assert!(Post().check(&req2, req2.state()));
assert!(!Post().check(&mut req)); assert!(!Post().check(&req, req.state()));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::PUT).finish();
Method::PUT, assert!(Put().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Put().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Put().check(&mut r));
assert!(!Put().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::DELETE).finish();
Method::DELETE, assert!(Delete().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Delete().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Delete().check(&mut r));
assert!(!Delete().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::HEAD).finish();
Method::HEAD, assert!(Head().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Head().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Head().check(&mut r));
assert!(!Head().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::OPTIONS).finish();
Method::OPTIONS, assert!(Options().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Options().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Options().check(&mut r));
assert!(!Options().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::CONNECT).finish();
Method::CONNECT, assert!(Connect().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Connect().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Connect().check(&mut r));
assert!(!Connect().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::PATCH).finish();
Method::PATCH, assert!(Patch().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Patch().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Patch().check(&mut r));
assert!(!Patch().check(&mut req));
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::TRACE).finish();
Method::TRACE, assert!(Trace().check(&r, r.state()));
Uri::from_str("/").unwrap(), assert!(!Trace().check(&req, req.state()));
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Trace().check(&mut r));
assert!(!Trace().check(&mut req));
} }
#[test] #[test]
fn test_preds() { fn test_preds() {
let mut r = HttpRequest::new( let r = TestRequest::default().method(Method::TRACE).finish();
Method::TRACE,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert!(Not(Get()).check(&mut r)); assert!(Not(Get()).check(&r, r.state()));
assert!(!Not(Trace()).check(&mut r)); assert!(!Not(Trace()).check(&r, r.state()));
assert!(All(Trace()).and(Trace()).check(&mut r)); assert!(All(Trace()).and(Trace()).check(&r, r.state()));
assert!(!All(Get()).and(Trace()).check(&mut r)); assert!(!All(Get()).and(Trace()).check(&r, r.state()));
assert!(Any(Get()).or(Trace()).check(&mut r)); assert!(Any(Get()).or(Trace()).check(&r, r.state()));
assert!(!Any(Get()).or(Get()).check(&mut r)); assert!(!Any(Get()).or(Get()).check(&r, r.state()));
} }
} }

View file

@ -12,6 +12,10 @@ use httpresponse::HttpResponse;
use middleware::Middleware; use middleware::Middleware;
use pred; use pred;
use route::Route; use route::Route;
use server::Request;
#[derive(Copy, Clone)]
pub(crate) struct RouteId(usize);
/// *Resource* is an entry in route table which corresponds to requested URL. /// *Resource* is an entry in route table which corresponds to requested URL.
/// ///
@ -131,7 +135,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::*; /// use actix_web::*;
/// fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// ///
/// App::new().resource("/", |r| r.method(http::Method::GET).f(index)); /// App::new().resource("/", |r| r.method(http::Method::GET).f(index));
/// ``` /// ```
@ -141,7 +145,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// # use actix_web::*; /// # use actix_web::*;
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// # fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// App::new().resource("/", |r| r.route().filter(pred::Get()).f(index)); /// App::new().resource("/", |r| r.route().filter(pred::Get()).f(index));
/// ``` /// ```
pub fn method(&mut self, method: Method) -> &mut Route<S> { pub fn method(&mut self, method: Method) -> &mut Route<S> {
@ -154,7 +158,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::*; /// use actix_web::*;
/// fn handler(req: HttpRequest) -> HttpResponse { unimplemented!() } /// fn handler(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// ///
/// App::new().resource("/", |r| r.h(handler)); /// App::new().resource("/", |r| r.h(handler));
/// ``` /// ```
@ -164,7 +168,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// # use actix_web::*; /// # use actix_web::*;
/// # fn handler(req: HttpRequest) -> HttpResponse { unimplemented!() } /// # fn handler(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// App::new().resource("/", |r| r.route().h(handler)); /// App::new().resource("/", |r| r.route().h(handler));
/// ``` /// ```
pub fn h<H: Handler<S>>(&mut self, handler: H) { pub fn h<H: Handler<S>>(&mut self, handler: H) {
@ -177,7 +181,7 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// use actix_web::*; /// use actix_web::*;
/// fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// ///
/// App::new().resource("/", |r| r.f(index)); /// App::new().resource("/", |r| r.f(index));
/// ``` /// ```
@ -187,12 +191,12 @@ impl<S: 'static> ResourceHandler<S> {
/// ```rust /// ```rust
/// # extern crate actix_web; /// # extern crate actix_web;
/// # use actix_web::*; /// # use actix_web::*;
/// # fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } /// # fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() }
/// App::new().resource("/", |r| r.route().f(index)); /// App::new().resource("/", |r| r.route().f(index));
/// ``` /// ```
pub fn f<F, R>(&mut self, handler: F) pub fn f<F, R>(&mut self, handler: F)
where where
F: Fn(HttpRequest<S>) -> R + 'static, F: Fn(&HttpRequest<S>) -> R + 'static,
R: Responder + 'static, R: Responder + 'static,
{ {
self.routes.push(Route::default()); self.routes.push(Route::default());
@ -279,19 +283,24 @@ impl<S: 'static> ResourceHandler<S> {
.push(Box::new(mw)); .push(Box::new(mw));
} }
pub(crate) fn handle( #[inline]
&self, mut req: HttpRequest<S>, pub(crate) fn get_route_id(&self, req: &HttpRequest<S>) -> Option<RouteId> {
) -> Result<AsyncResult<HttpResponse>, HttpRequest<S>> { for idx in 0..self.routes.len() {
for route in &self.routes { if (&self.routes[idx]).check(req) {
if route.check(&mut req) { return Some(RouteId(idx));
return if self.middlewares.is_empty() {
Ok(route.handle(req))
} else {
Ok(route.compose(req, Rc::clone(&self.middlewares)))
};
} }
} }
None
}
Err(req) #[inline]
pub(crate) fn handle(
&self, id: RouteId, req: &HttpRequest<S>,
) -> AsyncResult<HttpResponse> {
if self.middlewares.is_empty() {
(&self.routes[id.0]).handle(req)
} else {
(&self.routes[id.0]).compose(req.clone(), Rc::clone(&self.middlewares))
}
} }
} }

View file

@ -16,6 +16,7 @@ use middleware::{
Started as MiddlewareStarted, Started as MiddlewareStarted,
}; };
use pred::Predicate; use pred::Predicate;
use server::Request;
use with::{With, WithAsync}; use with::{With, WithAsync};
/// Resource route definition /// Resource route definition
@ -31,16 +32,17 @@ impl<S: 'static> Default for Route<S> {
fn default() -> Route<S> { fn default() -> Route<S> {
Route { Route {
preds: Vec::new(), preds: Vec::new(),
handler: InnerHandler::new(|_| HttpResponse::new(StatusCode::NOT_FOUND)), handler: InnerHandler::new(|_: &_| HttpResponse::new(StatusCode::NOT_FOUND)),
} }
} }
} }
impl<S: 'static> Route<S> { impl<S: 'static> Route<S> {
#[inline] #[inline]
pub(crate) fn check(&self, req: &mut HttpRequest<S>) -> bool { pub(crate) fn check(&self, req: &HttpRequest<S>) -> bool {
let state = req.state();
for pred in &self.preds { for pred in &self.preds {
if !pred.check(req) { if !pred.check(req, state) {
return false; return false;
} }
} }
@ -48,7 +50,7 @@ impl<S: 'static> Route<S> {
} }
#[inline] #[inline]
pub(crate) fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> { pub(crate) fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
self.handler.handle(req) self.handler.handle(req)
} }
@ -89,7 +91,7 @@ impl<S: 'static> Route<S> {
/// during route configuration, so it does not return reference to self. /// during route configuration, so it does not return reference to self.
pub fn f<F, R>(&mut self, handler: F) pub fn f<F, R>(&mut self, handler: F)
where where
F: Fn(HttpRequest<S>) -> R + 'static, F: Fn(&HttpRequest<S>) -> R + 'static,
R: Responder + 'static, R: Responder + 'static,
{ {
self.handler = InnerHandler::new(handler); self.handler = InnerHandler::new(handler);
@ -98,7 +100,7 @@ impl<S: 'static> Route<S> {
/// Set async handler function. /// Set async handler function.
pub fn a<H, R, F, E>(&mut self, handler: H) pub fn a<H, R, F, E>(&mut self, handler: H)
where where
H: Fn(HttpRequest<S>) -> F + 'static, H: Fn(&HttpRequest<S>) -> F + 'static,
F: Future<Item = R, Error = E> + 'static, F: Future<Item = R, Error = E> + 'static,
R: Responder + 'static, R: Responder + 'static,
E: Into<Error> + 'static, E: Into<Error> + 'static,
@ -307,7 +309,7 @@ impl<S: 'static> InnerHandler<S> {
#[inline] #[inline]
fn async<H, R, F, E>(h: H) -> Self fn async<H, R, F, E>(h: H) -> Self
where where
H: Fn(HttpRequest<S>) -> F + 'static, H: Fn(&HttpRequest<S>) -> F + 'static,
F: Future<Item = R, Error = E> + 'static, F: Future<Item = R, Error = E> + 'static,
R: Responder + 'static, R: Responder + 'static,
E: Into<Error> + 'static, E: Into<Error> + 'static,
@ -316,7 +318,7 @@ impl<S: 'static> InnerHandler<S> {
} }
#[inline] #[inline]
pub fn handle(&self, req: HttpRequest<S>) -> AsyncResult<HttpResponse> { pub fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
self.0.handle(req) self.0.handle(req)
} }
} }
@ -407,24 +409,27 @@ type Fut = Box<Future<Item = Option<HttpResponse>, Error = Error>>;
impl<S: 'static> StartMiddlewares<S> { impl<S: 'static> StartMiddlewares<S> {
fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> { fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> {
let len = info.mws.len(); let len = info.mws.len();
loop { loop {
if info.count == len { if info.count == len {
let reply = info.handler.handle(info.req.clone()); let reply = info.handler.handle(&info.req);
return WaitingResponse::init(info, reply); return WaitingResponse::init(info, reply);
} else { } else {
let state = info.mws[info.count].start(&mut info.req); let result = info.mws[info.count].start(&info.req);
match state { match result {
Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => { Ok(MiddlewareStarted::Response(resp)) => {
return RunMiddlewares::init(info, resp) return RunMiddlewares::init(info, resp);
} }
Ok(MiddlewareStarted::Future(fut)) => { Ok(MiddlewareStarted::Future(fut)) => {
return ComposeState::Starting(StartMiddlewares { return ComposeState::Starting(StartMiddlewares {
fut: Some(fut), fut: Some(fut),
_s: PhantomData, _s: PhantomData,
}) });
}
Err(err) => {
return RunMiddlewares::init(info, err.into());
} }
Err(err) => return RunMiddlewares::init(info, err.into()),
} }
} }
} }
@ -432,9 +437,12 @@ impl<S: 'static> StartMiddlewares<S> {
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> { fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
let len = info.mws.len(); let len = info.mws.len();
'outer: loop { 'outer: loop {
match self.fut.as_mut().unwrap().poll() { match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None, Ok(Async::NotReady) => {
return None;
}
Ok(Async::Ready(resp)) => { Ok(Async::Ready(resp)) => {
info.count += 1; info.count += 1;
if let Some(resp) = resp { if let Some(resp) = resp {
@ -442,11 +450,11 @@ impl<S: 'static> StartMiddlewares<S> {
} }
loop { loop {
if info.count == len { if info.count == len {
let reply = info.handler.handle(info.req.clone()); let reply = info.handler.handle(&info.req);
return Some(WaitingResponse::init(info, reply)); return Some(WaitingResponse::init(info, reply));
} else { } else {
let state = info.mws[info.count].start(&mut info.req); let result = info.mws[info.count].start(&info.req);
match state { match result {
Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => { Ok(MiddlewareStarted::Response(resp)) => {
return Some(RunMiddlewares::init(info, resp)); return Some(RunMiddlewares::init(info, resp));
@ -456,21 +464,25 @@ impl<S: 'static> StartMiddlewares<S> {
continue 'outer; continue 'outer;
} }
Err(err) => { Err(err) => {
return Some(RunMiddlewares::init(info, err.into())) return Some(RunMiddlewares::init(info, err.into()));
} }
} }
} }
} }
} }
Err(err) => return Some(RunMiddlewares::init(info, err.into())), Err(err) => {
return Some(RunMiddlewares::init(info, err.into()));
}
} }
} }
} }
} }
type HandlerFuture = Future<Item = HttpResponse, Error = Error>;
// waiting for response // waiting for response
struct WaitingResponse<S> { struct WaitingResponse<S> {
fut: Box<Future<Item = HttpResponse, Error = Error>>, fut: Box<HandlerFuture>,
_s: PhantomData<S>, _s: PhantomData<S>,
} }
@ -480,8 +492,8 @@ impl<S: 'static> WaitingResponse<S> {
info: &mut ComposeInfo<S>, reply: AsyncResult<HttpResponse>, info: &mut ComposeInfo<S>, reply: AsyncResult<HttpResponse>,
) -> ComposeState<S> { ) -> ComposeState<S> {
match reply.into() { match reply.into() {
AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()),
AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp), AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp),
AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()),
AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse { AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse {
fut, fut,
_s: PhantomData, _s: PhantomData,
@ -492,7 +504,7 @@ impl<S: 'static> WaitingResponse<S> {
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> { fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
match self.fut.poll() { match self.fut.poll() {
Ok(Async::NotReady) => None, Ok(Async::NotReady) => None,
Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)), Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, resp)),
Err(err) => Some(RunMiddlewares::init(info, err.into())), Err(err) => Some(RunMiddlewares::init(info, err.into())),
} }
} }
@ -511,7 +523,7 @@ impl<S: 'static> RunMiddlewares<S> {
let len = info.mws.len(); let len = info.mws.len();
loop { loop {
let state = info.mws[curr].response(&mut info.req, resp); let state = info.mws[curr].response(&info.req, resp);
resp = match state { resp = match state {
Err(err) => { Err(err) => {
info.count = curr + 1; info.count = curr + 1;
@ -530,7 +542,7 @@ impl<S: 'static> RunMiddlewares<S> {
curr, curr,
fut: Some(fut), fut: Some(fut),
_s: PhantomData, _s: PhantomData,
}) });
} }
}; };
} }
@ -554,7 +566,7 @@ impl<S: 'static> RunMiddlewares<S> {
if self.curr == len { if self.curr == len {
return Some(FinishingMiddlewares::init(info, resp)); return Some(FinishingMiddlewares::init(info, resp));
} else { } else {
let state = info.mws[self.curr].response(&mut info.req, resp); let state = info.mws[self.curr].response(&info.req, resp);
match state { match state {
Err(err) => { Err(err) => {
return Some(FinishingMiddlewares::init(info, err.into())) return Some(FinishingMiddlewares::init(info, err.into()))
@ -625,7 +637,7 @@ impl<S: 'static> FinishingMiddlewares<S> {
info.count -= 1; info.count -= 1;
let state = info.mws[info.count as usize] let state = info.mws[info.count as usize]
.finish(&mut info.req, self.resp.as_ref().unwrap()); .finish(&info.req, self.resp.as_ref().unwrap());
match state { match state {
MiddlewareFinished::Done => { MiddlewareFinished::Done => {
if info.count == 0 { if info.count == 0 {

View file

@ -4,29 +4,133 @@ use std::rc::Rc;
use regex::{escape, Regex}; use regex::{escape, Regex};
use smallvec::SmallVec; use smallvec::SmallVec;
use url::Url;
use error::UrlGenerationError; use error::UrlGenerationError;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use param::ParamItem; use param::{ParamItem, Params};
use resource::ResourceHandler; use resource::ResourceHandler;
use server::ServerSettings; use server::Request;
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) enum RouterResource {
Notset,
Normal(u16),
}
/// Interface for application router. /// Interface for application router.
pub struct Router(Rc<Inner>); pub struct Router(Rc<Inner>);
#[derive(Clone)]
pub struct RouteInfo {
router: Rc<Inner>,
resource: RouterResource,
prefix: u16,
params: Params,
}
impl RouteInfo {
/// This method returns reference to matched `Resource` object.
#[inline]
pub fn resource(&self) -> Option<&Resource> {
if let RouterResource::Normal(idx) = self.resource {
Some(&self.router.patterns[idx as usize])
} else {
None
}
}
/// Get a reference to the Params object.
///
/// Params is a container for url parameters.
/// A variable segment is specified in the form `{identifier}`,
/// where the identifier can be used later in a request handler to
/// access the matched value for that segment.
#[inline]
pub fn match_info(&self) -> &Params {
&self.params
}
#[doc(hidden)]
#[inline]
pub fn prefix_len(&self) -> u16 {
self.prefix
}
#[inline]
pub(crate) fn merge(&self, mut params: Params) -> RouteInfo {
let mut p = self.params.clone();
p.set_tail(params.tail);
for item in &params.segments {
p.add(item.0.clone(), item.1.clone());
}
RouteInfo {
params: p,
router: self.router.clone(),
resource: self.resource,
prefix: self.prefix,
}
}
/// Generate url for named resource
///
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
/// url_for) for detailed information.
pub fn url_for<U, I>(
&self, req: &Request, name: &str, elements: U,
) -> Result<Url, UrlGenerationError>
where
U: IntoIterator<Item = I>,
I: AsRef<str>,
{
if let Some(pattern) = self.router.named.get(name) {
let path = pattern.0.resource_path(elements, &self.router.prefix)?;
if path.starts_with('/') {
let conn = req.connection_info();
Ok(Url::parse(&format!(
"{}://{}{}",
conn.scheme(),
conn.host(),
path
))?)
} else {
Ok(Url::parse(&path)?)
}
} else {
Err(UrlGenerationError::ResourceNotFound)
}
}
/// Check if application contains matching route.
///
/// This method does not take `prefix` into account.
/// For example if prefix is `/test` and router contains route `/name`,
/// following path would be recognizable `/test/name` but `has_route()` call
/// would return `false`.
pub fn has_route(&self, path: &str) -> bool {
let path = if path.is_empty() { "/" } else { path };
for pattern in &self.router.patterns {
if pattern.is_match(path) {
return true;
}
}
false
}
}
struct Inner { struct Inner {
prefix: String, prefix: String,
prefix_len: usize, prefix_len: usize,
named: HashMap<String, (Resource, bool)>, named: HashMap<String, (Resource, bool)>,
patterns: Vec<Resource>, patterns: Vec<Resource>,
srv: ServerSettings,
} }
impl Router { impl Router {
/// Create new router /// Create new router
pub fn new<S>( pub fn new<S>(
prefix: &str, settings: ServerSettings, prefix: &str, map: Vec<(Resource, Option<ResourceHandler<S>>)>,
map: Vec<(Resource, Option<ResourceHandler<S>>)>,
) -> (Router, Vec<ResourceHandler<S>>) { ) -> (Router, Vec<ResourceHandler<S>>) {
let prefix = prefix.trim().trim_right_matches('/').to_owned(); let prefix = prefix.trim().trim_right_matches('/').to_owned();
let mut named = HashMap::new(); let mut named = HashMap::new();
@ -52,7 +156,6 @@ impl Router {
prefix_len, prefix_len,
named, named,
patterns, patterns,
srv: settings,
})), })),
resources, resources,
) )
@ -64,67 +167,61 @@ impl Router {
&self.0.prefix &self.0.prefix
} }
/// Server settings
#[inline]
pub fn server_settings(&self) -> &ServerSettings {
&self.0.srv
}
pub(crate) fn get_resource(&self, idx: usize) -> &Resource { pub(crate) fn get_resource(&self, idx: usize) -> &Resource {
&self.0.patterns[idx] &self.0.patterns[idx]
} }
pub(crate) fn route_info(&self, req: &Request, prefix: u16) -> RouteInfo {
let mut params = Params::with_url(req.url());
params.set_tail(prefix);
RouteInfo {
params,
router: self.0.clone(),
resource: RouterResource::Notset,
prefix: 0,
}
}
pub(crate) fn route_info_params(&self, params: Params, prefix: u16) -> RouteInfo {
RouteInfo {
params,
prefix,
router: self.0.clone(),
resource: RouterResource::Notset,
}
}
pub(crate) fn default_route_info(&self, prefix: u16) -> RouteInfo {
RouteInfo {
prefix,
router: self.0.clone(),
resource: RouterResource::Notset,
params: Params::new(),
}
}
/// Query for matched resource /// Query for matched resource
pub fn recognize<S>(&self, req: &mut HttpRequest<S>) -> Option<usize> { pub fn recognize(&self, req: &Request) -> Option<(usize, RouteInfo)> {
if self.0.prefix_len > req.path().len() { if self.0.prefix_len > req.path().len() {
return None; return None;
} }
for (idx, pattern) in self.0.patterns.iter().enumerate() { for (idx, pattern) in self.0.patterns.iter().enumerate() {
if pattern.match_with_params(req, self.0.prefix_len, true) { if let Some(params) = pattern.match_with_params(req, self.0.prefix_len, true)
let url = req.url().clone(); {
req.match_info_mut().set_url(url); return Some((
req.set_resource(idx); idx,
req.set_prefix_len(self.0.prefix_len as u16); RouteInfo {
return Some(idx); params,
router: self.0.clone(),
resource: RouterResource::Normal(idx as u16),
prefix: self.0.prefix_len as u16,
},
));
} }
} }
None None
} }
/// Check if application contains matching route.
///
/// This method does not take `prefix` into account.
/// For example if prefix is `/test` and router contains route `/name`,
/// following path would be recognizable `/test/name` but `has_route()` call
/// would return `false`.
pub fn has_route(&self, path: &str) -> bool {
let path = if path.is_empty() { "/" } else { path };
for pattern in &self.0.patterns {
if pattern.is_match(path) {
return true;
}
}
false
}
/// Build named resource path.
///
/// Check [`HttpRequest::url_for()`](../struct.HttpRequest.html#method.
/// url_for) for detailed information.
pub fn resource_path<U, I>(
&self, name: &str, elements: U,
) -> Result<String, UrlGenerationError>
where
U: IntoIterator<Item = I>,
I: AsRef<str>,
{
if let Some(pattern) = self.0.named.get(name) {
pattern.0.resource_path(self, elements)
} else {
Err(UrlGenerationError::ResourceNotFound)
}
}
} }
impl Clone for Router { impl Clone for Router {
@ -160,7 +257,7 @@ pub enum ResourceType {
} }
/// Resource type describes an entry in resources table /// Resource type describes an entry in resources table
#[derive(Clone)] #[derive(Clone, Debug)]
pub struct Resource { pub struct Resource {
tp: PatternType, tp: PatternType,
rtp: ResourceType, rtp: ResourceType,
@ -208,9 +305,7 @@ impl Resource {
// actix creates one router per thread // actix creates one router per thread
let names = re let names = re
.capture_names() .capture_names()
.filter_map(|name| { .filter_map(|name| name.map(|name| Rc::new(name.to_owned())))
name.map(|name| Rc::new(name.to_owned()))
})
.collect(); .collect();
PatternType::Dynamic(re, names, len) PatternType::Dynamic(re, names, len)
} else if for_prefix { } else if for_prefix {
@ -253,127 +348,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<S>( pub fn match_with_params(
&self, req: &mut HttpRequest<S>, plen: usize, insert: bool, &self, req: &Request, plen: usize, insert: bool,
) -> bool { ) -> Option<Params> {
let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new(); let path = &req.path()[plen..];
if insert {
let names = { if path.is_empty() {
let path = &req.path()[plen..]; "/"
if insert {
if path.is_empty() {
"/"
} else {
path
}
} else { } else {
path 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 passed = false;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
segments.push(ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
));
}
}
names
} else {
return false;
}
}
PatternType::Prefix(ref s) => return path.starts_with(s),
} }
} else {
path
}; };
let len = req.path().len(); match self.tp {
let params = req.match_info_mut(); PatternType::Static(ref s) => if s != path {
params.set_tail(len as u16); None
for (idx, segment) in segments.into_iter().enumerate() { } else {
params.add(names[idx].clone(), segment); Some(Params::with_url(req.url()))
},
PatternType::Dynamic(ref re, ref names, _) => {
if let Some(captures) = re.captures(path) {
let mut params = Params::with_url(req.url());
let mut idx = 0;
let mut passed = false;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
params.add(
names[idx].clone(),
ParamItem::UrlSegment(
(plen + m.start()) as u16,
(plen + m.end()) as u16,
),
);
idx += 1;
}
}
params.set_tail(req.path().len() as u16);
Some(params)
} else {
None
}
}
PatternType::Prefix(ref s) => if !path.starts_with(s) {
None
} else {
Some(Params::with_url(req.url()))
},
} }
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<S>( pub fn match_prefix_with_params(
&self, req: &mut HttpRequest<S>, plen: usize, &self, req: &Request, plen: usize,
) -> Option<usize> { ) -> Option<Params> {
let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new(); let path = &req.path()[plen..];
let path = if path.is_empty() { "/" } else { path };
let (names, tail_len) = { match self.tp {
let path = &req.path()[plen..]; PatternType::Static(ref s) => if s == path {
let path = if path.is_empty() { "/" } else { path }; Some(Params::with_url(req.url()))
} else {
None
},
PatternType::Dynamic(ref re, ref names, len) => {
if let Some(captures) = re.captures(path) {
let mut params = Params::with_url(req.url());
let mut pos = 0;
let mut passed = false;
let mut idx = 0;
for capture in captures.iter() {
if let Some(ref m) = capture {
if !passed {
passed = true;
continue;
}
match self.tp { params.add(
PatternType::Static(ref s) => if s == path { names[idx].clone(),
return Some(s.len()); ParamItem::UrlSegment(
} else {
return None;
},
PatternType::Dynamic(ref re, ref names, len) => {
if let Some(captures) = re.captures(path) {
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.push(ParamItem::UrlSegment(
(plen + m.start()) as u16, (plen + m.start()) as u16,
(plen + m.end()) as u16, (plen + m.end()) as u16,
)); ),
pos = m.end(); );
} idx += 1;
pos = m.end();
} }
(names, 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
} }
params.set_tail((plen + pos + len) as u16);
Some(params)
} else {
None
} }
} }
}; PatternType::Prefix(ref s) => {
let len = if path == s {
let params = req.match_info_mut(); s.len()
params.set_tail(tail_len as u16); } else if path.starts_with(s)
for (idx, segment) in segments.into_iter().enumerate() { && (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/'))
params.add(names[idx].clone(), segment); {
if s.ends_with('/') {
s.len() - 1
} else {
s.len()
}
} else {
return None;
};
let mut params = Params::with_url(req.url());
params.set_tail((plen + len) as u16);
Some(params)
}
} }
Some(tail_len)
} }
/// Build resource path. /// Build resource path.
pub fn resource_path<U, I>( pub fn resource_path<U, I>(
&self, router: &Router, elements: U, &self, elements: U, prefix: &str,
) -> Result<String, UrlGenerationError> ) -> Result<String, UrlGenerationError>
where where
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
@ -402,7 +498,6 @@ impl Resource {
}; };
if self.rtp != ResourceType::External { if self.rtp != ResourceType::External {
let prefix = router.prefix();
if prefix.ends_with('/') { if prefix.ends_with('/') {
if path.starts_with('/') { if path.starts_with('/') {
path.insert_str(0, &prefix[..prefix.len() - 1]); path.insert_str(0, &prefix[..prefix.len() - 1]);
@ -546,44 +641,57 @@ mod tests {
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
]; ];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("", routes);
let mut req = TestRequest::with_uri("/name").finish(); let req = TestRequest::with_uri("/name").finish();
assert_eq!(rec.recognize(&mut req), Some(0)); assert_eq!(rec.recognize(&req).unwrap().0, 0);
assert!(req.match_info().is_empty()); assert!(req.match_info().is_empty());
let mut req = TestRequest::with_uri("/name/value").finish(); let req = TestRequest::with_uri("/name/value").finish();
assert_eq!(rec.recognize(&mut req), Some(1)); let info = rec.recognize(&req).unwrap().1;
let req = req.with_route_info(info);
assert_eq!(req.match_info().get("val").unwrap(), "value"); assert_eq!(req.match_info().get("val").unwrap(), "value");
assert_eq!(&req.match_info()["val"], "value"); assert_eq!(&req.match_info()["val"], "value");
let mut req = TestRequest::with_uri("/name/value2/index.html").finish(); let req = TestRequest::with_uri("/name/value2/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(2)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 2);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("val").unwrap(), "value2"); assert_eq!(req.match_info().get("val").unwrap(), "value2");
let mut req = TestRequest::with_uri("/file/file.gz").finish(); let req = TestRequest::with_uri("/file/file.gz").finish();
assert_eq!(rec.recognize(&mut req), Some(3)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 3);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("file").unwrap(), "file"); assert_eq!(req.match_info().get("file").unwrap(), "file");
assert_eq!(req.match_info().get("ext").unwrap(), "gz"); assert_eq!(req.match_info().get("ext").unwrap(), "gz");
let mut req = TestRequest::with_uri("/vtest/ttt/index.html").finish(); let req = TestRequest::with_uri("/vtest/ttt/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(4)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 4);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("val").unwrap(), "test"); assert_eq!(req.match_info().get("val").unwrap(), "test");
assert_eq!(req.match_info().get("val2").unwrap(), "ttt"); assert_eq!(req.match_info().get("val2").unwrap(), "ttt");
let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish(); let req = TestRequest::with_uri("/v/blah-blah/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(5)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 5);
let req = req.with_route_info(info.1);
assert_eq!( assert_eq!(
req.match_info().get("tail").unwrap(), req.match_info().get("tail").unwrap(),
"blah-blah/index.html" "blah-blah/index.html"
); );
let mut req = TestRequest::with_uri("/test2/index.html").finish(); let req = TestRequest::with_uri("/test2/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(6)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 6);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("test").unwrap(), "index"); assert_eq!(req.match_info().get("test").unwrap(), "index");
let mut req = TestRequest::with_uri("/bbb/index.html").finish(); let req = TestRequest::with_uri("/bbb/index.html").finish();
assert_eq!(rec.recognize(&mut req), Some(7)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 7);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("test").unwrap(), "bbb"); assert_eq!(req.match_info().get("test").unwrap(), "bbb");
} }
@ -599,13 +707,13 @@ mod tests {
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
]; ];
let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("", routes);
let mut req = TestRequest::with_uri("/index.json").finish(); let req = TestRequest::with_uri("/index.json").finish();
assert_eq!(rec.recognize(&mut req), Some(0)); assert_eq!(rec.recognize(&req).unwrap().0, 0);
let mut req = TestRequest::with_uri("/test.json").finish(); let req = TestRequest::with_uri("/test.json").finish();
assert_eq!(rec.recognize(&mut req), Some(1)); assert_eq!(rec.recognize(&req).unwrap().0, 1);
} }
#[test] #[test]
@ -617,16 +725,18 @@ mod tests {
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
]; ];
let (rec, _) = Router::new::<()>("/test", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("/test", routes);
let mut req = TestRequest::with_uri("/name").finish(); let req = TestRequest::with_uri("/name").finish();
assert!(rec.recognize(&mut req).is_none()); assert!(rec.recognize(&req).is_none());
let mut req = TestRequest::with_uri("/test/name").finish(); let req = TestRequest::with_uri("/test/name").finish();
assert_eq!(rec.recognize(&mut req), Some(0)); assert_eq!(rec.recognize(&req).unwrap().0, 0);
let mut req = TestRequest::with_uri("/test/name/value").finish(); let req = TestRequest::with_uri("/test/name/value").finish();
assert_eq!(rec.recognize(&mut req), Some(1)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 1);
let req = req.with_route_info(info.1);
assert_eq!(req.match_info().get("val").unwrap(), "value"); assert_eq!(req.match_info().get("val").unwrap(), "value");
assert_eq!(&req.match_info()["val"], "value"); assert_eq!(&req.match_info()["val"], "value");
@ -638,16 +748,18 @@ mod tests {
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
]; ];
let (rec, _) = Router::new::<()>("/test2", ServerSettings::default(), routes); let (rec, _) = Router::new::<()>("/test2", routes);
let mut req = TestRequest::with_uri("/name").finish(); let req = TestRequest::with_uri("/name").finish();
assert!(rec.recognize(&mut req).is_none()); assert!(rec.recognize(&req).is_none());
let mut req = TestRequest::with_uri("/test2/name").finish(); let req = TestRequest::with_uri("/test2/name").finish();
assert_eq!(rec.recognize(&mut req), Some(0)); assert_eq!(rec.recognize(&req).unwrap().0, 0);
let mut req = TestRequest::with_uri("/test2/name-test").finish(); let req = TestRequest::with_uri("/test2/name-test").finish();
assert!(rec.recognize(&mut req).is_none()); assert!(rec.recognize(&req).is_none());
let mut req = TestRequest::with_uri("/test2/name/ttt").finish(); let req = TestRequest::with_uri("/test2/name/ttt").finish();
assert_eq!(rec.recognize(&mut req), Some(1)); let info = rec.recognize(&req).unwrap();
assert_eq!(info.0, 1);
let req = req.with_route_info(info.1);
assert_eq!(&req.match_info()["val"], "ttt"); assert_eq!(&req.match_info()["val"], "ttt");
} }
@ -681,29 +793,23 @@ mod tests {
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"));
let mut req = TestRequest::with_uri("/user/profile").finish(); let req = TestRequest::with_uri("/user/profile").finish();
let url = req.url().clone(); let info = re.match_with_params(&req, 0, true).unwrap();
req.match_info_mut().set_url(url); assert_eq!(info.get("id").unwrap(), "profile");
assert!(re.match_with_params(&mut req, 0, true));
assert_eq!(req.match_info().get("id").unwrap(), "profile");
let mut req = TestRequest::with_uri("/user/1245125").finish(); let req = TestRequest::with_uri("/user/1245125").finish();
let url = req.url().clone(); let info = re.match_with_params(&req, 0, true).unwrap();
req.match_info_mut().set_url(url); assert_eq!(info.get("id").unwrap(), "1245125");
assert!(re.match_with_params(&mut req, 0, true));
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}");
assert!(re.is_match("/v1/resource/320120")); assert!(re.is_match("/v1/resource/320120"));
assert!(!re.is_match("/v/resource/1")); assert!(!re.is_match("/v/resource/1"));
assert!(!re.is_match("/resource")); assert!(!re.is_match("/resource"));
let mut req = TestRequest::with_uri("/v151/resource/adahg32").finish(); let req = TestRequest::with_uri("/v151/resource/adahg32").finish();
let url = req.url().clone(); let info = re.match_with_params(&req, 0, true).unwrap();
req.match_info_mut().set_url(url); assert_eq!(info.get("version").unwrap(), "151");
assert!(re.match_with_params(&mut req, 0, true)); assert_eq!(info.get("id").unwrap(), "adahg32");
assert_eq!(req.match_info().get("version").unwrap(), "151");
assert_eq!(req.match_info().get("id").unwrap(), "adahg32");
} }
#[test] #[test]
@ -728,18 +834,15 @@ mod tests {
assert!(re.is_match("/name/gs")); assert!(re.is_match("/name/gs"));
assert!(!re.is_match("/name")); assert!(!re.is_match("/name"));
let mut req = TestRequest::with_uri("/test2/").finish(); let req = TestRequest::with_uri("/test2/").finish();
let url = req.url().clone(); let info = re.match_with_params(&req, 0, true).unwrap();
req.match_info_mut().set_url(url); assert_eq!(&info["name"], "test2");
assert!(re.match_with_params(&mut req, 0, true)); assert_eq!(&info[0], "test2");
assert_eq!(&req.match_info()["name"], "test2");
let mut req = let req = TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish();
TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish(); let info = re.match_with_params(&req, 0, true).unwrap();
let url = req.url().clone(); assert_eq!(&info["name"], "test2");
req.match_info_mut().set_url(url); assert_eq!(&info[0], "test2");
assert!(re.match_with_params(&mut req, 0, true));
assert_eq!(&req.match_info()["name"], "test2");
} }
#[test] #[test]
@ -754,18 +857,18 @@ mod tests {
Some(ResourceHandler::default()), Some(ResourceHandler::default()),
), ),
]; ];
let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); let (router, _) = Router::new::<()>("", routes);
let mut req = let req = TestRequest::with_uri("/index.json").finish();
TestRequest::with_uri("/index.json").finish_with_router(router.clone()); assert_eq!(router.recognize(&req).unwrap().0, 0);
assert_eq!(router.recognize(&mut req), Some(0)); let info = router.recognize(&req).unwrap().1;
let resource = req.resource().unwrap(); let resource = info.resource().unwrap();
assert_eq!(resource.name(), "r1"); assert_eq!(resource.name(), "r1");
let mut req = let req = TestRequest::with_uri("/test.json").finish();
TestRequest::with_uri("/test.json").finish_with_router(router.clone()); assert_eq!(router.recognize(&req).unwrap().0, 1);
assert_eq!(router.recognize(&mut req), Some(1)); let info = router.recognize(&req).unwrap().1;
let resource = req.resource().unwrap(); let resource = info.resource().unwrap();
assert_eq!(resource.name(), "r2"); assert_eq!(resource.name(), "r2");
} }
} }

View file

@ -14,8 +14,9 @@ use middleware::{
Started as MiddlewareStarted, Started as MiddlewareStarted,
}; };
use pred::Predicate; use pred::Predicate;
use resource::ResourceHandler; use resource::{ResourceHandler, RouteId};
use router::Resource; use router::Resource;
use server::Request;
type ScopeResource<S> = Rc<ResourceHandler<S>>; type ScopeResource<S> = Rc<ResourceHandler<S>>;
type Route<S> = Box<RouteHandler<S>>; type Route<S> = Box<RouteHandler<S>>;
@ -114,7 +115,7 @@ impl<S: 'static> Scope<S> {
/// ///
/// struct AppState; /// struct AppState;
/// ///
/// fn index(req: HttpRequest<AppState>) -> &'static str { /// fn index(req: &HttpRequest<AppState>) -> &'static str {
/// "Welcome!" /// "Welcome!"
/// } /// }
/// ///
@ -159,7 +160,7 @@ impl<S: 'static> Scope<S> {
/// ///
/// struct AppState; /// struct AppState;
/// ///
/// fn index(req: HttpRequest<AppState>) -> &'static str { /// fn index(req: &HttpRequest<AppState>) -> &'static str {
/// "Welcome!" /// "Welcome!"
/// } /// }
/// ///
@ -334,75 +335,61 @@ impl<S: 'static> Scope<S> {
} }
impl<S: 'static> RouteHandler<S> for Scope<S> { impl<S: 'static> RouteHandler<S> for Scope<S> {
fn handle(&self, mut req: HttpRequest<S>) -> AsyncResult<HttpResponse> { fn handle(&self, req: &HttpRequest<S>) -> AsyncResult<HttpResponse> {
let tail = req.match_info().tail as usize; 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(&mut req, tail, false) { if let Some(params) = pattern.match_with_params(req, tail, false) {
if self.middlewares.is_empty() { let req2 = req.with_route_info(req.route().merge(params));
return match resource.handle(req) { if let Some(id) = resource.get_route_id(&req2) {
Ok(result) => result, if self.middlewares.is_empty() {
Err(req) => { return resource.handle(id, &req2);
if let Some(ref default) = self.default { } else {
match default.handle(req) { return AsyncResult::async(Box::new(Compose::new(
Ok(result) => result, id,
Err(_) => AsyncResult::ok(HttpResponse::new( req2,
StatusCode::NOT_FOUND, Rc::clone(&self.middlewares),
)), Rc::clone(&resource),
} )));
} else { }
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
}
}
};
} else {
return AsyncResult::async(Box::new(Compose::new(
req,
Rc::clone(&self.middlewares),
Rc::clone(&resource),
)));
} }
} }
} }
// nested scopes // nested scopes
let len = req.prefix_len() as usize; let len = req.route().prefix_len() as usize;
'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) = prefix.match_prefix_with_params(&mut req, len) { if let Some(params) = prefix.match_prefix_with_params(req, tail) {
let req2 = req.with_route_info(req.route().merge(params));
let state = req.state();
for filter in filters { for filter in filters {
if !filter.check(&mut req) { if !filter.check(&req2, state) {
continue 'outer; continue 'outer;
} }
} }
let url = req.url().clone(); return handler.handle(&req2);
let prefix_len = (len + prefix_len) as u16;
req.set_prefix_len(prefix_len);
req.match_info_mut().set_tail(prefix_len);
req.match_info_mut().set_url(url);
return handler.handle(req);
} }
} }
// default handler // default handler
if self.middlewares.is_empty() { if let Some(ref resource) = self.default {
if let Some(ref default) = self.default { if let Some(id) = resource.get_route_id(req) {
match default.handle(req) { if self.middlewares.is_empty() {
Ok(result) => result, return resource.handle(id, req);
Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), } else {
return AsyncResult::async(Box::new(Compose::new(
id,
req.clone(),
Rc::clone(&self.middlewares),
Rc::clone(resource),
)));
} }
} else {
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
} }
} else if let Some(ref default) = self.default {
AsyncResult::async(Box::new(Compose::new(
req,
Rc::clone(&self.middlewares),
Rc::clone(default),
)))
} else {
unimplemented!()
} }
AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND))
} }
fn has_default_resource(&self) -> bool { fn has_default_resource(&self) -> bool {
@ -420,8 +407,9 @@ struct Wrapper<S: 'static> {
} }
impl<S: 'static, S2: 'static> RouteHandler<S2> for Wrapper<S> { impl<S: 'static, S2: 'static> RouteHandler<S2> for Wrapper<S> {
fn handle(&self, req: HttpRequest<S2>) -> AsyncResult<HttpResponse> { fn handle(&self, req: &HttpRequest<S2>) -> AsyncResult<HttpResponse> {
self.scope.handle(req.change_state(Rc::clone(&self.state))) let req = req.with_state(Rc::clone(&self.state));
self.scope.handle(&req)
} }
} }
@ -431,10 +419,9 @@ struct FiltersWrapper<S: 'static> {
} }
impl<S: 'static, S2: 'static> Predicate<S2> for FiltersWrapper<S> { impl<S: 'static, S2: 'static> Predicate<S2> for FiltersWrapper<S> {
fn check(&self, req: &mut HttpRequest<S2>) -> bool { fn check(&self, req: &Request, _: &S2) -> bool {
let mut req = req.change_state(Rc::clone(&self.state));
for filter in &self.filters { for filter in &self.filters {
if !filter.check(&mut req) { if !filter.check(&req, &self.state) {
return false; return false;
} }
} }
@ -450,6 +437,7 @@ struct Compose<S: 'static> {
struct ComposeInfo<S: 'static> { struct ComposeInfo<S: 'static> {
count: usize, count: usize,
id: RouteId,
req: HttpRequest<S>, req: HttpRequest<S>,
mws: Rc<Vec<Box<Middleware<S>>>>, mws: Rc<Vec<Box<Middleware<S>>>>,
resource: Rc<ResourceHandler<S>>, resource: Rc<ResourceHandler<S>>,
@ -477,14 +465,15 @@ impl<S: 'static> ComposeState<S> {
impl<S: 'static> Compose<S> { impl<S: 'static> Compose<S> {
fn new( fn new(
req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>, id: RouteId, req: HttpRequest<S>, mws: Rc<Vec<Box<Middleware<S>>>>,
resource: Rc<ResourceHandler<S>>, resource: Rc<ResourceHandler<S>>,
) -> Self { ) -> Self {
let mut info = ComposeInfo { let mut info = ComposeInfo {
count: 0, id,
req,
mws, mws,
req,
resource, resource,
count: 0,
}; };
let state = StartMiddlewares::init(&mut info); let state = StartMiddlewares::init(&mut info);
@ -522,27 +511,27 @@ type Fut = Box<Future<Item = Option<HttpResponse>, Error = Error>>;
impl<S: 'static> StartMiddlewares<S> { impl<S: 'static> StartMiddlewares<S> {
fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> { fn init(info: &mut ComposeInfo<S>) -> ComposeState<S> {
let len = info.mws.len(); let len = info.mws.len();
loop { loop {
if info.count == len { if info.count == len {
let reply = { let reply = info.resource.handle(info.id, &info.req);
let req = info.req.clone();
info.resource.handle(req).unwrap()
};
return WaitingResponse::init(info, reply); return WaitingResponse::init(info, reply);
} else { } else {
let state = info.mws[info.count].start(&mut info.req); let result = info.mws[info.count].start(&info.req);
match state { match result {
Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => { Ok(MiddlewareStarted::Response(resp)) => {
return RunMiddlewares::init(info, resp) return RunMiddlewares::init(info, resp);
} }
Ok(MiddlewareStarted::Future(fut)) => { Ok(MiddlewareStarted::Future(fut)) => {
return ComposeState::Starting(StartMiddlewares { return ComposeState::Starting(StartMiddlewares {
fut: Some(fut), fut: Some(fut),
_s: PhantomData, _s: PhantomData,
}) });
}
Err(err) => {
return RunMiddlewares::init(info, err.into());
} }
Err(err) => return RunMiddlewares::init(info, err.into()),
} }
} }
} }
@ -550,24 +539,25 @@ impl<S: 'static> StartMiddlewares<S> {
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> { fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
let len = info.mws.len(); let len = info.mws.len();
'outer: loop { 'outer: loop {
match self.fut.as_mut().unwrap().poll() { match self.fut.as_mut().unwrap().poll() {
Ok(Async::NotReady) => return None, Ok(Async::NotReady) => {
return None;
}
Ok(Async::Ready(resp)) => { Ok(Async::Ready(resp)) => {
info.count += 1; info.count += 1;
if let Some(resp) = resp { if let Some(resp) = resp {
return Some(RunMiddlewares::init(info, resp)); return Some(RunMiddlewares::init(info, resp));
} }
loop { loop {
if info.count == len { if info.count == len {
let reply = { let reply = { info.resource.handle(info.id, &info.req) };
let req = info.req.clone();
info.resource.handle(req).unwrap()
};
return Some(WaitingResponse::init(info, reply)); return Some(WaitingResponse::init(info, reply));
} else { } else {
let state = info.mws[info.count].start(&mut info.req); let result = info.mws[info.count].start(&info.req);
match state { match result {
Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Done) => info.count += 1,
Ok(MiddlewareStarted::Response(resp)) => { Ok(MiddlewareStarted::Response(resp)) => {
return Some(RunMiddlewares::init(info, resp)); return Some(RunMiddlewares::init(info, resp));
@ -577,13 +567,15 @@ impl<S: 'static> StartMiddlewares<S> {
continue 'outer; continue 'outer;
} }
Err(err) => { Err(err) => {
return Some(RunMiddlewares::init(info, err.into())) return Some(RunMiddlewares::init(info, err.into()));
} }
} }
} }
} }
} }
Err(err) => return Some(RunMiddlewares::init(info, err.into())), Err(err) => {
return Some(RunMiddlewares::init(info, err.into()));
}
} }
} }
} }
@ -613,7 +605,7 @@ impl<S: 'static> WaitingResponse<S> {
fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> { fn poll(&mut self, info: &mut ComposeInfo<S>) -> Option<ComposeState<S>> {
match self.fut.poll() { match self.fut.poll() {
Ok(Async::NotReady) => None, Ok(Async::NotReady) => None,
Ok(Async::Ready(response)) => Some(RunMiddlewares::init(info, response)), Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, resp)),
Err(err) => Some(RunMiddlewares::init(info, err.into())), Err(err) => Some(RunMiddlewares::init(info, err.into())),
} }
} }
@ -632,7 +624,7 @@ impl<S: 'static> RunMiddlewares<S> {
let len = info.mws.len(); let len = info.mws.len();
loop { loop {
let state = info.mws[curr].response(&mut info.req, resp); let state = info.mws[curr].response(&info.req, resp);
resp = match state { resp = match state {
Err(err) => { Err(err) => {
info.count = curr + 1; info.count = curr + 1;
@ -651,7 +643,7 @@ impl<S: 'static> RunMiddlewares<S> {
curr, curr,
fut: Some(fut), fut: Some(fut),
_s: PhantomData, _s: PhantomData,
}) });
} }
}; };
} }
@ -675,7 +667,7 @@ impl<S: 'static> RunMiddlewares<S> {
if self.curr == len { if self.curr == len {
return Some(FinishingMiddlewares::init(info, resp)); return Some(FinishingMiddlewares::init(info, resp));
} else { } else {
let state = info.mws[self.curr].response(&mut info.req, resp); let state = info.mws[self.curr].response(&info.req, resp);
match state { match state {
Err(err) => { Err(err) => {
return Some(FinishingMiddlewares::init(info, err.into())) return Some(FinishingMiddlewares::init(info, err.into()))
@ -745,7 +737,7 @@ impl<S: 'static> FinishingMiddlewares<S> {
info.count -= 1; info.count -= 1;
let state = info.mws[info.count as usize] let state = info.mws[info.count as usize]
.finish(&mut info.req, self.resp.as_ref().unwrap()); .finish(&info.req, self.resp.as_ref().unwrap());
match state { match state {
MiddlewareFinished::Done => { MiddlewareFinished::Done => {
if info.count == 0 { if info.count == 0 {
@ -794,7 +786,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/path1").finish(); let req = TestRequest::with_uri("/app/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -809,11 +801,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app").finish(); let req = TestRequest::with_uri("/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/").finish(); let req = TestRequest::with_uri("/app/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
@ -826,11 +818,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app").finish(); let req = TestRequest::with_uri("/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").finish(); let req = TestRequest::with_uri("/app/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -843,11 +835,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app").finish(); let req = TestRequest::with_uri("/app").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/").finish(); let req = TestRequest::with_uri("/app/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -866,19 +858,19 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/path1").finish(); let req = TestRequest::with_uri("/app/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::DELETE) .method(Method::DELETE)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::POST) .method(Method::POST)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -895,13 +887,13 @@ mod tests {
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::POST) .method(Method::POST)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/path1") let req = TestRequest::with_uri("/app/path1")
.method(Method::GET) .method(Method::GET)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -919,7 +911,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/ab-project1/path1").finish(); let req = TestRequest::with_uri("/ab-project1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
@ -931,7 +923,7 @@ mod tests {
_ => panic!(), _ => panic!(),
} }
let req = TestRequest::with_uri("/aa-project1/path1").finish(); let req = TestRequest::with_uri("/aa-project1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -948,7 +940,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1/path1").finish(); let req = TestRequest::with_uri("/app/t1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
@ -967,11 +959,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1").finish(); let req = TestRequest::with_uri("/app/t1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/t1/").finish(); let req = TestRequest::with_uri("/app/t1/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
@ -988,11 +980,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1").finish(); let req = TestRequest::with_uri("/app/t1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/").finish(); let req = TestRequest::with_uri("/app/t1/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -1009,11 +1001,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1").finish(); let req = TestRequest::with_uri("/app/t1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/").finish(); let req = TestRequest::with_uri("/app/t1/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -1034,13 +1026,13 @@ mod tests {
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::POST) .method(Method::POST)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::GET) .method(Method::GET)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -1055,7 +1047,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1/path1").finish(); let req = TestRequest::with_uri("/app/t1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
@ -1072,11 +1064,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/t1").finish(); let req = TestRequest::with_uri("/app/t1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
let req = TestRequest::with_uri("/app/t1/").finish(); let req = TestRequest::with_uri("/app/t1/").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
} }
@ -1095,13 +1087,13 @@ mod tests {
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::POST) .method(Method::POST)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
let req = TestRequest::with_uri("/app/t1/path1") let req = TestRequest::with_uri("/app/t1/path1")
.method(Method::GET) .method(Method::GET)
.finish(); .request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().status(), StatusCode::OK);
} }
@ -1123,7 +1115,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/project_1/path1").finish(); let req = TestRequest::with_uri("/app/project_1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
@ -1156,7 +1148,7 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/test/1/path1").finish(); let req = TestRequest::with_uri("/app/test/1/path1").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::CREATED); assert_eq!(resp.as_msg().status(), StatusCode::CREATED);
@ -1168,7 +1160,7 @@ mod tests {
_ => panic!(), _ => panic!(),
} }
let req = TestRequest::with_uri("/app/test/1/path2").finish(); let req = TestRequest::with_uri("/app/test/1/path2").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -1183,11 +1175,11 @@ mod tests {
}) })
.finish(); .finish();
let req = TestRequest::with_uri("/app/path2").finish(); let req = TestRequest::with_uri("/app/path2").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST); assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/path2").finish(); let req = TestRequest::with_uri("/path2").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND);
} }
@ -1202,15 +1194,15 @@ mod tests {
.default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed()))
.finish(); .finish();
let req = TestRequest::with_uri("/non-exist").finish(); let req = TestRequest::with_uri("/non-exist").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
let req = TestRequest::with_uri("/app1/non-exist").finish(); let req = TestRequest::with_uri("/app1/non-exist").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST); assert_eq!(resp.as_msg().status(), StatusCode::BAD_REQUEST);
let req = TestRequest::with_uri("/app2/non-exist").finish(); let req = TestRequest::with_uri("/app2/non-exist").request();
let resp = app.run(req); let resp = app.run(req);
assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED);
} }

25
src/server/error.rs Normal file
View file

@ -0,0 +1,25 @@
use futures::{Async, Poll};
use super::{helpers, HttpHandlerTask, Writer};
use http::{StatusCode, Version};
use httpresponse::HttpResponse;
use Error;
pub(crate) struct ServerError(Version, StatusCode);
impl ServerError {
pub fn err(ver: Version, status: StatusCode) -> Box<HttpHandlerTask> {
Box::new(ServerError(ver, status))
}
}
impl HttpHandlerTask for ServerError {
fn poll_io(&mut self, io: &mut Writer) -> Poll<bool, Error> {
{
let mut bytes = io.buffer();
helpers::write_status_line(self.0, self.1.as_u16(), bytes);
}
io.set_date();
Ok(Async::Ready(true))
}
}

View file

@ -8,11 +8,13 @@ use futures::{Async, Future, Poll};
use tokio_timer::Delay; use tokio_timer::Delay;
use error::{Error, PayloadError}; use error::{Error, PayloadError};
use http::{StatusCode, Version};
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use payload::{Payload, PayloadStatus, PayloadWriter}; use payload::{Payload, PayloadStatus, PayloadWriter};
use pipeline::Pipeline; use pipeline::Pipeline;
use super::error::ServerError;
use super::h1decoder::{DecoderError, H1Decoder, Message}; use super::h1decoder::{DecoderError, H1Decoder, Message};
use super::h1writer::H1Writer; use super::h1writer::H1Writer;
use super::input::PayloadType; use super::input::PayloadType;
@ -180,9 +182,7 @@ where
&& self.tasks.len() < MAX_PIPELINED_MESSAGES && self.tasks.len() < MAX_PIPELINED_MESSAGES
&& self.can_read() && self.can_read()
{ {
let res = self.stream.get_mut().read_available(&mut self.buf); match self.stream.get_mut().read_available(&mut self.buf) {
match res {
//self.stream.get_mut().read_available(&mut self.buf) {
Ok(Async::Ready(disconnected)) => { Ok(Async::Ready(disconnected)) => {
if disconnected { if disconnected {
// notify all tasks // notify all tasks
@ -363,22 +363,19 @@ where
if payload { if payload {
let (ps, pl) = Payload::new(false); let (ps, pl) = Payload::new(false);
msg.get_mut().payload = Some(pl); *msg.inner.payload.borrow_mut() = Some(pl);
self.payload = self.payload = Some(PayloadType::new(&msg.inner.headers, ps));
Some(PayloadType::new(&msg.get_ref().headers, ps));
} }
let mut req = HttpRequest::from_message(msg);
// set remote addr // set remote addr
req.set_peer_addr(self.addr); msg.inner.addr = self.addr;
// stop keepalive timer // stop keepalive timer
self.keepalive_timer.take(); self.keepalive_timer.take();
// search handler for request // search handler for request
for h in self.settings.handlers().iter_mut() { for h in self.settings.handlers().iter_mut() {
req = match h.handle(req) { msg = match h.handle(msg) {
Ok(mut pipe) => { Ok(mut pipe) => {
if self.tasks.is_empty() { if self.tasks.is_empty() {
match pipe.poll_io(&mut self.stream) { match pipe.poll_io(&mut self.stream) {
@ -415,15 +412,16 @@ where
}); });
continue 'outer; continue 'outer;
} }
Err(req) => req, Err(msg) => msg,
} }
} }
// handler is not found // handler is not found
self.tasks.push_back(Entry { self.tasks.push_back(Entry {
pipe: EntryPipe::Error( pipe: EntryPipe::Error(ServerError::err(
Pipeline::error(HttpResponse::NotFound()), Version::HTTP_11,
), StatusCode::NOT_FOUND,
)),
flags: EntryFlags::empty(), flags: EntryFlags::empty(),
}); });
} }
@ -475,12 +473,11 @@ mod tests {
use application::HttpApplication; use application::HttpApplication;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use server::h1decoder::Message; use server::h1decoder::Message;
use server::helpers::SharedHttpInnerMessage; use server::settings::{ServerSettings, WorkerSettings};
use server::settings::WorkerSettings; use server::{KeepAlive, Request};
use server::KeepAlive;
impl Message { impl Message {
fn message(self) -> SharedHttpInnerMessage { fn message(self) -> Request {
match self { match self {
Message::Message { msg, payload: _ } => msg, Message::Message { msg, payload: _ } => msg,
_ => panic!("error"), _ => panic!("error"),
@ -509,9 +506,9 @@ mod tests {
macro_rules! parse_ready { macro_rules! parse_ready {
($e:expr) => {{ ($e:expr) => {{
let settings: WorkerSettings<HttpApplication> = let settings: WorkerSettings<HttpApplication> =
WorkerSettings::new(Vec::new(), KeepAlive::Os); WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default());
match H1Decoder::new().decode($e, &settings) { match H1Decoder::new().decode($e, &settings) {
Ok(Some(msg)) => HttpRequest::from_message(msg.message()), Ok(Some(msg)) => msg.message(),
Ok(_) => unreachable!("Eof during parsing http request"), Ok(_) => unreachable!("Eof during parsing http request"),
Err(err) => unreachable!("Error during parsing http request: {:?}", err), Err(err) => unreachable!("Error during parsing http request: {:?}", err),
} }
@ -521,7 +518,7 @@ mod tests {
macro_rules! expect_parse_err { macro_rules! expect_parse_err {
($e:expr) => {{ ($e:expr) => {{
let settings: WorkerSettings<HttpApplication> = let settings: WorkerSettings<HttpApplication> =
WorkerSettings::new(Vec::new(), KeepAlive::Os); WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default());
match H1Decoder::new().decode($e, &settings) { match H1Decoder::new().decode($e, &settings) {
Err(err) => match err { Err(err) => match err {
@ -605,6 +602,7 @@ mod tests {
let settings = Rc::new(WorkerSettings::<HttpApplication>::new( let settings = Rc::new(WorkerSettings::<HttpApplication>::new(
Vec::new(), Vec::new(),
KeepAlive::Os, KeepAlive::Os,
ServerSettings::default(),
)); ));
let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf); let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf);
@ -620,6 +618,7 @@ mod tests {
let settings = Rc::new(WorkerSettings::<HttpApplication>::new( let settings = Rc::new(WorkerSettings::<HttpApplication>::new(
Vec::new(), Vec::new(),
KeepAlive::Os, KeepAlive::Os,
ServerSettings::default(),
)); ));
let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf); let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf);
@ -631,12 +630,16 @@ mod tests {
#[test] #[test]
fn test_parse() { fn test_parse() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n"); let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET); assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -648,7 +651,11 @@ mod tests {
#[test] #[test]
fn test_parse_partial() { fn test_parse_partial() {
let mut buf = BytesMut::from("PUT /test HTTP/1"); let mut buf = BytesMut::from("PUT /test HTTP/1");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
@ -659,7 +666,7 @@ mod tests {
buf.extend(b".1\r\n\r\n"); buf.extend(b".1\r\n\r\n");
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let mut req = HttpRequest::from_message(msg.message()); let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::PUT); assert_eq!(*req.method(), Method::PUT);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -671,12 +678,16 @@ mod tests {
#[test] #[test]
fn test_parse_post() { fn test_parse_post() {
let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n"); let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let mut req = HttpRequest::from_message(msg.message()); let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_10); assert_eq!(req.version(), Version::HTTP_10);
assert_eq!(*req.method(), Method::POST); assert_eq!(*req.method(), Method::POST);
assert_eq!(req.path(), "/test2"); assert_eq!(req.path(), "/test2");
@ -689,12 +700,16 @@ mod tests {
fn test_parse_body() { fn test_parse_body() {
let mut buf = let mut buf =
BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let mut req = HttpRequest::from_message(msg.message()); let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET); assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -716,12 +731,16 @@ mod tests {
fn test_parse_body_crlf() { fn test_parse_body_crlf() {
let mut buf = let mut buf =
BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let mut req = HttpRequest::from_message(msg.message()); let mut req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET); assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -742,14 +761,18 @@ mod tests {
#[test] #[test]
fn test_parse_partial_eof() { fn test_parse_partial_eof() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n"); let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
assert!(reader.decode(&mut buf, &settings).unwrap().is_none()); assert!(reader.decode(&mut buf, &settings).unwrap().is_none());
buf.extend(b"\r\n"); buf.extend(b"\r\n");
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET); assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -761,7 +784,11 @@ mod tests {
#[test] #[test]
fn test_headers_split_field() { fn test_headers_split_field() {
let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n"); let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n");
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() } assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() }
@ -775,7 +802,7 @@ mod tests {
buf.extend(b"t: value\r\n\r\n"); buf.extend(b"t: value\r\n\r\n");
match reader.decode(&mut buf, &settings) { match reader.decode(&mut buf, &settings) {
Ok(Some(msg)) => { Ok(Some(msg)) => {
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert_eq!(req.version(), Version::HTTP_11); assert_eq!(req.version(), Version::HTTP_11);
assert_eq!(*req.method(), Method::GET); assert_eq!(*req.method(), Method::GET);
assert_eq!(req.path(), "/test"); assert_eq!(req.path(), "/test");
@ -792,10 +819,14 @@ mod tests {
Set-Cookie: c1=cookie1\r\n\ Set-Cookie: c1=cookie1\r\n\
Set-Cookie: c2=cookie2\r\n\r\n", Set-Cookie: c2=cookie2\r\n\r\n",
); );
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
let val: Vec<_> = req let val: Vec<_> = req
.headers() .headers()
@ -988,7 +1019,11 @@ mod tests {
#[test] #[test]
fn test_http_request_upgrade() { fn test_http_request_upgrade() {
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut buf = BytesMut::from( let mut buf = BytesMut::from(
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
connection: upgrade\r\n\ connection: upgrade\r\n\
@ -998,7 +1033,7 @@ mod tests {
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert!(!req.keep_alive()); assert!(!req.keep_alive());
assert!(req.upgrade()); assert!(req.upgrade());
assert_eq!( assert_eq!(
@ -1054,12 +1089,16 @@ mod tests {
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n", transfer-encoding: chunked\r\n\r\n",
); );
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert!(req.chunked().unwrap()); assert!(req.chunked().unwrap());
buf.extend(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); buf.extend(b"4\r\ndata\r\n4\r\nline\r\n0\r\n\r\n");
@ -1090,11 +1129,15 @@ mod tests {
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n", transfer-encoding: chunked\r\n\r\n",
); );
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert!(req.chunked().unwrap()); assert!(req.chunked().unwrap());
buf.extend( buf.extend(
@ -1112,7 +1155,7 @@ mod tests {
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req2 = HttpRequest::from_message(msg.message()); let req2 = msg.message();
assert!(req2.chunked().unwrap()); assert!(req2.chunked().unwrap());
assert_eq!(*req2.method(), Method::POST); assert_eq!(*req2.method(), Method::POST);
assert!(req2.chunked().unwrap()); assert!(req2.chunked().unwrap());
@ -1124,12 +1167,16 @@ mod tests {
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n", transfer-encoding: chunked\r\n\r\n",
); );
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req = HttpRequest::from_message(msg.message()); let req = msg.message();
assert!(req.chunked().unwrap()); assert!(req.chunked().unwrap());
buf.extend(b"4\r\n1111\r\n"); buf.extend(b"4\r\n1111\r\n");
@ -1171,13 +1218,16 @@ mod tests {
&"GET /test HTTP/1.1\r\n\ &"GET /test HTTP/1.1\r\n\
transfer-encoding: chunked\r\n\r\n"[..], transfer-encoding: chunked\r\n\r\n"[..],
); );
let settings = WorkerSettings::<HttpApplication>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<HttpApplication>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut reader = H1Decoder::new(); let mut reader = H1Decoder::new();
let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap();
assert!(msg.is_payload()); assert!(msg.is_payload());
let req = HttpRequest::from_message(msg.message()); assert!(msg.message().chunked().unwrap());
assert!(req.chunked().unwrap());
buf.extend(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); // test: test\r\n\r\n") buf.extend(b"4;test\r\ndata\r\n4\r\nline\r\n0\r\n\r\n"); // test: test\r\n\r\n")
let chunk = reader.decode(&mut buf, &settings).unwrap().unwrap().chunk(); let chunk = reader.decode(&mut buf, &settings).unwrap().unwrap().chunk();

View file

@ -4,12 +4,11 @@ use bytes::{Bytes, BytesMut};
use futures::{Async, Poll}; use futures::{Async, Poll};
use httparse; use httparse;
use super::helpers::SharedHttpInnerMessage; use super::message::{MessageFlags, Request};
use super::settings::WorkerSettings; use super::settings::WorkerSettings;
use error::ParseError; use error::ParseError;
use http::header::{HeaderName, HeaderValue}; use http::header::{HeaderName, HeaderValue};
use http::{header, HttpTryFrom, Method, Uri, Version}; use http::{header, HttpTryFrom, Method, Uri, Version};
use httprequest::MessageFlags;
use uri::Url; use uri::Url;
const MAX_BUFFER_SIZE: usize = 131_072; const MAX_BUFFER_SIZE: usize = 131_072;
@ -20,10 +19,7 @@ pub(crate) struct H1Decoder {
} }
pub(crate) enum Message { pub(crate) enum Message {
Message { Message { msg: Request, payload: bool },
msg: SharedHttpInnerMessage,
payload: bool,
},
Chunk(Bytes), Chunk(Bytes),
Eof, Eof,
} }
@ -84,7 +80,7 @@ impl H1Decoder {
fn parse_message<H>( fn parse_message<H>(
&self, buf: &mut BytesMut, settings: &WorkerSettings<H>, &self, buf: &mut BytesMut, settings: &WorkerSettings<H>,
) -> Poll<(SharedHttpInnerMessage, Option<EncodingDecoder>), ParseError> { ) -> Poll<(Request, Option<EncodingDecoder>), ParseError> {
// Parse http message // Parse http message
let mut has_upgrade = false; let mut has_upgrade = false;
let mut chunked = false; let mut chunked = false;
@ -122,11 +118,12 @@ impl H1Decoder {
let slice = buf.split_to(len).freeze(); let slice = buf.split_to(len).freeze();
// convert headers // convert headers
let mut msg = settings.get_http_message(); let mut msg = settings.get_request_context();
{ {
let msg_mut = msg.get_mut(); let inner = &mut msg.inner;
msg_mut inner
.flags .flags
.get_mut()
.set(MessageFlags::KEEPALIVE, version != Version::HTTP_10); .set(MessageFlags::KEEPALIVE, version != Version::HTTP_10);
for idx in headers[..headers_len].iter() { for idx in headers[..headers_len].iter() {
@ -177,20 +174,20 @@ impl H1Decoder {
} else { } else {
false false
}; };
msg_mut.flags.set(MessageFlags::KEEPALIVE, ka); inner.flags.get_mut().set(MessageFlags::KEEPALIVE, ka);
} }
_ => (), _ => (),
} }
msg_mut.headers.append(name, value); inner.headers.append(name, value);
} else { } else {
return Err(ParseError::Header); return Err(ParseError::Header);
} }
} }
msg_mut.url = path; inner.url = path;
msg_mut.method = method; inner.method = method;
msg_mut.version = version; inner.version = version;
} }
msg msg
}; };
@ -202,7 +199,7 @@ impl H1Decoder {
} else if let Some(len) = content_length { } else if let Some(len) = content_length {
// Content-Length // Content-Length
Some(EncodingDecoder::length(len)) Some(EncodingDecoder::length(len))
} else if has_upgrade || msg.get_ref().method == Method::CONNECT { } else if has_upgrade || msg.inner.method == Method::CONNECT {
// upgrade(websocket) or connect // upgrade(websocket) or connect
Some(EncodingDecoder::eof()) Some(EncodingDecoder::eof())
} else { } else {

View file

@ -7,14 +7,16 @@ use std::rc::Rc;
use tokio_io::AsyncWrite; use tokio_io::AsyncWrite;
use super::helpers; use super::helpers;
use super::output::Output; use super::output::{Output, ResponseInfo, ResponseLength};
use super::settings::WorkerSettings; use super::settings::WorkerSettings;
use super::Request;
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE}; use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
use body::{Binary, Body}; use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE}; use http::header::{
HeaderValue, CONNECTION, CONTENT_ENCODING, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
};
use http::{Method, Version}; use http::{Method, Version};
use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
@ -101,8 +103,8 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
} }
#[inline] #[inline]
fn set_date(&self, dst: &mut BytesMut) { fn set_date(&mut self) {
self.settings.set_date(dst, true) self.settings.set_date(self.buffer.as_mut(), true)
} }
#[inline] #[inline]
@ -111,11 +113,11 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
} }
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, &mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding,
encoding: ContentEncoding,
) -> io::Result<WriterState> { ) -> io::Result<WriterState> {
// prepare task // prepare task
self.buffer.for_server(req, msg, encoding); let mut info = ResponseInfo::new(req.inner.method == Method::HEAD);
self.buffer.for_server(&mut info, &req.inner, msg, encoding);
if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) { if msg.keep_alive().unwrap_or_else(|| req.keep_alive()) {
self.flags = Flags::STARTED | Flags::KEEPALIVE; self.flags = Flags::STARTED | Flags::KEEPALIVE;
} else { } else {
@ -123,7 +125,7 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
} }
// Connection upgrade // Connection upgrade
let version = msg.version().unwrap_or_else(|| req.version); let version = msg.version().unwrap_or_else(|| req.inner.version);
if msg.upgrade() { if msg.upgrade() {
self.flags.insert(Flags::UPGRADE); self.flags.insert(Flags::UPGRADE);
msg.headers_mut() msg.headers_mut()
@ -166,16 +168,29 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
helpers::write_status_line(version, msg.status().as_u16(), &mut buffer); helpers::write_status_line(version, msg.status().as_u16(), &mut buffer);
buffer.extend_from_slice(reason); buffer.extend_from_slice(reason);
match body { // content length
Body::Empty => if req.method != Method::HEAD { match info.length {
buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n"); ResponseLength::Chunked => {
} else { buffer.extend_from_slice(b"\r\ntransfer-encoding: chunked\r\n")
buffer.extend_from_slice(b"\r\n");
},
Body::Binary(ref bytes) => {
helpers::write_content_length(bytes.len(), &mut buffer)
} }
_ => buffer.extend_from_slice(b"\r\n"), ResponseLength::Zero => {
buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n")
}
ResponseLength::Length(len) => {
helpers::write_content_length(len, &mut buffer)
}
ResponseLength::Length64(len) => {
let s = format!("{}", len);
buffer.extend_from_slice(b"\r\ncontent-length: ");
buffer.extend_from_slice(s.as_ref());
buffer.extend_from_slice(b"\r\n");
}
ResponseLength::None => buffer.extend_from_slice(b"\r\n"),
}
if let Some(ce) = info.content_encoding {
buffer.extend_from_slice(b"content-encoding: ");
buffer.extend_from_slice(ce.as_ref());
buffer.extend_from_slice(b"\r\n");
} }
// write headers // write headers
@ -185,9 +200,13 @@ impl<T: AsyncWrite, H: 'static> Writer for H1Writer<T, H> {
unsafe { unsafe {
let mut buf = &mut *(buffer.bytes_mut() as *mut [u8]); let mut buf = &mut *(buffer.bytes_mut() as *mut [u8]);
for (key, value) in msg.headers() { for (key, value) in msg.headers() {
if is_bin && key == CONTENT_LENGTH { match *key {
is_bin = false; TRANSFER_ENCODING | CONTENT_ENCODING => continue,
continue; CONTENT_LENGTH => match info.length {
ResponseLength::None => (),
_ => continue,
},
_ => (),
} }
has_date = has_date || key == DATE; has_date = has_date || key == DATE;
let v = value.as_ref(); let v = value.as_ref();

View file

@ -14,6 +14,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
use tokio_timer::Delay; use tokio_timer::Delay;
use error::{Error, PayloadError}; use error::{Error, PayloadError};
use http::{StatusCode, Version};
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httprequest::HttpRequest; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
@ -21,6 +22,7 @@ use payload::{Payload, PayloadStatus, PayloadWriter};
use pipeline::Pipeline; use pipeline::Pipeline;
use uri::Url; use uri::Url;
use super::error::ServerError;
use super::h2writer::H2Writer; use super::h2writer::H2Writer;
use super::input::PayloadType; use super::input::PayloadType;
use super::settings::WorkerSettings; use super::settings::WorkerSettings;
@ -331,34 +333,35 @@ impl<H: HttpHandler + 'static> Entry<H> {
// Payload and Content-Encoding // Payload and Content-Encoding
let (psender, payload) = Payload::new(false); let (psender, payload) = Payload::new(false);
let mut msg = settings.get_http_message(); let mut msg = settings.get_request_context();
msg.get_mut().url = Url::new(parts.uri); msg.inner.url = Url::new(parts.uri);
msg.get_mut().method = parts.method; msg.inner.method = parts.method;
msg.get_mut().version = parts.version; msg.inner.version = parts.version;
msg.get_mut().headers = parts.headers; msg.inner.headers = parts.headers;
msg.get_mut().payload = Some(payload); *msg.inner.payload.borrow_mut() = Some(payload);
msg.get_mut().addr = addr; msg.inner.addr = addr;
let mut req = HttpRequest::from_message(msg);
// Payload sender // Payload sender
let psender = PayloadType::new(req.headers(), psender); let psender = PayloadType::new(msg.headers(), psender);
// start request processing // start request processing
let mut task = None; let mut task = None;
for h in settings.handlers().iter_mut() { for h in settings.handlers().iter_mut() {
req = match h.handle(req) { msg = match h.handle(msg) {
Ok(t) => { Ok(t) => {
task = Some(t); task = Some(t);
break; break;
} }
Err(req) => req, Err(msg) => msg,
} }
} }
Entry { Entry {
task: task.map(EntryPipe::Task).unwrap_or_else(|| { task: task.map(EntryPipe::Task).unwrap_or_else(|| {
EntryPipe::Error(Pipeline::error(HttpResponse::NotFound())) EntryPipe::Error(ServerError::err(
Version::HTTP_2,
StatusCode::NOT_FOUND,
))
}), }),
payload: psender, payload: psender,
stream: H2Writer::new(resp, Rc::clone(settings)), stream: H2Writer::new(resp, Rc::clone(settings)),

View file

@ -9,15 +9,15 @@ use std::rc::Rc;
use std::{cmp, io}; use std::{cmp, io};
use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING};
use http::{HttpTryFrom, Version}; use http::{HttpTryFrom, Method, Version};
use super::helpers; use super::helpers;
use super::output::Output; use super::message::Request;
use super::output::{Output, ResponseInfo};
use super::settings::WorkerSettings; use super::settings::WorkerSettings;
use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE}; use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE};
use body::{Binary, Body}; use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
const CHUNK_SIZE: usize = 16_384; const CHUNK_SIZE: usize = 16_384;
@ -75,8 +75,8 @@ impl<H: 'static> Writer for H2Writer<H> {
} }
#[inline] #[inline]
fn set_date(&self, dst: &mut BytesMut) { fn set_date(&mut self) {
self.settings.set_date(dst, true) self.settings.set_date(self.buffer.as_mut(), true)
} }
#[inline] #[inline]
@ -85,12 +85,12 @@ impl<H: 'static> Writer for H2Writer<H> {
} }
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, &mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding,
encoding: ContentEncoding,
) -> io::Result<WriterState> { ) -> io::Result<WriterState> {
// prepare response // prepare response
self.flags.insert(Flags::STARTED); self.flags.insert(Flags::STARTED);
self.buffer.for_server(req, msg, encoding); let mut info = ResponseInfo::new(req.inner.method == Method::HEAD);
self.buffer.for_server(&mut info, &req.inner, msg, encoding);
// http2 specific // http2 specific
msg.headers_mut().remove(CONNECTION); msg.headers_mut().remove(CONNECTION);

View file

@ -1,91 +1,7 @@
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use http::Version; use http::Version;
use std::cell::RefCell;
use std::collections::VecDeque;
use std::rc::Rc;
use std::{mem, ptr, slice}; use std::{mem, ptr, slice};
use httprequest::HttpInnerMessage;
/// Internal use only!
pub(crate) struct SharedMessagePool(RefCell<VecDeque<Rc<HttpInnerMessage>>>);
impl SharedMessagePool {
pub fn new() -> SharedMessagePool {
SharedMessagePool(RefCell::new(VecDeque::with_capacity(128)))
}
#[inline]
pub fn get(&self) -> Rc<HttpInnerMessage> {
if let Some(msg) = self.0.borrow_mut().pop_front() {
msg
} else {
Rc::new(HttpInnerMessage::default())
}
}
#[inline]
pub fn release(&self, mut msg: Rc<HttpInnerMessage>) {
let v = &mut self.0.borrow_mut();
if v.len() < 128 {
Rc::get_mut(&mut msg).unwrap().reset();
v.push_front(msg);
}
}
}
pub(crate) struct SharedHttpInnerMessage(
Option<Rc<HttpInnerMessage>>,
Option<Rc<SharedMessagePool>>,
);
impl Drop for SharedHttpInnerMessage {
fn drop(&mut self) {
if let Some(ref pool) = self.1 {
if let Some(msg) = self.0.take() {
if Rc::strong_count(&msg) == 1 {
pool.release(msg);
}
}
}
}
}
impl Clone for SharedHttpInnerMessage {
fn clone(&self) -> SharedHttpInnerMessage {
SharedHttpInnerMessage(self.0.clone(), self.1.clone())
}
}
impl Default for SharedHttpInnerMessage {
fn default() -> SharedHttpInnerMessage {
SharedHttpInnerMessage(Some(Rc::new(HttpInnerMessage::default())), None)
}
}
impl SharedHttpInnerMessage {
pub fn from_message(msg: HttpInnerMessage) -> SharedHttpInnerMessage {
SharedHttpInnerMessage(Some(Rc::new(msg)), None)
}
pub fn new(
msg: Rc<HttpInnerMessage>, pool: Rc<SharedMessagePool>,
) -> SharedHttpInnerMessage {
SharedHttpInnerMessage(Some(msg), Some(pool))
}
#[inline]
pub fn get_mut(&mut self) -> &mut HttpInnerMessage {
let r: &HttpInnerMessage = self.0.as_ref().unwrap().as_ref();
unsafe { &mut *(r as *const _ as *mut _) }
}
#[inline]
pub fn get_ref(&self) -> &HttpInnerMessage {
self.0.as_ref().unwrap()
}
}
const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\ const DEC_DIGITS_LUT: &[u8] = b"0001020304050607080910111213141516171819\
2021222324252627282930313233343536373839\ 2021222324252627282930313233343536373839\
4041424344454647484950515253545556575859\ 4041424344454647484950515253545556575859\

220
src/server/message.rs Normal file
View file

@ -0,0 +1,220 @@
use std::cell::{Cell, Ref, RefCell, RefMut};
use std::collections::VecDeque;
use std::net::SocketAddr;
use http::{header, HeaderMap, Method, Uri, Version};
use extensions::Extensions;
use httpmessage::HttpMessage;
use info::ConnectionInfo;
use payload::Payload;
use server::ServerSettings;
use uri::Url as InnerUrl;
bitflags! {
pub(crate) struct MessageFlags: u8 {
const KEEPALIVE = 0b0000_0001;
const CONN_INFO = 0b0000_0010;
}
}
/// Request's context
pub struct Request {
pub(crate) inner: Box<InnerRequest>,
}
pub(crate) struct InnerRequest {
pub(crate) version: Version,
pub(crate) method: Method,
pub(crate) url: InnerUrl,
pub(crate) flags: Cell<MessageFlags>,
pub(crate) headers: HeaderMap,
pub(crate) extensions: RefCell<Extensions>,
pub(crate) addr: Option<SocketAddr>,
pub(crate) info: RefCell<ConnectionInfo>,
pub(crate) payload: RefCell<Option<Payload>>,
pub(crate) settings: ServerSettings,
}
impl HttpMessage for Request {
type Stream = Payload;
fn headers(&self) -> &HeaderMap {
&self.inner.headers
}
#[inline]
fn payload(&self) -> Payload {
if let Some(payload) = self.inner.payload.borrow_mut().take() {
payload
} else {
Payload::empty()
}
}
}
impl Request {
/// Create new RequestContext instance
pub fn new(settings: ServerSettings) -> Request {
Request {
inner: Box::new(InnerRequest {
settings,
method: Method::GET,
url: InnerUrl::default(),
version: Version::HTTP_11,
headers: HeaderMap::with_capacity(16),
flags: Cell::new(MessageFlags::empty()),
addr: None,
info: RefCell::new(ConnectionInfo::default()),
payload: RefCell::new(None),
extensions: RefCell::new(Extensions::new()),
}),
}
}
#[inline]
pub(crate) fn url(&self) -> &InnerUrl {
&self.inner.url
}
/// Read the Request Uri.
#[inline]
pub fn uri(&self) -> &Uri {
self.inner.url.uri()
}
/// Read the Request method.
#[inline]
pub fn method(&self) -> &Method {
&self.inner.method
}
/// Read the Request Version.
#[inline]
pub fn version(&self) -> Version {
self.inner.version
}
/// The target path of this Request.
#[inline]
pub fn path(&self) -> &str {
self.inner.url.path()
}
#[inline]
/// Returns Request's headers.
pub fn headers(&self) -> &HeaderMap {
&self.inner.headers
}
#[inline]
/// Returns mutable Request's headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.inner.headers
}
/// Peer socket address
///
/// Peer address is actual socket address, if proxy is used in front of
/// actix http server, then peer address would be address of this proxy.
///
/// To get client connection information `connection_info()` method should
/// be used.
pub fn peer_addr(&self) -> Option<SocketAddr> {
self.inner.addr
}
/// Checks if a connection should be kept alive.
#[inline]
pub fn keep_alive(&self) -> bool {
self.inner.flags.get().contains(MessageFlags::KEEPALIVE)
}
/// Request extensions
#[inline]
pub fn extensions(&self) -> Ref<Extensions> {
self.inner.extensions.borrow()
}
/// Mutable reference to a the request's extensions
#[inline]
pub fn extensions_mut(&self) -> RefMut<Extensions> {
self.inner.extensions.borrow_mut()
}
/// Check if request requires connection upgrade
pub fn upgrade(&self) -> bool {
if let Some(conn) = self.inner.headers.get(header::CONNECTION) {
if let Ok(s) = conn.to_str() {
return s.to_lowercase().contains("upgrade");
}
}
self.inner.method == Method::CONNECT
}
/// Get *ConnectionInfo* for the correct request.
pub fn connection_info(&self) -> Ref<ConnectionInfo> {
if self.inner.flags.get().contains(MessageFlags::CONN_INFO) {
self.inner.info.borrow()
} else {
let mut flags = self.inner.flags.get();
flags.insert(MessageFlags::CONN_INFO);
self.inner.flags.set(flags);
self.inner.info.borrow_mut().update(self);
self.inner.info.borrow()
}
}
/// Server settings
#[inline]
pub fn server_settings(&self) -> &ServerSettings {
&self.inner.settings
}
#[inline]
pub(crate) fn reset(&mut self) {
self.inner.headers.clear();
self.inner.extensions.borrow_mut().clear();
self.inner.flags.set(MessageFlags::empty());
*self.inner.payload.borrow_mut() = None;
}
}
pub(crate) struct RequestPool(RefCell<VecDeque<Request>>, RefCell<ServerSettings>);
thread_local!(static POOL: &'static RequestPool = RequestPool::create());
impl RequestPool {
fn create() -> &'static RequestPool {
let pool = RequestPool(
RefCell::new(VecDeque::with_capacity(128)),
RefCell::new(ServerSettings::default()),
);
Box::leak(Box::new(pool))
}
pub fn pool(settings: ServerSettings) -> &'static RequestPool {
POOL.with(|p| {
*p.1.borrow_mut() = settings;
*p
})
}
#[inline]
pub fn get(&self) -> Request {
if let Some(msg) = self.0.borrow_mut().pop_front() {
msg
} else {
Request::new(self.1.borrow().clone())
}
}
#[inline]
pub fn release(&self, mut msg: Request) {
let v = &mut self.0.borrow_mut();
if v.len() < 128 {
msg.reset();
v.push_front(msg);
}
}
}

View file

@ -8,6 +8,7 @@ use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tcp::TcpStream; use tokio_tcp::TcpStream;
mod channel; mod channel;
mod error;
pub(crate) mod h1; pub(crate) mod h1;
pub(crate) mod h1decoder; pub(crate) mod h1decoder;
mod h1writer; mod h1writer;
@ -15,11 +16,13 @@ mod h2;
mod h2writer; mod h2writer;
pub(crate) mod helpers; pub(crate) mod helpers;
pub(crate) mod input; pub(crate) mod input;
pub(crate) mod message;
pub(crate) mod output; pub(crate) mod output;
pub(crate) mod settings; pub(crate) mod settings;
mod srv; mod srv;
mod worker; mod worker;
pub use self::message::Request;
pub use self::settings::ServerSettings; pub use self::settings::ServerSettings;
pub use self::srv::HttpServer; pub use self::srv::HttpServer;
@ -30,7 +33,7 @@ use actix::Message;
use body::Binary; use body::Binary;
use error::Error; use error::Error;
use header::ContentEncoding; use header::ContentEncoding;
use httprequest::{HttpInnerMessage, HttpRequest}; use httprequest::HttpRequest;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
/// max buffer size 64k /// max buffer size 64k
@ -128,13 +131,13 @@ pub trait HttpHandler: 'static {
type Task: HttpHandlerTask; type Task: HttpHandlerTask;
/// Handle request /// Handle request
fn handle(&self, req: HttpRequest) -> Result<Self::Task, HttpRequest>; fn handle(&self, req: Request) -> Result<Self::Task, Request>;
} }
impl HttpHandler for Box<HttpHandler<Task = Box<HttpHandlerTask>>> { impl HttpHandler for Box<HttpHandler<Task = Box<HttpHandlerTask>>> {
type Task = Box<HttpHandlerTask>; type Task = Box<HttpHandlerTask>;
fn handle(&self, req: HttpRequest) -> Result<Box<HttpHandlerTask>, HttpRequest> { fn handle(&self, req: Request) -> Result<Box<HttpHandlerTask>, Request> {
self.as_ref().handle(req) self.as_ref().handle(req)
} }
} }
@ -165,13 +168,13 @@ pub trait IntoHttpHandler {
type Handler: HttpHandler; type Handler: HttpHandler;
/// Convert into `HttpHandler` object. /// Convert into `HttpHandler` object.
fn into_handler(self, settings: ServerSettings) -> Self::Handler; fn into_handler(self) -> Self::Handler;
} }
impl<T: HttpHandler> IntoHttpHandler for T { impl<T: HttpHandler> IntoHttpHandler for T {
type Handler = T; type Handler = T;
fn into_handler(self, _: ServerSettings) -> Self::Handler { fn into_handler(self) -> Self::Handler {
self self
} }
} }
@ -190,14 +193,13 @@ pub trait Writer {
fn written(&self) -> u64; fn written(&self) -> u64;
#[doc(hidden)] #[doc(hidden)]
fn set_date(&self, st: &mut BytesMut); fn set_date(&mut self);
#[doc(hidden)] #[doc(hidden)]
fn buffer(&mut self) -> &mut BytesMut; fn buffer(&mut self) -> &mut BytesMut;
fn start( fn start(
&mut self, req: &mut HttpInnerMessage, resp: &mut HttpResponse, &mut self, req: &Request, resp: &mut HttpResponse, encoding: ContentEncoding,
encoding: ContentEncoding,
) -> io::Result<WriterState>; ) -> io::Result<WriterState>;
fn write(&mut self, payload: &Binary) -> io::Result<WriterState>; fn write(&mut self, payload: &Binary) -> io::Result<WriterState>;

View file

@ -15,11 +15,37 @@ use http::header::{
}; };
use http::{HttpTryFrom, Method, Version}; use http::{HttpTryFrom, Method, Version};
use super::message::{InnerRequest, Request};
use body::{Binary, Body}; use body::{Binary, Body};
use header::ContentEncoding; use header::ContentEncoding;
use httprequest::HttpInnerMessage;
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
#[derive(Debug)]
pub(crate) enum ResponseLength {
Chunked,
Zero,
Length(usize),
Length64(u64),
None,
}
#[derive(Debug)]
pub(crate) struct ResponseInfo {
head: bool,
pub length: ResponseLength,
pub content_encoding: Option<&'static str>,
}
impl ResponseInfo {
pub fn new(head: bool) -> Self {
ResponseInfo {
head,
length: ResponseLength::None,
content_encoding: None,
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum Output { pub(crate) enum Output {
Empty(BytesMut), Empty(BytesMut),
@ -119,13 +145,12 @@ impl Output {
} }
} }
pub fn for_server( pub(crate) fn for_server(
&mut self, req: &HttpInnerMessage, resp: &mut HttpResponse, &mut self, info: &mut ResponseInfo, req: &InnerRequest, resp: &mut HttpResponse,
response_encoding: ContentEncoding, response_encoding: ContentEncoding,
) { ) {
let buf = self.take(); let buf = self.take();
let version = resp.version().unwrap_or_else(|| req.version); let version = resp.version().unwrap_or_else(|| req.version);
let is_head = req.method == Method::HEAD;
let mut len = 0; let mut len = 0;
#[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] #[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
@ -158,10 +183,7 @@ impl Output {
encoding => encoding, encoding => encoding,
}; };
if encoding.is_compression() { if encoding.is_compression() {
resp.headers_mut().insert( info.content_encoding = Some(encoding.as_str());
CONTENT_ENCODING,
HeaderValue::from_static(encoding.as_str()),
);
} }
encoding encoding
} else { } else {
@ -173,8 +195,8 @@ impl Output {
#[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] #[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))]
let transfer = match resp.body() { let transfer = match resp.body() {
&Body::Empty => { &Body::Empty => {
if req.method != Method::HEAD { if !info.head {
resp.headers_mut().remove(CONTENT_LENGTH); info.length = ResponseLength::Zero;
} }
*self = Output::Empty(buf); *self = Output::Empty(buf);
return; return;
@ -216,13 +238,8 @@ impl Output {
} }
} }
if is_head { info.length = ResponseLength::Length(len);
let mut b = BytesMut::new(); if info.head {
let _ = write!(b, "{}", len);
resp.headers_mut().insert(
CONTENT_LENGTH,
HeaderValue::try_from(b.freeze()).unwrap(),
);
*self = Output::Empty(buf); *self = Output::Empty(buf);
} else { } else {
*self = Output::Buffer(buf); *self = Output::Buffer(buf);
@ -236,7 +253,7 @@ impl Output {
} }
if encoding != ContentEncoding::Identity { if encoding != ContentEncoding::Identity {
encoding = ContentEncoding::Identity; encoding = ContentEncoding::Identity;
resp.headers_mut().remove(CONTENT_ENCODING); info.content_encoding.take();
} }
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
} else { } else {
@ -245,12 +262,12 @@ impl Output {
{ {
resp.headers_mut().remove(CONTENT_LENGTH); resp.headers_mut().remove(CONTENT_LENGTH);
} }
Output::streaming_encoding(buf, version, resp) Output::streaming_encoding(info, buf, version, resp)
} }
} }
}; };
// check for head response // check for head response
if is_head { if info.head {
resp.set_body(Body::Empty); resp.set_body(Body::Empty);
*self = Output::Empty(transfer.buf.unwrap()); *self = Output::Empty(transfer.buf.unwrap());
return; return;
@ -277,18 +294,17 @@ impl Output {
} }
fn streaming_encoding( fn streaming_encoding(
buf: BytesMut, version: Version, resp: &mut HttpResponse, info: &mut ResponseInfo, buf: BytesMut, version: Version,
resp: &mut HttpResponse,
) -> TransferEncoding { ) -> TransferEncoding {
match resp.chunked() { match resp.chunked() {
Some(true) => { Some(true) => {
// Enable transfer encoding // Enable transfer encoding
resp.headers_mut().remove(CONTENT_LENGTH);
if version == Version::HTTP_2 { if version == Version::HTTP_2 {
resp.headers_mut().remove(TRANSFER_ENCODING); info.length = ResponseLength::None;
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
} else { } else {
resp.headers_mut() info.length = ResponseLength::Chunked;
.insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked"));
TransferEncoding::chunked(buf) TransferEncoding::chunked(buf)
} }
} }
@ -315,6 +331,7 @@ impl Output {
if !chunked { if !chunked {
if let Some(len) = len { if let Some(len) = len {
info.length = ResponseLength::Length64(len);
TransferEncoding::length(len, buf) TransferEncoding::length(len, buf)
} else { } else {
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
@ -323,14 +340,11 @@ impl Output {
// Enable transfer encoding // Enable transfer encoding
match version { match version {
Version::HTTP_11 => { Version::HTTP_11 => {
resp.headers_mut().insert( info.length = ResponseLength::Chunked;
TRANSFER_ENCODING,
HeaderValue::from_static("chunked"),
);
TransferEncoding::chunked(buf) TransferEncoding::chunked(buf)
} }
_ => { _ => {
resp.headers_mut().remove(TRANSFER_ENCODING); info.length = ResponseLength::None;
TransferEncoding::eof(buf) TransferEncoding::eof(buf)
} }
} }

View file

@ -12,6 +12,7 @@ use time;
use super::channel::Node; use super::channel::Node;
use super::helpers; use super::helpers;
use super::message::{Request, RequestPool};
use super::KeepAlive; use super::KeepAlive;
use body::Body; use body::Body;
use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool}; use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool};
@ -156,14 +157,17 @@ pub(crate) struct WorkerSettings<H> {
keep_alive: u64, keep_alive: u64,
ka_enabled: bool, ka_enabled: bool,
bytes: Rc<SharedBytesPool>, bytes: Rc<SharedBytesPool>,
messages: Rc<helpers::SharedMessagePool>, messages: &'static RequestPool,
channels: Cell<usize>, channels: Cell<usize>,
node: Box<Node<()>>, node: Box<Node<()>>,
date: UnsafeCell<Date>, date: UnsafeCell<Date>,
settings: ServerSettings,
} }
impl<H> WorkerSettings<H> { impl<H> WorkerSettings<H> {
pub(crate) fn new(h: Vec<H>, keep_alive: KeepAlive) -> WorkerSettings<H> { pub(crate) fn new(
h: Vec<H>, keep_alive: KeepAlive, settings: ServerSettings,
) -> WorkerSettings<H> {
let (keep_alive, ka_enabled) = match keep_alive { let (keep_alive, ka_enabled) = match keep_alive {
KeepAlive::Timeout(val) => (val as u64, true), KeepAlive::Timeout(val) => (val as u64, true),
KeepAlive::Os | KeepAlive::Tcp(_) => (0, true), KeepAlive::Os | KeepAlive::Tcp(_) => (0, true),
@ -171,14 +175,15 @@ impl<H> WorkerSettings<H> {
}; };
WorkerSettings { WorkerSettings {
keep_alive,
ka_enabled,
h: RefCell::new(h), h: RefCell::new(h),
bytes: Rc::new(SharedBytesPool::new()), bytes: Rc::new(SharedBytesPool::new()),
messages: Rc::new(helpers::SharedMessagePool::new()), messages: RequestPool::pool(settings.clone()),
channels: Cell::new(0), channels: Cell::new(0),
node: Box::new(Node::head()), node: Box::new(Node::head()),
date: UnsafeCell::new(Date::new()), date: UnsafeCell::new(Date::new()),
keep_alive,
ka_enabled,
settings,
} }
} }
@ -210,11 +215,8 @@ impl<H> WorkerSettings<H> {
self.bytes.release_bytes(bytes) self.bytes.release_bytes(bytes)
} }
pub fn get_http_message(&self) -> helpers::SharedHttpInnerMessage { pub fn get_request_context(&self) -> Request {
helpers::SharedHttpInnerMessage::new( self.messages.get()
self.messages.get(),
Rc::clone(&self.messages),
)
} }
pub fn add_channel(&self) { pub fn add_channel(&self) {
@ -316,7 +318,11 @@ mod tests {
#[test] #[test]
fn test_date() { fn test_date() {
let settings = WorkerSettings::<()>::new(Vec::new(), KeepAlive::Os); let settings = WorkerSettings::<()>::new(
Vec::new(),
KeepAlive::Os,
ServerSettings::default(),
);
let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf1 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);
settings.set_date(&mut buf1, true); settings.set_date(&mut buf1, true);
let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10);

View file

@ -358,12 +358,10 @@ where
let addr = Arbiter::start(move |ctx: &mut Context<_>| { let addr = Arbiter::start(move |ctx: &mut Context<_>| {
let s = ServerSettings::from_parts(parts); let s = ServerSettings::from_parts(parts);
let apps: Vec<_> = (*factory)() let apps: Vec<_> =
.into_iter() (*factory)().into_iter().map(|h| h.into_handler()).collect();
.map(|h| h.into_handler(s.clone()))
.collect();
ctx.add_message_stream(rx); ctx.add_message_stream(rx);
Worker::new(apps, socks, ka) Worker::new(apps, socks, ka, s)
}); });
workers.push((idx, tx)); workers.push((idx, tx));
self.workers.push((idx, addr)); self.workers.push((idx, addr));
@ -404,7 +402,7 @@ impl<H: IntoHttpHandler> HttpServer<H> {
/// fn main() { /// fn main() {
/// let sys = actix::System::new("example"); // <- create Actix system /// let sys = actix::System::new("example"); // <- create Actix system
/// ///
/// server::new(|| App::new().resource("/", |r| r.h(|_| HttpResponse::Ok()))) /// server::new(|| App::new().resource("/", |r| r.h(|_: &_| HttpResponse::Ok())))
/// .bind("127.0.0.1:0") /// .bind("127.0.0.1:0")
/// .expect("Can not bind to 127.0.0.1:0") /// .expect("Can not bind to 127.0.0.1:0")
/// .start(); /// .start();
@ -559,9 +557,13 @@ impl<H: IntoHttpHandler> HttpServer<H> {
let settings = ServerSettings::new(Some(addr), &self.host, secure); let settings = ServerSettings::new(Some(addr), &self.host, secure);
let apps: Vec<_> = (*self.factory)() let apps: Vec<_> = (*self.factory)()
.into_iter() .into_iter()
.map(|h| h.into_handler(settings.clone())) .map(|h| h.into_handler())
.collect(); .collect();
self.h = Some(Rc::new(WorkerSettings::new(apps, self.keep_alive))); self.h = Some(Rc::new(WorkerSettings::new(
apps,
self.keep_alive,
settings,
)));
// start server // start server
let signals = self.subscribe_to_signals(); let signals = self.subscribe_to_signals();
@ -645,12 +647,10 @@ impl<H: IntoHttpHandler> StreamHandler2<ServerCommand, ()> for HttpServer<H> {
let addr = Arbiter::start(move |ctx: &mut Context<_>| { let addr = Arbiter::start(move |ctx: &mut Context<_>| {
let settings = ServerSettings::new(Some(addr), &host, false); let settings = ServerSettings::new(Some(addr), &host, false);
let apps: Vec<_> = (*factory)() let apps: Vec<_> =
.into_iter() (*factory)().into_iter().map(|h| h.into_handler()).collect();
.map(|h| h.into_handler(settings.clone()))
.collect();
ctx.add_message_stream(rx); ctx.add_message_stream(rx);
Worker::new(apps, socks, ka) Worker::new(apps, socks, ka, settings)
}); });
for item in &self.accept { for item in &self.accept {
let _ = item.1.send(Command::Worker(new_idx, tx.clone())); let _ = item.1.send(Command::Worker(new_idx, tx.clone()));

View file

@ -25,7 +25,7 @@ use actix::msgs::StopArbiter;
use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response}; use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response};
use server::channel::HttpChannel; use server::channel::HttpChannel;
use server::settings::WorkerSettings; use server::settings::{ServerSettings, WorkerSettings};
use server::{HttpHandler, KeepAlive}; use server::{HttpHandler, KeepAlive};
#[derive(Message)] #[derive(Message)]
@ -68,6 +68,7 @@ where
impl<H: HttpHandler + 'static> Worker<H> { impl<H: HttpHandler + 'static> Worker<H> {
pub(crate) fn new( pub(crate) fn new(
h: Vec<H>, socks: Slab<SocketInfo>, keep_alive: KeepAlive, h: Vec<H>, socks: Slab<SocketInfo>, keep_alive: KeepAlive,
settings: ServerSettings,
) -> Worker<H> { ) -> Worker<H> {
let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive { let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive {
Some(time::Duration::new(val as u64, 0)) Some(time::Duration::new(val as u64, 0))
@ -76,7 +77,7 @@ impl<H: HttpHandler + 'static> Worker<H> {
}; };
Worker { Worker {
settings: Rc::new(WorkerSettings::new(h, keep_alive)), settings: Rc::new(WorkerSettings::new(h, keep_alive, settings)),
socks, socks,
tcp_ka, tcp_ka,
} }

View file

@ -22,14 +22,15 @@ use client::{ClientConnector, ClientRequest, ClientRequestBuilder};
use error::Error; use error::Error;
use handler::{AsyncResultItem, Handler, Responder}; use handler::{AsyncResultItem, Handler, Responder};
use header::{Header, IntoHeaderValue}; use header::{Header, IntoHeaderValue};
use httprequest::HttpRequest; use httprequest::{HttpRequest, RouterResource};
use httpresponse::HttpResponse; use httpresponse::HttpResponse;
use middleware::Middleware; use middleware::Middleware;
use param::Params; use param::Params;
use payload::Payload; use payload::Payload;
use resource::ResourceHandler; use resource::ResourceHandler;
use router::Router; use router::Router;
use server::{HttpServer, IntoHttpHandler, ServerSettings}; use server::{HttpServer, IntoHttpHandler, Request, ServerSettings};
use uri::Url as InnerUrl;
use ws; use ws;
/// The `TestServer` type. /// The `TestServer` type.
@ -43,7 +44,7 @@ use ws;
/// # extern crate actix_web; /// # extern crate actix_web;
/// # use actix_web::*; /// # use actix_web::*;
/// # /// #
/// # fn my_handler(req: HttpRequest) -> HttpResponse { /// # fn my_handler(req: &HttpRequest) -> HttpResponse {
/// # HttpResponse::Ok().into() /// # HttpResponse::Ok().into()
/// # } /// # }
/// # /// #
@ -330,8 +331,12 @@ impl<S: 'static> TestApp<S> {
} }
/// Register handler for "/" /// Register handler for "/"
pub fn handler<H: Handler<S>>(&mut self, handler: H) { pub fn handler<F, R>(&mut self, handler: F)
self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler))); where
F: Fn(&HttpRequest<S>) -> R + 'static,
R: Responder + 'static,
{
self.app = Some(self.app.take().unwrap().resource("/", |r| r.f(handler)));
} }
/// Register middleware /// Register middleware
@ -357,8 +362,8 @@ impl<S: 'static> TestApp<S> {
impl<S: 'static> IntoHttpHandler for TestApp<S> { impl<S: 'static> IntoHttpHandler for TestApp<S> {
type Handler = HttpApplication<S>; type Handler = HttpApplication<S>;
fn into_handler(mut self, settings: ServerSettings) -> HttpApplication<S> { fn into_handler(mut self) -> HttpApplication<S> {
self.app.take().unwrap().into_handler(settings) self.app.take().unwrap().into_handler()
} }
} }
@ -384,7 +389,7 @@ impl<S: 'static> Iterator for TestApp<S> {
/// # use actix_web::*; /// # use actix_web::*;
/// use actix_web::test::TestRequest; /// use actix_web::test::TestRequest;
/// ///
/// fn index(req: HttpRequest) -> HttpResponse { /// fn index(req: &HttpRequest) -> HttpResponse {
/// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) { /// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
/// HttpResponse::Ok().into() /// HttpResponse::Ok().into()
/// } else { /// } else {
@ -533,11 +538,19 @@ impl<S: 'static> TestRequest<S> {
cookies, cookies,
payload, payload,
} = self; } = self;
let mut req = HttpRequest::new(method, uri, version, headers, payload); let (router, _) = Router::new::<S>("/", Vec::new());
let mut req = Request::new(ServerSettings::default());
req.inner.method = method;
req.inner.url = InnerUrl::new(uri);
req.inner.version = version;
req.inner.headers = headers;
*req.inner.payload.borrow_mut() = payload;
let mut req =
HttpRequest::new(req, Rc::new(state), router.route_info_params(params, 0));
req.set_cookies(cookies); req.set_cookies(cookies);
req.as_mut().params = params; req
let (router, _) = Router::new::<S>("/", ServerSettings::default(), Vec::new());
req.with_state(Rc::new(state), router)
} }
#[cfg(test)] #[cfg(test)]
@ -554,10 +567,37 @@ impl<S: 'static> TestRequest<S> {
payload, payload,
} = self; } = self;
let mut req = HttpRequest::new(method, uri, version, headers, payload); let mut req = Request::new(ServerSettings::default());
req.inner.method = method;
req.inner.url = InnerUrl::new(uri);
req.inner.version = version;
req.inner.headers = headers;
*req.inner.payload.borrow_mut() = payload;
let mut req =
HttpRequest::new(req, Rc::new(state), router.route_info_params(params, 0));
req.set_cookies(cookies); req.set_cookies(cookies);
req.as_mut().params = params; req
req.with_state(Rc::new(state), router) }
/// Complete request creation and generate server `Request` instance
pub fn request(self) -> Request {
let TestRequest {
state,
method,
uri,
version,
headers,
params,
cookies,
payload,
} = self;
let mut req = Request::new(ServerSettings::default());
req.inner.method = method;
req.inner.url = InnerUrl::new(uri);
req.inner.version = version;
req.inner.headers = headers;
*req.inner.payload.borrow_mut() = payload;
req
} }
/// This method generates `HttpRequest` instance and runs handler /// This method generates `HttpRequest` instance and runs handler
@ -566,7 +606,7 @@ impl<S: 'static> TestRequest<S> {
/// This method panics is handler returns actor or async result. /// This method panics is handler returns actor or async result.
pub fn run<H: Handler<S>>(self, h: &H) -> Result<HttpResponse, Error> { pub fn run<H: Handler<S>>(self, h: &H) -> Result<HttpResponse, Error> {
let req = self.finish(); let req = self.finish();
let resp = h.handle(req.clone()); let resp = h.handle(&req);
match resp.respond_to(&req) { match resp.respond_to(&req) {
Ok(resp) => match resp.into().into() { Ok(resp) => match resp.into().into() {

View file

@ -42,9 +42,9 @@ where
{ {
type Result = AsyncResult<HttpResponse>; type Result = AsyncResult<HttpResponse>;
fn handle(&self, req: HttpRequest<S>) -> Self::Result { fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
let mut fut = WithHandlerFut { let mut fut = WithHandlerFut {
req, req: req.clone(),
started: false, started: false,
hnd: Rc::clone(&self.hnd), hnd: Rc::clone(&self.hnd),
cfg: self.cfg.clone(), cfg: self.cfg.clone(),
@ -167,9 +167,9 @@ where
{ {
type Result = AsyncResult<HttpResponse>; type Result = AsyncResult<HttpResponse>;
fn handle(&self, req: HttpRequest<S>) -> Self::Result { fn handle(&self, req: &HttpRequest<S>) -> Self::Result {
let mut fut = WithAsyncHandlerFut { let mut fut = WithAsyncHandlerFut {
req, req: req.clone(),
started: false, started: false,
hnd: Rc::clone(&self.hnd), hnd: Rc::clone(&self.hnd),
cfg: Rc::clone(&self.cfg), cfg: Rc::clone(&self.cfg),

View file

@ -24,7 +24,7 @@ use payload::PayloadHelper;
use client::{ use client::{
ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse, ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse,
HttpResponseParserError, SendRequest, SendRequestError, HttpResponseParserError, Pipeline, SendRequest, SendRequestError,
}; };
use super::frame::Frame; use super::frame::Frame;
@ -275,7 +275,7 @@ impl Client {
struct Inner { struct Inner {
tx: UnboundedSender<Bytes>, tx: UnboundedSender<Bytes>,
rx: PayloadHelper<ClientResponse>, rx: PayloadHelper<Box<Pipeline>>,
closed: bool, closed: bool,
} }
@ -431,7 +431,7 @@ impl Future for ClientHandshake {
let inner = Inner { let inner = Inner {
tx: self.tx.take().unwrap(), tx: self.tx.take().unwrap(),
rx: PayloadHelper::new(resp), rx: PayloadHelper::new(resp.payload()),
closed: false, closed: false,
}; };

View file

@ -13,7 +13,7 @@
//! use actix_web::{ws, HttpRequest, HttpResponse}; //! use actix_web::{ws, HttpRequest, HttpResponse};
//! //!
//! // do websocket handshake and start actor //! // do websocket handshake and start actor
//! fn ws_index(req: HttpRequest) -> Result<HttpResponse> { //! fn ws_index(req: &HttpRequest) -> Result<HttpResponse> {
//! ws::start(req, Ws) //! ws::start(req, Ws)
//! } //! }
//! //!
@ -171,15 +171,15 @@ pub enum Message {
} }
/// Do websocket handshake and start actor /// Do websocket handshake and start actor
pub fn start<A, S>(req: HttpRequest<S>, actor: A) -> Result<HttpResponse, Error> pub fn start<A, S>(req: &HttpRequest<S>, actor: A) -> Result<HttpResponse, Error>
where where
A: Actor<Context = WebsocketContext<A, S>> + StreamHandler<Message, ProtocolError>, A: Actor<Context = WebsocketContext<A, S>> + StreamHandler<Message, ProtocolError>,
S: 'static, S: 'static,
{ {
let mut resp = handshake(&req)?; let mut resp = handshake(req)?;
let stream = WsStream::new(req.clone()); let stream = WsStream::new(req.payload());
let mut ctx = WebsocketContext::new(req, actor); let mut ctx = WebsocketContext::new(req.clone(), actor);
ctx.add_stream(stream); ctx.add_stream(stream);
Ok(resp.body(ctx)) Ok(resp.body(ctx))
@ -359,162 +359,116 @@ pub trait WsWriter {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use std::str::FromStr;
use super::*; use super::*;
use http::{header, HeaderMap, Method, Uri, Version}; use http::{header, HeaderMap, Method, Uri, Version};
use std::str::FromStr; use test::TestRequest;
#[test] #[test]
fn test_handshake() { fn test_handshake() {
let req = HttpRequest::new( let req = TestRequest::default().method(Method::POST).finish();
Method::POST,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert_eq!( assert_eq!(
HandshakeError::GetMethodRequired, HandshakeError::GetMethodRequired,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let req = HttpRequest::new( let req = TestRequest::default().finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
HeaderMap::new(),
None,
);
assert_eq!( assert_eq!(
HandshakeError::NoWebsocketUpgrade, HandshakeError::NoWebsocketUpgrade,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert(header::UPGRADE, header::HeaderValue::from_static("test")); .header(header::UPGRADE, header::HeaderValue::from_static("test"))
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
HandshakeError::NoWebsocketUpgrade, HandshakeError::NoWebsocketUpgrade,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::UPGRADE, header::UPGRADE,
header::HeaderValue::from_static("websocket"), header::HeaderValue::from_static("websocket"),
); )
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
HandshakeError::NoConnectionUpgrade, HandshakeError::NoConnectionUpgrade,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::UPGRADE, header::UPGRADE,
header::HeaderValue::from_static("websocket"), header::HeaderValue::from_static("websocket"),
); )
headers.insert( .header(
header::CONNECTION, header::CONNECTION,
header::HeaderValue::from_static("upgrade"), header::HeaderValue::from_static("upgrade"),
); )
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
HandshakeError::NoVersionHeader, HandshakeError::NoVersionHeader,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::UPGRADE, header::UPGRADE,
header::HeaderValue::from_static("websocket"), header::HeaderValue::from_static("websocket"),
); )
headers.insert( .header(
header::CONNECTION, header::CONNECTION,
header::HeaderValue::from_static("upgrade"), header::HeaderValue::from_static("upgrade"),
); )
headers.insert( .header(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("5"), header::HeaderValue::from_static("5"),
); )
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
HandshakeError::UnsupportedVersion, HandshakeError::UnsupportedVersion,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::UPGRADE, header::UPGRADE,
header::HeaderValue::from_static("websocket"), header::HeaderValue::from_static("websocket"),
); )
headers.insert( .header(
header::CONNECTION, header::CONNECTION,
header::HeaderValue::from_static("upgrade"), header::HeaderValue::from_static("upgrade"),
); )
headers.insert( .header(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"), header::HeaderValue::from_static("13"),
); )
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
HandshakeError::BadWebsocketKey, HandshakeError::BadWebsocketKey,
handshake(&req).err().unwrap() handshake(&req).err().unwrap()
); );
let mut headers = HeaderMap::new(); let req = TestRequest::default()
headers.insert( .header(
header::UPGRADE, header::UPGRADE,
header::HeaderValue::from_static("websocket"), header::HeaderValue::from_static("websocket"),
); )
headers.insert( .header(
header::CONNECTION, header::CONNECTION,
header::HeaderValue::from_static("upgrade"), header::HeaderValue::from_static("upgrade"),
); )
headers.insert( .header(
header::SEC_WEBSOCKET_VERSION, header::SEC_WEBSOCKET_VERSION,
header::HeaderValue::from_static("13"), header::HeaderValue::from_static("13"),
); )
headers.insert( .header(
header::SEC_WEBSOCKET_KEY, header::SEC_WEBSOCKET_KEY,
header::HeaderValue::from_static("13"), header::HeaderValue::from_static("13"),
); )
let req = HttpRequest::new( .finish();
Method::GET,
Uri::from_str("/").unwrap(),
Version::HTTP_11,
headers,
None,
);
assert_eq!( assert_eq!(
StatusCode::SWITCHING_PROTOCOLS, StatusCode::SWITCHING_PROTOCOLS,
handshake(&req).unwrap().finish().status() handshake(&req).unwrap().finish().status()

View file

@ -67,7 +67,7 @@ fn test_simple() {
#[test] #[test]
fn test_with_query_parameter() { fn test_with_query_parameter() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| match req.query().get("qp") { app.handler(|req: &HttpRequest| match req.query().get("qp") {
Some(_) => HttpResponse::Ok().finish(), Some(_) => HttpResponse::Ok().finish(),
None => HttpResponse::BadRequest().finish(), None => HttpResponse::BadRequest().finish(),
}) })
@ -110,7 +110,7 @@ fn test_no_decompress() {
#[test] #[test]
fn test_client_gzip_encoding() { fn test_client_gzip_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -140,7 +140,7 @@ fn test_client_gzip_encoding_large() {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -173,7 +173,7 @@ fn test_client_gzip_encoding_large_random() {
.collect::<String>(); .collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -202,7 +202,7 @@ fn test_client_gzip_encoding_large_random() {
#[test] #[test]
fn test_client_brotli_encoding() { fn test_client_brotli_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -236,7 +236,7 @@ fn test_client_brotli_encoding_large_random() {
.collect::<String>(); .collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(move |bytes: Bytes| { .and_then(move |bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -266,7 +266,7 @@ fn test_client_brotli_encoding_large_random() {
#[test] #[test]
fn test_client_deflate_encoding() { fn test_client_deflate_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -300,7 +300,7 @@ fn test_client_deflate_encoding_large_random() {
.collect::<String>(); .collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -328,7 +328,7 @@ fn test_client_deflate_encoding_large_random() {
#[test] #[test]
fn test_client_streaming_explicit() { fn test_client_streaming_explicit() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.map_err(Error::from) .map_err(Error::from)
.and_then(|body| { .and_then(|body| {
@ -393,7 +393,7 @@ fn test_client_cookie_handling() {
let mut srv = test::TestServer::new(move |app| { let mut srv = test::TestServer::new(move |app| {
let cookie1 = cookie1b.clone(); let cookie1 = cookie1b.clone();
let cookie2 = cookie2b.clone(); let cookie2 = cookie2b.clone();
app.handler(move |req: HttpRequest| { app.handler(move |req: &HttpRequest| {
// Check cookies were sent correctly // Check cookies were sent correctly
req.cookie("cookie1").ok_or_else(err) req.cookie("cookie1").ok_or_else(err)
.and_then(|c1| if c1.value() == "value1" { .and_then(|c1| if c1.value() == "value1" {

View file

@ -20,21 +20,21 @@ struct MiddlewareTest {
} }
impl<S> middleware::Middleware<S> for MiddlewareTest { impl<S> middleware::Middleware<S> for MiddlewareTest {
fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> { fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started> {
self.start self.start
.store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed); .store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
Ok(middleware::Started::Done) Ok(middleware::Started::Done)
} }
fn response( fn response(
&self, _: &mut HttpRequest<S>, resp: HttpResponse, &self, _: &HttpRequest<S>, resp: HttpResponse,
) -> Result<middleware::Response> { ) -> Result<middleware::Response> {
self.response self.response
.store(self.response.load(Ordering::Relaxed) + 1, Ordering::Relaxed); .store(self.response.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
Ok(middleware::Response::Done(resp)) Ok(middleware::Response::Done(resp))
} }
fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished { fn finish(&self, _: &HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
self.finish self.finish
.store(self.finish.load(Ordering::Relaxed) + 1, Ordering::Relaxed); .store(self.finish.load(Ordering::Relaxed) + 1, Ordering::Relaxed);
middleware::Finished::Done middleware::Finished::Done
@ -331,7 +331,7 @@ fn test_scope_middleware_async_handler() {
assert_eq!(num3.load(Ordering::Relaxed), 1); assert_eq!(num3.load(Ordering::Relaxed), 1);
} }
fn index_test_middleware_async_error(_: HttpRequest) -> FutureResponse<HttpResponse> { fn index_test_middleware_async_error(_: &HttpRequest) -> FutureResponse<HttpResponse> {
future::result(Err(error::ErrorBadRequest("TEST"))).responder() future::result(Err(error::ErrorBadRequest("TEST"))).responder()
} }
@ -412,7 +412,7 @@ fn test_resource_middleware_async_error() {
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw); r.middleware(mw);
r.h(index_test_middleware_async_error); r.f(index_test_middleware_async_error);
}) })
}); });
@ -432,7 +432,7 @@ struct MiddlewareAsyncTest {
} }
impl<S> middleware::Middleware<S> for MiddlewareAsyncTest { impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
fn start(&self, _: &mut HttpRequest<S>) -> Result<middleware::Started> { fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started> {
let to = Delay::new(Instant::now() + Duration::from_millis(10)); let to = Delay::new(Instant::now() + Duration::from_millis(10));
let start = Arc::clone(&self.start); let start = Arc::clone(&self.start);
@ -445,7 +445,7 @@ impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
} }
fn response( fn response(
&self, _: &mut HttpRequest<S>, resp: HttpResponse, &self, _: &HttpRequest<S>, resp: HttpResponse,
) -> Result<middleware::Response> { ) -> Result<middleware::Response> {
let to = Delay::new(Instant::now() + Duration::from_millis(10)); let to = Delay::new(Instant::now() + Duration::from_millis(10));
@ -458,7 +458,7 @@ impl<S> middleware::Middleware<S> for MiddlewareAsyncTest {
))) )))
} }
fn finish(&self, _: &mut HttpRequest<S>, _: &HttpResponse) -> middleware::Finished { fn finish(&self, _: &HttpRequest<S>, _: &HttpResponse) -> middleware::Finished {
let to = Delay::new(Instant::now() + Duration::from_millis(10)); let to = Delay::new(Instant::now() + Duration::from_millis(10));
let finish = Arc::clone(&self.finish); let finish = Arc::clone(&self.finish);
@ -697,7 +697,7 @@ fn test_async_resource_middleware() {
}; };
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw); r.middleware(mw);
r.h(|_| HttpResponse::Ok()); r.f(|_| HttpResponse::Ok());
}) })
}); });
@ -736,7 +736,7 @@ fn test_async_resource_middleware_multiple() {
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw1); r.middleware(mw1);
r.middleware(mw2); r.middleware(mw2);
r.h(|_| HttpResponse::Ok()); r.f(|_| HttpResponse::Ok());
}) })
}); });
@ -775,7 +775,7 @@ fn test_async_sync_resource_middleware_multiple() {
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw1); r.middleware(mw1);
r.middleware(mw2); r.middleware(mw2);
r.h(|_| HttpResponse::Ok()); r.f(|_| HttpResponse::Ok());
}) })
}); });
@ -793,7 +793,7 @@ fn test_async_sync_resource_middleware_multiple() {
struct MiddlewareWithErr; struct MiddlewareWithErr;
impl<S> middleware::Middleware<S> for MiddlewareWithErr { impl<S> middleware::Middleware<S> for MiddlewareWithErr {
fn start(&self, _req: &mut HttpRequest<S>) -> Result<middleware::Started, Error> { fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started, Error> {
Err(ErrorInternalServerError("middleware error")) Err(ErrorInternalServerError("middleware error"))
} }
} }
@ -801,7 +801,7 @@ impl<S> middleware::Middleware<S> for MiddlewareWithErr {
struct MiddlewareAsyncWithErr; struct MiddlewareAsyncWithErr;
impl<S> middleware::Middleware<S> for MiddlewareAsyncWithErr { impl<S> middleware::Middleware<S> for MiddlewareAsyncWithErr {
fn start(&self, _req: &mut HttpRequest<S>) -> Result<middleware::Started, Error> { fn start(&self, _: &HttpRequest<S>) -> Result<middleware::Started, Error> {
Ok(middleware::Started::Future(Box::new(future::err( Ok(middleware::Started::Future(Box::new(future::err(
ErrorInternalServerError("middleware error"), ErrorInternalServerError("middleware error"),
)))) ))))
@ -827,7 +827,7 @@ fn test_middleware_chain_with_error() {
App::new() App::new()
.middleware(mw1) .middleware(mw1)
.middleware(MiddlewareWithErr) .middleware(MiddlewareWithErr)
.resource("/test", |r| r.h(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
}); });
let request = srv.get().uri(srv.url("/test")).finish().unwrap(); let request = srv.get().uri(srv.url("/test")).finish().unwrap();
@ -857,7 +857,7 @@ fn test_middleware_async_chain_with_error() {
App::new() App::new()
.middleware(mw1) .middleware(mw1)
.middleware(MiddlewareAsyncWithErr) .middleware(MiddlewareAsyncWithErr)
.resource("/test", |r| r.h(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
}); });
let request = srv.get().uri(srv.url("/test")).finish().unwrap(); let request = srv.get().uri(srv.url("/test")).finish().unwrap();
@ -888,7 +888,7 @@ fn test_scope_middleware_chain_with_error() {
scope scope
.middleware(mw1) .middleware(mw1)
.middleware(MiddlewareWithErr) .middleware(MiddlewareWithErr)
.resource("/test", |r| r.h(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
}) })
}); });
@ -920,7 +920,7 @@ fn test_scope_middleware_async_chain_with_error() {
scope scope
.middleware(mw1) .middleware(mw1)
.middleware(MiddlewareAsyncWithErr) .middleware(MiddlewareAsyncWithErr)
.resource("/test", |r| r.h(|_| HttpResponse::Ok())) .resource("/test", |r| r.f(|_| HttpResponse::Ok()))
}) })
}); });
@ -951,7 +951,7 @@ fn test_resource_middleware_chain_with_error() {
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw1); r.middleware(mw1);
r.middleware(MiddlewareWithErr); r.middleware(MiddlewareWithErr);
r.h(|_| HttpResponse::Ok()); r.f(|_| HttpResponse::Ok());
}) })
}); });
@ -982,7 +982,7 @@ fn test_resource_middleware_async_chain_with_error() {
App::new().resource("/test", move |r| { App::new().resource("/test", move |r| {
r.middleware(mw1); r.middleware(mw1);
r.middleware(MiddlewareAsyncWithErr); r.middleware(MiddlewareAsyncWithErr);
r.h(|_| HttpResponse::Ok()); r.f(|_| HttpResponse::Ok());
}) })
}); });

View file

@ -368,8 +368,8 @@ fn test_head_empty() {
} }
// read response // read response
//let bytes = srv.execute(response.body()).unwrap(); // let bytes = srv.execute(response.body()).unwrap();
//assert!(bytes.is_empty()); // assert!(bytes.is_empty());
} }
#[test] #[test]
@ -524,7 +524,7 @@ fn test_body_brotli() {
#[test] #[test]
fn test_gzip_encoding() { fn test_gzip_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -557,7 +557,7 @@ fn test_gzip_encoding() {
fn test_gzip_encoding_large() { fn test_gzip_encoding_large() {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -594,7 +594,7 @@ fn test_reading_gzip_encoding_large_random() {
.collect::<String>(); .collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -627,7 +627,7 @@ fn test_reading_gzip_encoding_large_random() {
#[test] #[test]
fn test_reading_deflate_encoding() { fn test_reading_deflate_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -660,7 +660,7 @@ fn test_reading_deflate_encoding() {
fn test_reading_deflate_encoding_large() { fn test_reading_deflate_encoding_large() {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -697,7 +697,7 @@ fn test_reading_deflate_encoding_large_random() {
.collect::<String>(); .collect::<String>();
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -731,7 +731,7 @@ fn test_reading_deflate_encoding_large_random() {
#[test] #[test]
fn test_brotli_encoding() { fn test_brotli_encoding() {
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()
@ -765,7 +765,7 @@ fn test_brotli_encoding() {
fn test_brotli_encoding_large() { fn test_brotli_encoding_large() {
let data = STR.repeat(10); let data = STR.repeat(10);
let mut srv = test::TestServer::new(|app| { let mut srv = test::TestServer::new(|app| {
app.handler(|req: HttpRequest| { app.handler(|req: &HttpRequest| {
req.body() req.body()
.and_then(|bytes: Bytes| { .and_then(|bytes: Bytes| {
Ok(HttpResponse::Ok() Ok(HttpResponse::Ok()