From fec6047ddc4179b68831892b12b031cb23248a5c Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Mon, 25 Jun 2018 10:58:04 +0600 Subject: [PATCH] refactor HttpRequest mutability --- src/application.rs | 218 ++++++------ src/client/mod.rs | 1 + src/client/pipeline.rs | 16 +- src/client/response.rs | 31 +- src/error.rs | 3 - src/extractor.rs | 68 ++-- src/fs.rs | 63 ++-- src/handler.rs | 25 +- src/helpers.rs | 124 ++++--- src/httpmessage.rs | 403 ++++++++++++---------- src/httprequest.rs | 496 ++++++++------------------- src/httpresponse.rs | 35 +- src/info.rs | 103 +++--- src/json.rs | 197 ++++++----- src/lib.rs | 2 + src/middleware/cors.rs | 68 ++-- src/middleware/csrf.rs | 25 +- src/middleware/defaultheaders.rs | 11 +- src/middleware/errhandlers.rs | 19 +- src/middleware/identity.rs | 60 ++-- src/middleware/logger.rs | 44 +-- src/middleware/mod.rs | 9 +- src/middleware/session.rs | 11 +- src/param.rs | 23 +- src/payload.rs | 17 +- src/pipeline.rs | 98 +++--- src/pred.rs | 196 ++++------- src/resource.rs | 45 ++- src/route.rs | 66 ++-- src/router.rs | 567 ++++++++++++++++++------------- src/scope.rs | 228 ++++++------- src/server/error.rs | 25 ++ src/server/h1.rs | 148 +++++--- src/server/h1decoder.rs | 29 +- src/server/h1writer.rs | 61 ++-- src/server/h2.rs | 29 +- src/server/h2writer.rs | 16 +- src/server/helpers.rs | 84 ----- src/server/message.rs | 220 ++++++++++++ src/server/mod.rs | 18 +- src/server/output.rs | 74 ++-- src/server/settings.rs | 28 +- src/server/srv.rs | 26 +- src/server/worker.rs | 5 +- src/test.rs | 72 +++- src/with.rs | 8 +- src/ws/client.rs | 6 +- src/ws/mod.rs | 196 ++++------- tests/test_client.rs | 20 +- tests/test_middleware.rs | 38 +-- tests/test_server.rs | 20 +- 51 files changed, 2239 insertions(+), 2156 deletions(-) create mode 100644 src/server/error.rs create mode 100644 src/server/message.rs diff --git a/src/application.rs b/src/application.rs index 906e8f9a0..2cf7a4fab 100644 --- a/src/application.rs +++ b/src/application.rs @@ -7,12 +7,13 @@ use http::{Method, StatusCode}; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::Middleware; +use param::Params; use pipeline::{HandlerType, Pipeline, PipelineHandler}; use pred::Predicate; use resource::ResourceHandler; -use router::{Resource, Router}; +use router::{Resource, RouteInfo, Router}; use scope::Scope; -use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, ServerSettings}; +use server::{HttpHandler, HttpHandlerTask, IntoHttpHandler, Request, ServerSettings}; /// Application pub struct HttpApplication { @@ -46,36 +47,34 @@ impl PipelineHandler for Inner { } fn handle( - &self, req: HttpRequest, htype: HandlerType, + &self, req: &HttpRequest, htype: HandlerType, ) -> AsyncResult { match htype { - HandlerType::Normal(idx) => match self.resources[idx].handle(req) { - Ok(result) => result, - Err(req) => match self.default.handle(req) { - Ok(result) => result, - Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), - }, - }, + HandlerType::Normal(idx) => { + if let Some(id) = self.resources[idx].get_route_id(req) { + return self.resources[idx].handle(id, req); + } + } HandlerType::Handler(idx) => match self.handlers[idx] { - PrefixHandlerType::Handler(_, ref hnd) => hnd.handle(req), - PrefixHandlerType::Scope(_, ref hnd, _) => hnd.handle(req), - }, - HandlerType::Default => match self.default.handle(req) { - Ok(result) => result, - Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), + PrefixHandlerType::Handler(_, ref hnd) => return hnd.handle(req), + PrefixHandlerType::Scope(_, ref hnd, _) => return hnd.handle(req), }, + _ => (), + } + if let Some(id) = self.default.get_route_id(req) { + self.default.handle(id, req) + } else { + AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)) } } } impl HttpApplication { #[inline] - fn get_handler(&self, req: &mut HttpRequest) -> HandlerType { - if let Some(idx) = self.router.recognize(req) { - HandlerType::Normal(idx) + fn get_handler(&self, req: &Request) -> (RouteInfo, HandlerType) { + if let Some((idx, info)) = self.router.recognize(req) { + (info, HandlerType::Normal(idx)) } else { - req.match_info_mut().set_tail(0); - 'outer: for idx in 0..self.inner.handlers.len() { match self.inner.handlers[idx] { PrefixHandlerType::Handler(ref prefix, _) => { @@ -90,77 +89,68 @@ impl HttpApplication { if m { let prefix_len = (self.inner.prefix + prefix.len()) as u16; - let url = req.url().clone(); - req.set_prefix_len(prefix_len); - req.match_info_mut().set_url(url); - req.match_info_mut().set_tail(prefix_len); - return HandlerType::Handler(idx); + let info = self.router.route_info(req, prefix_len); + return (info, HandlerType::Handler(idx)); } } PrefixHandlerType::Scope(ref pattern, _, ref filters) => { - if let Some(prefix_len) = + if let Some(params) = pattern.match_prefix_with_params(req, self.inner.prefix) { for filter in filters { - if !filter.check(req) { + if !filter.check(req, &self.state) { continue 'outer; } } - - let prefix_len = (self.inner.prefix + prefix_len) as u16; - let url = req.url().clone(); - req.set_prefix_len(prefix_len); - let params = req.match_info_mut(); - params.set_tail(prefix_len); - params.set_url(url); - return HandlerType::Handler(idx); + let info = self + .router + .route_info_params(params, self.inner.prefix as u16); + return (info, HandlerType::Handler(idx)); } } } } - HandlerType::Default + ( + self.router.default_route_info(self.inner.prefix as u16), + HandlerType::Default, + ) } } #[cfg(test)] - pub(crate) fn run(&self, mut req: HttpRequest) -> AsyncResult { - let tp = self.get_handler(&mut req); - self.inner.handle(req, tp) - } + pub(crate) fn run(&self, mut req: Request) -> AsyncResult { + let (info, tp) = self.get_handler(&req); + let req = HttpRequest::new(req, Rc::clone(&self.state), info); - #[cfg(test)] - pub(crate) fn prepare_request(&self, req: HttpRequest) -> HttpRequest { - req.with_state(Rc::clone(&self.state), self.router.clone()) + self.inner.handle(&req, tp) } } impl HttpHandler for HttpApplication { type Task = Pipeline>; - fn handle(&self, req: HttpRequest) -> Result>, HttpRequest> { + fn handle(&self, mut msg: Request) -> Result>, Request> { let m = { - let path = req.path(); + let path = msg.path(); path.starts_with(&self.prefix) && (path.len() == self.prefix_len || path.split_at(self.prefix_len).1.starts_with('/')) }; if m { - let mut req2 = - req.clone_with_state(Rc::clone(&self.state), self.router.clone()); - if let Some(ref filters) = self.filters { for filter in filters { - if !filter.check(&mut req2) { - return Err(req); + if !filter.check(&msg, &self.state) { + return Err(msg); } } } - let tp = self.get_handler(&mut req2); + let (info, tp) = self.get_handler(&msg); 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 { - Err(req) + Err(msg) } } } @@ -168,7 +158,6 @@ impl HttpHandler for HttpApplication { struct ApplicationParts { state: S, prefix: String, - settings: ServerSettings, default: Rc>, resources: Vec<(Resource, Option>)>, handlers: Vec>, @@ -219,7 +208,6 @@ where parts: Some(ApplicationParts { state, prefix: "/".to_owned(), - settings: ServerSettings::default(), default: Rc::new(ResourceHandler::default_not_found()), resources: Vec::new(), handlers: Vec::new(), @@ -498,7 +486,7 @@ where /// # extern crate actix_web; /// use actix_web::{App, HttpRequest, HttpResponse, Result}; /// - /// fn index(mut req: HttpRequest) -> Result { + /// fn index(req: &HttpRequest) -> Result { /// let url = req.url_for("youtube", &["oHg5SJYRHA0"])?; /// assert_eq!(url.as_str(), "https://youtube.com/watch/oHg5SJYRHA0"); /// Ok(HttpResponse::Ok().into()) @@ -544,7 +532,7 @@ where /// use actix_web::{http, App, HttpRequest, HttpResponse}; /// /// 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::POST => HttpResponse::MethodNotAllowed(), /// _ => HttpResponse::NotFound(), @@ -636,8 +624,7 @@ where }; } - let (router, resources) = - Router::new(&prefix, parts.settings.clone(), resources); + let (router, resources) = Router::new(&prefix, resources); let inner = Rc::new(Inner { prefix: prefix_len, @@ -708,7 +695,7 @@ struct BoxedApplication { impl HttpHandler for BoxedApplication { type Task = Box; - fn handle(&self, req: HttpRequest) -> Result { + fn handle(&self, req: Request) -> Result { self.app.handle(req).map(|t| { let task: Self::Task = Box::new(t); task @@ -719,11 +706,7 @@ impl HttpHandler for BoxedApplication { impl IntoHttpHandler for App { type Handler = HttpApplication; - fn into_handler(mut self, settings: ServerSettings) -> HttpApplication { - { - let parts = self.parts.as_mut().expect("Use after finish"); - parts.settings = settings; - } + fn into_handler(mut self) -> HttpApplication { self.finish() } } @@ -731,11 +714,7 @@ impl IntoHttpHandler for App { impl<'a, S: 'static> IntoHttpHandler for &'a mut App { type Handler = HttpApplication; - fn into_handler(self, settings: ServerSettings) -> HttpApplication { - { - let parts = self.parts.as_mut().expect("Use after finish"); - parts.settings = settings; - } + fn into_handler(self) -> HttpApplication { self.finish() } } @@ -769,18 +748,18 @@ mod tests { .resource("/test", |r| r.f(|_| HttpResponse::Ok())) .finish(); - let req = TestRequest::with_uri("/test").finish(); + let req = TestRequest::with_uri("/test").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); let app = App::new() .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) .finish(); - let req = TestRequest::with_uri("/blah").finish(); + let req = TestRequest::with_uri("/blah").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); } @@ -791,7 +770,8 @@ mod tests { .prefix("/test") .resource("/test", |r| r.f(|_| HttpResponse::Ok())) .finish(); - assert!(app.handle(HttpRequest::default()).is_err()); + let ctx = TestRequest::default().request(); + assert!(app.handle(ctx).is_err()); } #[test] @@ -799,8 +779,7 @@ mod tests { let app = App::with_state(10) .resource("/", |r| r.f(|_| HttpResponse::Ok())) .finish(); - let req = - HttpRequest::default().with_state(Rc::clone(&app.state), app.router.clone()); + let req = TestRequest::with_state(10).request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -811,69 +790,73 @@ mod tests { .prefix("/test") .resource("/blah", |r| r.f(|_| HttpResponse::Ok())) .finish(); - let req = TestRequest::with_uri("/test").finish(); + let req = TestRequest::with_uri("/test").request(); let resp = app.handle(req); assert!(resp.is_ok()); - let req = TestRequest::with_uri("/test/").finish(); + let req = TestRequest::with_uri("/test/").request(); let resp = app.handle(req); 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); assert!(resp.is_ok()); - let req = TestRequest::with_uri("/testing").finish(); + let req = TestRequest::with_uri("/testing").request(); let resp = app.handle(req); assert!(resp.is_err()); } #[test] 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); 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); 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); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } #[test] 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); 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); 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); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -882,26 +865,26 @@ mod tests { fn test_handler_with_prefix() { let app = App::new() .prefix("prefix") - .handler("/test", |_| HttpResponse::Ok()) + .handler("/test", |_: &_| HttpResponse::Ok()) .finish(); - let req = TestRequest::with_uri("/prefix/test").finish(); + let req = TestRequest::with_uri("/prefix/test").request(); let resp = app.run(req); 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); 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); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -915,15 +898,19 @@ mod tests { }) .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); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -932,31 +919,30 @@ mod tests { fn test_handler_prefix() { let app = App::new() .prefix("/app") - .handler("/test", |_| HttpResponse::Ok()) + .handler("/test", |_: &_| HttpResponse::Ok()) .finish(); - let req = TestRequest::with_uri("/test").finish(); + let req = TestRequest::with_uri("/test").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); - let req = TestRequest::with_uri("/app/test").finish(); - 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 req = TestRequest::with_uri("/app/test").request(); let resp = app.run(req); 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); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -966,7 +952,7 @@ mod tests { let mut srv = TestServer::with_factory(|| { App::new() .filter(pred::Get()) - .handler("/test", |_| HttpResponse::Ok()) + .handler("/test", |_: &_| HttpResponse::Ok()) }); let request = srv.get().uri(srv.url("/test")).finish().unwrap(); @@ -985,11 +971,11 @@ mod tests { .resource("/some", |r| r.f(|_| Some("some"))) .finish(); - let req = TestRequest::with_uri("/none").finish(); + let req = TestRequest::with_uri("/none").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::OK); assert_eq!(resp.as_msg().body(), &Body::Binary(Binary::Slice(b"some"))); diff --git a/src/client/mod.rs b/src/client/mod.rs index 5685e093f..a0713fe32 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -35,6 +35,7 @@ pub use self::connector::{ Pause, Resume, }; pub(crate) use self::parser::{HttpResponseParser, HttpResponseParserError}; +pub(crate) use self::pipeline::Pipeline; pub use self::pipeline::{SendRequest, SendRequestError}; pub use self::request::{ClientRequest, ClientRequestBuilder}; pub use self::response::ClientResponse; diff --git a/src/client/pipeline.rs b/src/client/pipeline.rs index 2886b42f2..fbbce4546 100644 --- a/src/client/pipeline.rs +++ b/src/client/pipeline.rs @@ -1,6 +1,6 @@ use bytes::{Bytes, BytesMut}; use futures::sync::oneshot; -use futures::{Async, Future, Poll}; +use futures::{Async, Future, Poll, Stream}; use http::header::CONTENT_ENCODING; use std::time::{Duration, Instant}; use std::{io, mem}; @@ -230,7 +230,7 @@ impl Future for SendRequest { } } -pub(crate) struct Pipeline { +pub struct Pipeline { body: IoBody, body_completed: bool, conn: Option, @@ -315,7 +315,7 @@ impl Pipeline { } #[inline] - pub fn poll(&mut self) -> Poll, PayloadError> { + pub(crate) fn poll(&mut self) -> Poll, PayloadError> { if self.conn.is_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 { + type Item = Bytes; + type Error = PayloadError; + + fn poll(&mut self) -> Poll, Self::Error> { + Pipeline::poll(self) + } +} diff --git a/src/client/response.rs b/src/client/response.rs index 28d6f51bf..a5c23014e 100644 --- a/src/client/response.rs +++ b/src/client/response.rs @@ -1,3 +1,4 @@ +use std::cell::RefCell; use std::{fmt, str}; use bytes::Bytes; @@ -30,23 +31,33 @@ impl Default for ClientMessage { } /// An HTTP Client response -pub struct ClientResponse(ClientMessage, Option>); +pub struct ClientResponse(ClientMessage, RefCell>>); impl HttpMessage for ClientResponse { + type Stream = Box; + /// Get the headers from the response. #[inline] fn headers(&self) -> &HeaderMap { &self.0.headers } + + #[inline] + fn payload(&self) -> Box { + self.1 + .borrow_mut() + .take() + .expect("Payload is already consumed.") + } } impl 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) { - self.1 = Some(pl); + *self.1.borrow_mut() = Some(pl); } /// 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, Self::Error> { - if let Some(ref mut pl) = self.1 { - pl.poll() - } else { - Ok(Async::Ready(None)) - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/error.rs b/src/error.rs index 6d8d3b042..129de76b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -612,9 +612,6 @@ pub enum UrlGenerationError { /// Not all path pattern covered #[fail(display = "Not all path pattern covered")] NotEnoughElements, - /// Router is not available - #[fail(display = "Router is not available")] - RouterNotAvailable, /// URL parse error #[fail(display = "{}", _0)] ParseError(#[cause] UrlParseError), diff --git a/src/extractor.rs b/src/extractor.rs index 5ace390dc..1b75f9f14 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -267,12 +267,12 @@ where #[inline] fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { - let req = req.clone(); + let req2 = req.clone(); let err = Rc::clone(&cfg.ehandler); Box::new( - UrlEncoded::new(req.clone()) + UrlEncoded::new(req) .limit(cfg.limit) - .map_err(move |e| (*err)(e, req)) + .map_err(move |e| (*err)(e, &req2)) .map(Form), ) } @@ -321,7 +321,7 @@ impl fmt::Display for Form { /// ``` pub struct FormConfig { limit: usize, - ehandler: Rc) -> Error>, + ehandler: Rc) -> Error>, } impl FormConfig { @@ -334,7 +334,7 @@ impl FormConfig { /// Set custom error handler pub fn error_handler(&mut self, f: F) -> &mut Self where - F: Fn(UrlencodedError, HttpRequest) -> Error + 'static, + F: Fn(UrlencodedError, &HttpRequest) -> Error + 'static, { self.ehandler = Rc::new(f); self @@ -383,9 +383,7 @@ impl FromRequest for Bytes { // check content-type cfg.check_mimetype(req)?; - Ok(Box::new( - MessageBody::new(req.clone()).limit(cfg.limit).from_err(), - )) + Ok(Box::new(MessageBody::new(req).limit(cfg.limit).from_err())) } } @@ -429,7 +427,7 @@ impl FromRequest for String { let encoding = req.encoding()?; Ok(Box::new( - MessageBody::new(req.clone()) + MessageBody::new(req) .limit(cfg.limit) .from_err() .and_then(move |body| { @@ -617,7 +615,6 @@ mod tests { use mime; use resource::ResourceHandler; use router::{Resource, Router}; - use server::ServerSettings; use test::TestRequest; #[derive(Deserialize, Debug, PartialEq)] @@ -628,9 +625,9 @@ mod tests { #[test] fn test_bytes() { let cfg = PayloadConfig::default(); - let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); - req.payload_mut() - .unread_data(Bytes::from_static(b"hello=world")); + let req = TestRequest::with_header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); match Bytes::from_request(&req, &cfg).unwrap().poll().unwrap() { Async::Ready(s) => { @@ -643,9 +640,9 @@ mod tests { #[test] fn test_string() { let cfg = PayloadConfig::default(); - let mut req = TestRequest::with_header(header::CONTENT_LENGTH, "11").finish(); - req.payload_mut() - .unread_data(Bytes::from_static(b"hello=world")); + let req = TestRequest::with_header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) + .finish(); match String::from_request(&req, &cfg).unwrap().poll().unwrap() { Async::Ready(s) => { @@ -657,13 +654,12 @@ mod tests { #[test] fn test_form() { - let mut req = TestRequest::with_header( + let req = TestRequest::with_header( header::CONTENT_TYPE, "application/x-www-form-urlencoded", ).header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) .finish(); - req.payload_mut() - .unread_data(Bytes::from_static(b"hello=world")); let mut cfg = FormConfig::default(); cfg.limit(4096); @@ -677,7 +673,7 @@ mod tests { #[test] fn test_payload_config() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); let mut cfg = PayloadConfig::default(); cfg.mimetype(mime::APPLICATION_JSON); assert!(cfg.check_mimetype(&req).is_err()); @@ -712,14 +708,15 @@ mod tests { #[test] 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(); resource.name("index"); let mut routes = Vec::new(); routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource))); - let (router, _) = Router::new("", ServerSettings::default(), routes); - assert!(router.recognize(&mut req).is_some()); + let (router, _) = Router::new("", routes); + let info = router.recognize(&req).unwrap().1; + let req = req.with_route_info(info); let s = Path::::from_request(&req, &()).unwrap(); assert_eq!(s.key, "name"); @@ -732,8 +729,9 @@ mod tests { let s = Query::::from_request(&req, &()).unwrap(); assert_eq!(s.id, "test"); - let mut req = TestRequest::with_uri("/name/32/").finish(); - assert!(router.recognize(&mut req).is_some()); + let req = TestRequest::with_uri("/name/32/").finish_with_router(router.clone()); + let info = router.recognize(&req).unwrap().1; + let req = req.with_route_info(info); let s = Path::::from_request(&req, &()).unwrap(); assert_eq!(s.as_ref().key, "name"); @@ -754,24 +752,26 @@ mod tests { resource.name("index"); let mut routes = Vec::new(); 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(); - assert!(router.recognize(&mut req).is_some()); - - assert_eq!(*Path::::from_request(&mut req, &()).unwrap(), 32); + let req = TestRequest::with_uri("/32/").finish_with_router(router.clone()); + let info = router.recognize(&req).unwrap().1; + let req = req.with_route_info(info); + assert_eq!(*Path::::from_request(&req, &()).unwrap(), 32); } #[test] fn test_tuple_extract() { - let mut req = TestRequest::with_uri("/name/user1/?id=test").finish(); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let mut routes = Vec::new(); routes.push((Resource::new("index", "/{key}/{value}/"), Some(resource))); - let (router, _) = Router::new("", ServerSettings::default(), routes); - assert!(router.recognize(&mut req).is_some()); + let (router, _) = Router::new("", routes); + + 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() { Ok(Async::Ready(res)) => res, diff --git a/src/fs.rs b/src/fs.rs index d2ac4b9b9..663788234 100644 --- a/src/fs.rs +++ b/src/fs.rs @@ -2,6 +2,7 @@ use std::fmt::Write; use std::fs::{DirEntry, File, Metadata}; use std::io::{Read, Seek}; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; 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 error::Error; -use handler::{AsyncResult, Handler, Responder, RouteHandler, WrapHandler}; +use handler::{ + AsyncResult, AsyncResultItem, Handler, Responder, RouteHandler, WrapHandler, +}; use header; use http::{ContentEncoding, Method, StatusCode}; use httpmessage::HttpMessage; @@ -610,7 +613,7 @@ impl StaticFiles { index: None, show_index: false, cpu_pool: pool, - default: Box::new(WrapHandler::new(|_| { + default: Box::new(WrapHandler::new(|_: &_| { HttpResponse::new(StatusCode::NOT_FOUND) })), renderer: Box::new(directory_listing), @@ -657,7 +660,7 @@ impl StaticFiles { impl Handler for StaticFiles { type Result = Result, Error>; - fn handle(&self, req: HttpRequest) -> Self::Result { + fn handle(&self, req: &HttpRequest) -> Self::Result { if !self.accessible { Ok(self.default.handle(req)) } else { @@ -667,7 +670,9 @@ impl Handler for StaticFiles { .map(|tail| PathBuf::from_param(tail.trim_left_matches('/'))) { Some(Ok(path)) => path, - _ => return Ok(self.default.handle(req)), + _ => { + return Ok(self.default.handle(req)); + } }; // full filepath @@ -833,7 +838,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml" @@ -858,7 +864,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/xml" @@ -882,7 +889,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "image/png" @@ -916,7 +924,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "image/png" @@ -940,7 +949,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "application/octet-stream" @@ -965,7 +975,8 @@ mod tests { 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!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "text/x-toml" @@ -1136,7 +1147,6 @@ mod tests { .unwrap(); assert_eq!(te, "chunked"); } - let bytes = srv.execute(response.body()).unwrap(); let data = Bytes::from(fs::read("tests/test.binary").unwrap()); assert_eq!(bytes, data); @@ -1178,27 +1188,22 @@ mod tests { fn test_static_files() { let mut st = StaticFiles::new(".").show_files_listing(); st.accessible = false; - let resp = st - .handle(HttpRequest::default()) - .respond_to(&HttpRequest::default()) - .unwrap(); + let req = TestRequest::default().finish(); + let resp = st.handle(&req).respond_to(&req).unwrap(); let resp = resp.as_msg(); assert_eq!(resp.status(), StatusCode::NOT_FOUND); st.accessible = true; st.show_index = false; - let resp = st - .handle(HttpRequest::default()) - .respond_to(&HttpRequest::default()) - .unwrap(); + let req = TestRequest::default().finish(); + let resp = st.handle(&req).respond_to(&req).unwrap(); let resp = resp.as_msg(); assert_eq!(resp.status(), StatusCode::NOT_FOUND); - let mut req = HttpRequest::default(); - req.match_info_mut().add_static("tail", ""); + let req = TestRequest::default().param("tail", "").finish(); 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(); assert_eq!( resp.headers().get(header::CONTENT_TYPE).unwrap(), @@ -1213,7 +1218,7 @@ mod tests { let st = StaticFiles::new(".").index_file("index.html"); 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(); assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!( @@ -1222,8 +1227,7 @@ mod tests { ); 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(); assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!( @@ -1234,15 +1238,14 @@ mod tests { #[test] fn test_redirect_to_index_nested() { - let st = StaticFiles::new(".").index_file("Cargo.toml"); - let req = TestRequest::default().uri("/tools/wsload").finish(); - - let resp = st.handle(req).respond_to(&HttpRequest::default()).unwrap(); + let st = StaticFiles::new(".").index_file("mod.rs"); + let req = TestRequest::default().uri("/src/client").finish(); + let resp = st.handle(&req).respond_to(&req).unwrap(); let resp = resp.as_msg(); assert_eq!(resp.status(), StatusCode::FOUND); assert_eq!( resp.headers().get(header::LOCATION).unwrap(), - "/tools/wsload/Cargo.toml" + "/src/client/mod.rs" ); } diff --git a/src/handler.rs b/src/handler.rs index 61dd5694b..c7fd63982 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -10,6 +10,7 @@ use http::StatusCode; use httprequest::HttpRequest; use httpresponse::HttpResponse; use resource::ResourceHandler; +use server::Request; /// Trait defines object that could be registered as route handler #[allow(unused_variables)] @@ -18,7 +19,7 @@ pub trait Handler: 'static { type Result: Responder; /// Handle request - fn handle(&self, req: HttpRequest) -> Self::Result; + fn handle(&self, req: &HttpRequest) -> Self::Result; } /// Trait implemented by types that generate responses for clients. @@ -203,12 +204,12 @@ where /// Handler for Fn() impl Handler for F where - F: Fn(HttpRequest) -> R + 'static, + F: Fn(&HttpRequest) -> R + 'static, R: Responder + 'static, { type Result = R; - fn handle(&self, req: HttpRequest) -> R { + fn handle(&self, req: &HttpRequest) -> R { (self)(req) } } @@ -402,9 +403,8 @@ where } } -// /// Trait defines object that could be registered as resource route pub(crate) trait RouteHandler: 'static { - fn handle(&self, req: HttpRequest) -> AsyncResult; + fn handle(&self, &HttpRequest) -> AsyncResult; fn has_default_resource(&self) -> bool { false @@ -443,8 +443,8 @@ where R: Responder + 'static, S: 'static, { - fn handle(&self, req: HttpRequest) -> AsyncResult { - match self.h.handle(req.clone()).respond_to(&req) { + fn handle(&self, req: &HttpRequest) -> AsyncResult { + match self.h.handle(req).respond_to(req) { Ok(reply) => reply.into(), Err(err) => AsyncResult::err(err.into()), } @@ -454,7 +454,7 @@ where /// Async route handler pub(crate) struct AsyncHandler where - H: Fn(HttpRequest) -> F + 'static, + H: Fn(&HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, @@ -466,7 +466,7 @@ where impl AsyncHandler where - H: Fn(HttpRequest) -> F + 'static, + H: Fn(&HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, @@ -482,14 +482,15 @@ where impl RouteHandler for AsyncHandler where - H: Fn(HttpRequest) -> F + 'static, + H: Fn(&HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, S: 'static, { - fn handle(&self, req: HttpRequest) -> AsyncResult { - let fut = (self.h)(req.clone()).map_err(|e| e.into()).then(move |r| { + fn handle(&self, req: &HttpRequest) -> AsyncResult { + let req = req.clone(); + let fut = (self.h)(&req).map_err(|e| e.into()).then(move |r| { match r.respond_to(&req) { Ok(reply) => match reply.into().into() { AsyncResultItem::Ok(resp) => Either::A(ok(resp)), diff --git a/src/helpers.rs b/src/helpers.rs index 0b35f047c..50a9bcf6a 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -36,7 +36,7 @@ use httpresponse::HttpResponse; /// # use actix_web::*; /// use actix_web::http::NormalizePath; /// -/// # fn index(req: HttpRequest) -> HttpResponse { +/// # fn index(req: &HttpRequest) -> HttpResponse { /// # HttpResponse::Ok().into() /// # } /// fn main() { @@ -86,57 +86,41 @@ impl NormalizePath { impl Handler for NormalizePath { type Result = HttpResponse; - fn handle(&self, req: HttpRequest) -> Self::Result { - if let Some(router) = req.router() { - let query = req.query_string(); - if self.merge { - // merge slashes - let p = self.re_merge.replace_all(req.path(), "/"); - if p.len() != req.path().len() { - if router.has_route(p.as_ref()) { + fn handle(&self, req: &HttpRequest) -> Self::Result { + let query = req.query_string(); + if self.merge { + // merge slashes + let p = self.re_merge.replace_all(req.path(), "/"); + if p.len() != req.path().len() { + if req.route().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() { p + "?" + query } else { p }; return HttpResponse::build(self.redirect) - .header(header::LOCATION, p.as_ref()) + .header(header::LOCATION, p.as_str()) .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 - 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 + // try to remove trailing slash + if p.ends_with('/') { 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); return if !query.is_empty() { req.header( @@ -148,22 +132,36 @@ impl Handler for NormalizePath { }.finish(); } } - } - // append trailing slash - if self.append && !req.path().ends_with('/') { - let p = req.path().to_owned() + "/"; - if router.has_route(&p) { - let p = if !query.is_empty() { - p + "?" + query + } else if p.ends_with('/') { + // try to remove trailing slash + let p = p.as_ref().trim_right_matches('/'); + if req.route().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 { - p - }; - return HttpResponse::build(self.redirect) - .header(header::LOCATION, p.as_str()) - .finish(); + req.header(header::LOCATION, p) + }.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) } } @@ -175,7 +173,7 @@ mod tests { use http::{header, Method}; use test::TestRequest; - fn index(_req: HttpRequest) -> HttpResponse { + fn index(_req: &HttpRequest) -> HttpResponse { HttpResponse::new(StatusCode::OK) } @@ -207,9 +205,9 @@ mod tests { ("/resource2/?p1=1&p2=2", "", StatusCode::OK), ]; 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 r = resp.as_msg(); + let r = &resp.as_msg(); assert_eq!(r.status(), code); if !target.is_empty() { assert_eq!( @@ -246,9 +244,9 @@ mod tests { ("/resource2/?p1=1&p2=2", StatusCode::OK), ]; 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 r = resp.as_msg(); + let r = &resp.as_msg(); assert_eq!(r.status(), code); } } @@ -329,9 +327,9 @@ mod tests { ), ]; 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 r = resp.as_msg(); + let r = &resp.as_msg(); assert_eq!(r.status(), code); if !target.is_empty() { assert_eq!( @@ -509,9 +507,9 @@ mod tests { ), ]; 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 r = resp.as_msg(); + let r = &resp.as_msg(); assert_eq!(r.status(), code); if !target.is_empty() { assert_eq!( diff --git a/src/httpmessage.rs b/src/httpmessage.rs index 5917e7fb3..8ed48de2c 100644 --- a/src/httpmessage.rs +++ b/src/httpmessage.rs @@ -16,12 +16,19 @@ use error::{ use header::Header; use json::JsonBody; use multipart::Multipart; +use payload::Payload; /// Trait that implements general purpose operations on http messages -pub trait HttpMessage { +pub trait HttpMessage: Sized { + /// Type of message payload stream + type Stream: Stream + Sized; + /// Read the message headers. fn headers(&self) -> &HeaderMap; + /// Message payload stream + fn payload(&self) -> Self::Stream; + #[doc(hidden)] /// Get a header fn get_header(&self) -> Option @@ -123,10 +130,7 @@ pub trait HttpMessage { /// } /// # fn main() {} /// ``` - fn body(self) -> MessageBody - where - Self: Stream + Sized, - { + fn body(&self) -> MessageBody { MessageBody::new(self) } @@ -160,10 +164,7 @@ pub trait HttpMessage { /// } /// # fn main() {} /// ``` - fn urlencoded(self) -> UrlEncoded - where - Self: Stream + Sized, - { + fn urlencoded(&self) -> UrlEncoded { UrlEncoded::new(self) } @@ -199,10 +200,7 @@ pub trait HttpMessage { /// } /// # fn main() {} /// ``` - fn json(self) -> JsonBody - where - Self: Stream + Sized, - { + fn json(&self) -> JsonBody { JsonBody::new(self) } @@ -241,45 +239,42 @@ pub trait HttpMessage { /// } /// # fn main() {} /// ``` - fn multipart(self) -> Multipart - where - Self: Stream + Sized, - { + fn multipart(&self) -> Multipart { let boundary = Multipart::boundary(self.headers()); - Multipart::new(boundary, self) + Multipart::new(boundary, self.payload()) } /// Return stream of lines. - fn readlines(self) -> Readlines - where - Self: Stream + Sized, - { + fn readlines(&self) -> Readlines { Readlines::new(self) } } /// Stream to read request line by line. -pub struct Readlines -where - T: HttpMessage + Stream + 'static, -{ - req: T, +pub struct Readlines { + stream: T::Stream, buff: BytesMut, limit: usize, checked_buff: bool, + encoding: EncodingRef, + err: Option, } -impl Readlines -where - T: HttpMessage + Stream + 'static, -{ +impl Readlines { /// 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 { - req, + stream: req.payload(), buff: BytesMut::with_capacity(262_144), limit: 262_144, checked_buff: true, + err: None, + encoding, } } @@ -288,17 +283,28 @@ where self.limit = limit; 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 Stream for Readlines -where - T: HttpMessage + Stream + 'static, -{ +impl Stream for Readlines { type Item = String; type Error = ReadlinesError; fn poll(&mut self) -> Poll, 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 if !self.checked_buff { let mut found: Option = None; @@ -313,13 +319,13 @@ where if ind + 1 > self.limit { 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 { str::from_utf8(&self.buff.split_to(ind + 1)) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - encoding + self.encoding .decode(&self.buff.split_to(ind + 1), DecoderTrap::Strict) .map_err(|_| ReadlinesError::EncodingError)? }; @@ -328,7 +334,7 @@ where self.checked_buff = true; } // poll req for more bytes - match self.req.poll() { + match self.stream.poll() { Ok(Async::Ready(Some(mut bytes))) => { // check if there is a newline in bytes let mut found: Option = None; @@ -343,13 +349,13 @@ where if ind + 1 > self.limit { 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 { str::from_utf8(&bytes.split_to(ind + 1)) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - encoding + self.encoding .decode(&bytes.split_to(ind + 1), DecoderTrap::Strict) .map_err(|_| ReadlinesError::EncodingError)? }; @@ -369,13 +375,13 @@ where if self.buff.len() > self.limit { 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 { str::from_utf8(&self.buff) .map_err(|_| ReadlinesError::EncodingError)? .to_owned() } else { - encoding + self.encoding .decode(&self.buff, DecoderTrap::Strict) .map_err(|_| ReadlinesError::EncodingError)? }; @@ -388,19 +394,36 @@ where } /// Future that resolves to a complete http message body. -pub struct MessageBody { +pub struct MessageBody { limit: usize, - req: Option, + length: Option, + stream: Option, + err: Option, fut: Option>>, } -impl MessageBody { - /// Create `RequestBody` for request. - pub fn new(req: T) -> MessageBody { +impl MessageBody { + /// Create `MessageBody` for request. + pub fn new(req: &T) -> MessageBody { + 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::() { + len = Some(l) + } else { + return Self::err(PayloadError::UnknownLength); + } + } else { + return Self::err(PayloadError::UnknownLength); + } + } + MessageBody { limit: 262_144, - req: Some(req), + length: len, + stream: Some(req.payload()), fut: None, + err: None, } } @@ -409,68 +432,114 @@ impl MessageBody { self.limit = limit; self } + + fn err(e: PayloadError) -> Self { + MessageBody { + stream: None, + limit: 262_144, + fut: None, + err: Some(e), + length: None, + } + } } impl Future for MessageBody where - T: HttpMessage + Stream + 'static, + T: HttpMessage + 'static, { type Item = Bytes; type Error = PayloadError; fn poll(&mut self) -> Poll { - if let Some(req) = self.req.take() { - if let Some(len) = req.headers().get(header::CONTENT_LENGTH) { - if let Ok(s) = len.to_str() { - if let Ok(len) = s.parse::() { - 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()), - )); + if let Some(ref mut fut) = self.fut { + return fut.poll(); } - self.fut - .as_mut() - .expect("UrlEncoded could not be used second time") - .poll() + if let Some(err) = self.err.take() { + return Err(err); + } + + 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. -pub struct UrlEncoded { - req: Option, +pub struct UrlEncoded { + stream: Option, limit: usize, + length: Option, + encoding: EncodingRef, + err: Option, fut: Option>>, } -impl UrlEncoded { +impl UrlEncoded { /// Create a new future to URL encode a request - pub fn new(req: T) -> UrlEncoded { + pub fn new(req: &T) -> UrlEncoded { + // 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::() { + len = Some(l) + } else { + return Self::err(UrlencodedError::UnknownLength); + } + } else { + return Self::err(UrlencodedError::UnknownLength); + } + }; + 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, fut: None, + err: Some(e), + length: None, + encoding: UTF_8, } } @@ -483,66 +552,58 @@ impl UrlEncoded { impl Future for UrlEncoded where - T: HttpMessage + Stream + 'static, + T: HttpMessage + 'static, U: DeserializeOwned + 'static, { type Item = U; type Error = UrlencodedError; fn poll(&mut self) -> Poll { - if let Some(req) = self.req.take() { - if let Some(len) = req.headers().get(header::CONTENT_LENGTH) { - if let Ok(s) = len.to_str() { - if let Ok(len) = s.parse::() { - 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::(&body) - .map_err(|_| UrlencodedError::Parse) - } else { - let body = encoding - .decode(&body, DecoderTrap::Strict) - .map_err(|_| UrlencodedError::Parse)?; - serde_urlencoded::from_str::(&body) - .map_err(|_| UrlencodedError::Parse) - } - }); - self.fut = Some(Box::new(fut)); + if let Some(ref mut fut) = self.fut { + return fut.poll(); } - self.fut - .as_mut() + if let Some(err) = self.err.take() { + 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") - .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::(&body) + .map_err(|_| UrlencodedError::Parse) + } else { + let body = encoding + .decode(&body, DecoderTrap::Strict) + .map_err(|_| UrlencodedError::Parse)?; + serde_urlencoded::from_str::(&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") .finish(); assert_eq!(req.content_type(), "application/json"); - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); assert_eq!(req.content_type(), ""); } @@ -574,7 +635,7 @@ mod tests { fn test_mime_type() { let req = TestRequest::with_header("content-type", "application/json").finish(); 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); let req = TestRequest::with_header("content-type", "application/json; charset=utf-8") @@ -596,7 +657,7 @@ mod tests { #[test] fn test_encoding() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); assert_eq!(UTF_8.name(), req.encoding().unwrap().name()); let req = TestRequest::with_header("content-type", "application/json").finish(); @@ -626,27 +687,19 @@ mod tests { #[test] fn test_chunked() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); assert!(!req.chunked().unwrap()); let req = TestRequest::with_header(header::TRANSFER_ENCODING, "chunked").finish(); assert!(req.chunked().unwrap()); - let mut headers = HeaderMap::new(); - let hdr = Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"); - - headers.insert( - header::TRANSFER_ENCODING, - header::HeaderValue::from_shared(hdr).unwrap(), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::TRANSFER_ENCODING, + Bytes::from_static(b"some va\xadscc\xacas0xsdasdlue"), + ) + .finish(); assert!(req.chunked().is_err()); } @@ -716,9 +769,8 @@ mod tests { header::CONTENT_TYPE, "application/x-www-form-urlencoded", ).header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) .finish(); - req.payload_mut() - .unread_data(Bytes::from_static(b"hello=world")); let result = req.urlencoded::().poll().ok().unwrap(); assert_eq!( @@ -732,9 +784,8 @@ mod tests { header::CONTENT_TYPE, "application/x-www-form-urlencoded; charset=utf-8", ).header(header::CONTENT_LENGTH, "11") + .set_payload(Bytes::from_static(b"hello=world")) .finish(); - req.payload_mut() - .unread_data(Bytes::from_static(b"hello=world")); let result = req.urlencoded().poll().ok().unwrap(); assert_eq!( @@ -759,16 +810,17 @@ mod tests { _ => unreachable!("error"), } - let mut req = HttpRequest::default(); - req.payload_mut().unread_data(Bytes::from_static(b"test")); + let req = TestRequest::default() + .set_payload(Bytes::from_static(b"test")) + .finish(); match req.body().poll().ok().unwrap() { Async::Ready(bytes) => assert_eq!(bytes, Bytes::from_static(b"test")), _ => unreachable!("error"), } - let mut req = HttpRequest::default(); - req.payload_mut() - .unread_data(Bytes::from_static(b"11111111111111")); + let mut req = TestRequest::default() + .set_payload(Bytes::from_static(b"11111111111111")) + .finish(); match req.body().limit(5).poll().err().unwrap() { PayloadError::Overflow => (), _ => unreachable!("error"), @@ -777,13 +829,14 @@ mod tests { #[test] fn test_readlines() { - let mut req = HttpRequest::default(); - req.payload_mut().unread_data(Bytes::from_static( - b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ - industry. Lorem Ipsum has been the industry's standard dummy\n\ - Contrary to popular belief, Lorem Ipsum is not simply random text.", - )); - let mut r = Readlines::new(req); + let req = TestRequest::default() + .set_payload(Bytes::from_static( + b"Lorem Ipsum is simply dummy text of the printing and typesetting\n\ + industry. Lorem Ipsum has been the industry's standard dummy\n\ + Contrary to popular belief, Lorem Ipsum is not simply random text.", + )) + .finish(); + let mut r = Readlines::new(&req); match r.poll().ok().unwrap() { Async::Ready(Some(s)) => assert_eq!( s, diff --git a/src/httprequest.rs b/src/httprequest.rs index ffd13919b..3cfcb68aa 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -1,6 +1,8 @@ //! HTTP Request message related code. +use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::HashMap; use std::net::SocketAddr; +use std::ops::Deref; use std::rc::Rc; use std::{cmp, fmt, io, str}; @@ -22,261 +24,158 @@ use httpresponse::{HttpResponse, HttpResponseBuilder}; use info::ConnectionInfo; use param::Params; use payload::Payload; -use router::{Resource, Router}; -use server::helpers::SharedHttpInnerMessage; +use router::{Resource, RouteInfo, Router}; +use server::message::{MessageFlags, Request}; 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, - pub payload: Option, - pub prefix: u16, - resource: RouterResource, -} - struct Query(HashMap); struct Cookies(Vec>); -struct Info(ConnectionInfo); #[derive(Debug, Copy, Clone, PartialEq)] -enum RouterResource { +pub(crate) enum RouterResource { Notset, Normal(u16), } -impl Default for HttpInnerMessage { - fn default() -> HttpInnerMessage { - HttpInnerMessage { - method: Method::GET, - url: InnerUrl::default(), - version: Version::HTTP_11, - headers: HeaderMap::with_capacity(16), - flags: MessageFlags::empty(), - params: Params::new(), - addr: None, - payload: None, - extensions: Extensions::new(), - prefix: 0, - resource: RouterResource::Notset, +/// An HTTP Request +pub struct HttpRequest { + req: Rc, + state: Rc, + route: RouteInfo, +} + +impl HttpMessage for HttpRequest { + type Stream = Payload; + + #[inline] + fn headers(&self) -> &HeaderMap { + self.req.headers() + } + + #[inline] + fn payload(&self) -> Payload { + if let Some(payload) = self.req.inner.payload.borrow_mut().take() { + payload + } else { + Payload::empty() } } } -impl HttpInnerMessage { - /// Checks if a connection should be kept alive. - #[inline] - pub fn keep_alive(&self) -> bool { - self.flags.contains(MessageFlags::KEEPALIVE) - } +impl Deref for HttpRequest { + type Target = Request; - #[inline] - pub(crate) fn reset(&mut self) { - 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(SharedHttpInnerMessage, Option>, Option); - -impl HttpRequest<()> { - /// Construct a new Request. - #[inline] - pub(crate) fn new( - method: Method, uri: Uri, version: Version, headers: HeaderMap, - payload: Option, - ) -> 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(self, state: Rc, router: Router) -> HttpRequest { - HttpRequest(self.0, Some(state), Some(router)) - } - - pub(crate) fn clone_with_state( - &self, state: Rc, router: Router, - ) -> HttpRequest { - HttpRequest(self.0.clone(), Some(state), Some(router)) - } -} - -impl HttpMessage for HttpRequest { - #[inline] - fn headers(&self) -> &HeaderMap { - &self.as_ref().headers + fn deref(&self) -> &Request { + self.req.as_ref() } } impl HttpRequest { + #[inline] + pub(crate) fn new(req: Request, state: Rc, route: RouteInfo) -> HttpRequest { + HttpRequest { + state, + route, + req: Rc::new(req), + } + } + #[inline] /// Construct new http request with state. - pub fn change_state(&self, state: Rc) -> HttpRequest { - HttpRequest(self.0.clone(), Some(state), self.2.clone()) + pub(crate) fn with_state(&self, state: Rc) -> HttpRequest { + HttpRequest { + state, + req: self.req.clone(), + route: self.route.clone(), + } } #[inline] - /// Construct new http request without state. - pub fn drop_state(&self) -> HttpRequest { - HttpRequest(self.0.clone(), None, self.2.clone()) - } - - /// get mutable reference for inner message - /// 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() + /// Construct new http request with new RouteInfo. + pub(crate) fn with_route_info(&self, route: RouteInfo) -> HttpRequest { + HttpRequest { + route, + req: self.req.clone(), + state: self.state.clone(), + } } /// Shared application state #[inline] pub fn state(&self) -> &S { - self.1.as_ref().unwrap() + &self.state + } + + #[inline] + /// Server request + pub fn request(&self) -> &Request { + &self.req } /// Request extensions #[inline] - pub fn extensions(&self) -> &Extensions { - &self.as_ref().extensions + pub fn extensions(&self) -> Ref { + self.req.extensions() } /// Mutable reference to a the request's extensions #[inline] - pub fn extensions_mut(&mut self) -> &mut Extensions { - &mut self.as_mut().extensions + pub fn extensions_mut(&self) -> RefMut { + self.req.extensions_mut() } /// Default `CpuPool` #[inline] #[doc(hidden)] pub fn cpu_pool(&self) -> &CpuPool { - self.router() - .expect("HttpRequest has to have Router instance") - .server_settings() - .cpu_pool() + self.req.server_settings().cpu_pool() } + #[inline] /// Create http response pub fn response(&self, status: StatusCode, body: Body) -> HttpResponse { - if let Some(router) = self.router() { - router.server_settings().get_response(status, body) - } else { - HttpResponse::with_body(status, body) - } + self.req.server_settings().get_response(status, body) } + #[inline] /// Create http response builder pub fn build_response(&self, status: StatusCode) -> HttpResponseBuilder { - if let Some(router) = self.router() { - 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; + self.req.server_settings().get_response_builder(status) } /// Read the Request Uri. #[inline] pub fn uri(&self) -> &Uri { - self.as_ref().url.uri() + self.request().inner.url.uri() } /// Read the Request method. #[inline] pub fn method(&self) -> &Method { - &self.as_ref().method + &self.request().inner.method } /// Read the Request Version. #[inline] pub fn version(&self) -> Version { - self.as_ref().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 + self.request().inner.version } /// The target path of this Request. #[inline] pub fn path(&self) -> &str { - self.as_ref().url.path() + self.request().inner.url.path() } #[inline] pub(crate) fn url(&self) -> &InnerUrl { - &self.as_ref().url + &self.request().inner.url } - /// Get *ConnectionInfo* for correct request. - pub fn connection_info(&self) -> &ConnectionInfo { - if self.extensions().get::().is_none() { - let mut req = self.clone(); - req.as_mut() - .extensions - .insert(Info(ConnectionInfo::new(self))); - } - &self.extensions().get::().unwrap().0 + /// Get *ConnectionInfo* for the correct request. + #[inline] + pub fn connection_info(&self) -> Ref { + self.request().connection_info() } /// Generate url for named resource @@ -306,22 +205,7 @@ impl HttpRequest { U: IntoIterator, I: AsRef, { - if self.router().is_none() { - 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)?) - } - } + self.route.url_for(&self, name, elements) } /// Generate url for named resource @@ -333,25 +217,16 @@ impl HttpRequest { self.url_for(name, &NO_PARAMS) } - /// This method returns reference to current `Router` object. + /// This method returns reference to current `RouteInfo` object. #[inline] - pub fn router(&self) -> Option<&Router> { - self.2.as_ref() + pub fn route(&self) -> &RouteInfo { + &self.route } /// This method returns reference to matched `Resource` object. #[inline] pub fn resource(&self) -> Option<&Resource> { - if let Some(ref router) = self.2 { - 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); + self.route.resource() } /// Peer socket address @@ -363,25 +238,20 @@ impl HttpRequest { /// be used. #[inline] pub fn peer_addr(&self) -> Option { - self.as_ref().addr - } - - #[inline] - pub(crate) fn set_peer_addr(&mut self, addr: Option) { - self.as_mut().addr = addr; + self.request().inner.addr } /// url query parameters. - pub fn query(&self) -> &HashMap { + pub fn query(&self) -> Ref> { if self.extensions().get::().is_none() { let mut query = HashMap::new(); for (key, val) in form_urlencoded::parse(self.query_string().as_ref()) { query.insert(key.as_ref().to_string(), val.to_string()); } let mut req = self.clone(); - req.as_mut().extensions.insert(Query(query)); + self.extensions_mut().insert(Query(query)); } - &self.extensions().get::().unwrap().0 + Ref::map(self.extensions(), |ext| &ext.get::().unwrap().0) } /// The query string in the URL. @@ -397,12 +267,12 @@ impl HttpRequest { } /// Load request cookies. - pub fn cookies(&self) -> Result<&Vec>, CookieParseError> { - if self.extensions().get::().is_none() { + #[inline] + pub fn cookies(&self) -> Result>>, CookieParseError> { + if self.extensions().get::().is_none() { let mut req = self.clone(); - let msg = req.as_mut(); 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)?; for cookie_str in s.split(';').map(|s| s.trim()) { if !cookie_str.is_empty() { @@ -410,17 +280,20 @@ impl HttpRequest { } } } - msg.extensions.insert(Cookies(cookies)); + self.extensions_mut().insert(Cookies(cookies)); } - Ok(&self.extensions().get::().unwrap().0) + Ok(Ref::map(self.extensions(), |ext| { + &ext.get::().unwrap().0 + })) } /// Return request cookie. - pub fn cookie(&self, name: &str) -> Option<&Cookie> { + #[inline] + pub fn cookie(&self, name: &str) -> Option> { if let Ok(cookies) = self.cookies() { - for cookie in cookies { + for cookie in cookies.iter() { if cookie.name() == name { - return Some(cookie); + return Some(cookie.to_owned()); } } } @@ -441,68 +314,31 @@ impl HttpRequest { /// access the matched value for that segment. #[inline] pub fn match_info(&self) -> &Params { - &self.as_ref().params - } - - /// 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) + &self.route.match_info() } /// Check if request requires connection upgrade pub(crate) fn upgrade(&self) -> bool { - if let Some(conn) = self.as_ref().headers.get(header::CONNECTION) { - if let Ok(s) = conn.to_str() { - return s.to_lowercase().contains("upgrade"); - } - } - self.as_ref().method == Method::CONNECT + self.request().upgrade() } /// Set read buffer capacity /// /// Default buffer capacity is 32Kb. 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) } } - - #[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 Clone for HttpRequest { fn clone(&self) -> HttpRequest { - 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 FromRequest for HttpRequest { } } -impl Stream for HttpRequest { - type Item = Bytes; - type Error = PayloadError; - - fn poll(&mut self) -> Poll, PayloadError> { - let msg = self.as_mut(); - if msg.payload.is_none() { - Ok(Async::Ready(None)) - } else { - msg.payload.as_mut().unwrap().poll() - } - } -} - -impl io::Read for HttpRequest { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - 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 AsyncRead for HttpRequest {} - impl fmt::Debug for HttpRequest { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let res = writeln!( f, "\nHttpRequest {:?} {}:{}", - self.as_ref().version, - self.as_ref().method, + self.version(), + self.method(), self.path() ); if !self.query_string().is_empty() { let _ = writeln!(f, " query: ?{:?}", self.query_string()); } 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:"); - for (key, val) in self.as_ref().headers.iter() { + for (key, val) in self.headers().iter() { let _ = writeln!(f, " {:?}: {:?}", key, val); } res @@ -597,7 +380,6 @@ mod tests { use super::*; use resource::ResourceHandler; use router::Resource; - use server::ServerSettings; use test::TestRequest; #[test] @@ -609,7 +391,7 @@ mod tests { #[test] fn test_no_request_cookies() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); assert!(req.cookies().unwrap().is_empty()); } @@ -648,33 +430,27 @@ mod tests { #[test] fn test_request_match_info() { - let mut req = TestRequest::with_uri("/value/?id=test").finish(); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let mut routes = Vec::new(); routes.push((Resource::new("index", "/{key}/"), Some(resource))); - let (router, _) = Router::new("", ServerSettings::default(), routes); - assert!(router.recognize(&mut req).is_some()); + let (router, _) = Router::new("", routes); - 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] fn test_url_for() { - let req2 = HttpRequest::default(); - assert_eq!( - req2.url_for("unknown", &["test"]), - Err(UrlGenerationError::RouterNotAvailable) - ); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let routes = vec![(Resource::new("index", "/user/{name}.{ext}"), Some(resource))]; - let (router, _) = Router::new("/", ServerSettings::default(), routes); - assert!(router.has_route("/user/test.html")); - assert!(!router.has_route("/test/unknown")); + let (router, _) = Router::new("/", routes); + let info = router.default_route_info(0); + assert!(info.has_route("/user/test.html")); + assert!(!info.has_route("/test/unknown")); let req = TestRequest::with_header(header::HOST, "www.rust-lang.org") .finish_with_router(router); @@ -696,16 +472,16 @@ mod tests { #[test] fn test_url_for_with_prefix() { - let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish(); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let routes = vec![(Resource::new("index", "/user/{name}.html"), Some(resource))]; - let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); - assert!(router.has_route("/user/test.html")); - assert!(!router.has_route("/prefix/user/test.html")); + let (router, _) = Router::new("/prefix/", routes); + let info = router.default_route_info(0); + 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"]); assert_eq!( url.ok().unwrap().as_str(), @@ -715,16 +491,17 @@ mod tests { #[test] fn test_url_for_static() { - let req = TestRequest::with_header(header::HOST, "www.rust-lang.org").finish(); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let routes = vec![(Resource::new("index", "/index.html"), Some(resource))]; - let (router, _) = Router::new("/prefix/", ServerSettings::default(), routes); - assert!(router.has_route("/index.html")); - assert!(!router.has_route("/prefix/index.html")); + let (router, _) = Router::new("/prefix/", routes); + let info = router.default_route_info(0); + 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"); assert_eq!( url.ok().unwrap().as_str(), @@ -734,18 +511,17 @@ mod tests { #[test] fn test_url_for_external() { - let req = HttpRequest::default(); - let mut resource = ResourceHandler::<()>::default(); resource.name("index"); let routes = vec![( Resource::external("youtube", "https://youtube.com/watch/{video_id}"), None, )]; - let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); - assert!(!router.has_route("https://youtube.com/watch/unknown")); + let router = Router::new::<()>("", routes).0; + 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"]); assert_eq!( url.ok().unwrap().as_str(), diff --git a/src/httpresponse.rs b/src/httpresponse.rs index 3f6dce76c..71db87678 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -553,10 +553,10 @@ impl HttpResponseBuilder { /// # extern crate actix_web; /// use actix_web::{http, HttpRequest, HttpResponse, Result}; /// - /// fn index(req: HttpRequest) -> HttpResponse { + /// fn index(req: &HttpRequest) -> HttpResponse { /// let mut builder = HttpResponse::Ok(); /// - /// if let Some(cookie) = req.cookie("name") { + /// if let Some(ref cookie) = req.cookie("name") { /// builder.del_cookie(cookie); /// } /// @@ -860,13 +860,9 @@ impl<'a> From<&'a ClientResponse> for HttpResponseBuilder { impl<'a, S> From<&'a HttpRequest> for HttpResponseBuilder { fn from(req: &'a HttpRequest) -> HttpResponseBuilder { - if let Some(router) = req.router() { - router - .server_settings() - .get_response_builder(StatusCode::OK) - } else { - HttpResponse::Ok() - } + req.request() + .server_settings() + .get_response_builder(StatusCode::OK) } } @@ -1050,6 +1046,8 @@ mod tests { use std::str::FromStr; use time::Duration; + use test::TestRequest; + #[test] fn test_debug() { let resp = HttpResponse::Ok() @@ -1062,17 +1060,10 @@ mod tests { #[test] fn test_response_cookies() { - let mut headers = HeaderMap::new(); - headers.insert(COOKIE, HeaderValue::from_static("cookie1=value1")); - headers.insert(COOKIE, HeaderValue::from_static("cookie2=value2")); - - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header(COOKIE, "cookie1=value1") + .header(COOKIE, "cookie2=value2") + .finish(); let cookies = req.cookies().unwrap(); let resp = HttpResponse::Ok() @@ -1094,7 +1085,7 @@ mod tests { .map(|v| v.to_str().unwrap().to_owned()) .collect(); val.sort(); - assert!(val[0].starts_with("cookie2=; Max-Age=0;")); + assert!(val[0].starts_with("cookie1=; Max-Age=0;")); assert_eq!( val[1], "name=value; HttpOnly; Path=/test; Domain=www.rust-lang.org; Max-Age=86400" @@ -1208,7 +1199,7 @@ mod tests { #[test] fn test_into_response() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); let resp: HttpResponse = "test".into(); assert_eq!(resp.status(), StatusCode::OK); diff --git a/src/info.rs b/src/info.rs index dad10b646..5d43b8e97 100644 --- a/src/info.rs +++ b/src/info.rs @@ -1,14 +1,17 @@ +use std::rc::Rc; use std::str::FromStr; use http::header::{self, HeaderName}; use httpmessage::HttpMessage; use httprequest::HttpRequest; +use server::Request; -const X_FORWARDED_FOR: &str = "X-FORWARDED-FOR"; -const X_FORWARDED_HOST: &str = "X-FORWARDED-HOST"; -const X_FORWARDED_PROTO: &str = "X-FORWARDED-PROTO"; +const X_FORWARDED_FOR: &[u8] = b"x-forwarded-for"; +const X_FORWARDED_HOST: &[u8] = b"x-forwarded-host"; +const X_FORWARDED_PROTO: &[u8] = b"x-forwarded-proto"; /// `HttpRequest` connection information +#[derive(Clone, Default)] pub struct ConnectionInfo { scheme: String, host: String, @@ -19,7 +22,7 @@ pub struct ConnectionInfo { impl ConnectionInfo { /// Create *ConnectionInfo* instance for a request. #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))] - pub fn new(req: &HttpRequest) -> ConnectionInfo { + pub fn update(&mut self, req: &Request) { let mut host = None; let mut scheme = None; let mut remote = None; @@ -56,7 +59,7 @@ impl ConnectionInfo { if scheme.is_none() { if let Some(h) = req .headers() - .get(HeaderName::from_str(X_FORWARDED_PROTO).unwrap()) + .get(HeaderName::from_lowercase(X_FORWARDED_PROTO).unwrap()) { if let Ok(h) = h.to_str() { scheme = h.split(',').next().map(|v| v.trim()); @@ -64,12 +67,8 @@ impl ConnectionInfo { } if scheme.is_none() { scheme = req.uri().scheme_part().map(|a| a.as_str()); - if scheme.is_none() { - if let Some(router) = req.router() { - if router.server_settings().secure() { - scheme = Some("https") - } - } + if scheme.is_none() && req.server_settings().secure() { + scheme = Some("https") } } } @@ -78,7 +77,7 @@ impl ConnectionInfo { if host.is_none() { if let Some(h) = req .headers() - .get(HeaderName::from_str(X_FORWARDED_HOST).unwrap()) + .get(HeaderName::from_lowercase(X_FORWARDED_HOST).unwrap()) { if let Ok(h) = h.to_str() { host = h.split(',').next().map(|v| v.trim()); @@ -91,9 +90,7 @@ impl ConnectionInfo { if host.is_none() { host = req.uri().authority_part().map(|a| a.as_str()); if host.is_none() { - if let Some(router) = req.router() { - host = Some(router.server_settings().host()); - } + host = Some(req.server_settings().host()); } } } @@ -103,7 +100,7 @@ impl ConnectionInfo { if remote.is_none() { if let Some(h) = req .headers() - .get(HeaderName::from_str(X_FORWARDED_FOR).unwrap()) + .get(HeaderName::from_lowercase(X_FORWARDED_FOR).unwrap()) { if let Ok(h) = h.to_str() { remote = h.split(',').next().map(|v| v.trim()); @@ -115,12 +112,10 @@ impl ConnectionInfo { } } - ConnectionInfo { - scheme: scheme.unwrap_or("http").to_owned(), - host: host.unwrap_or("localhost").to_owned(), - remote: remote.map(|s| s.to_owned()), - peer, - } + self.scheme = scheme.unwrap_or("http").to_owned(); + self.host = host.unwrap_or("localhost").to_owned(); + self.remote = remote.map(|s| s.to_owned()); + self.peer = peer; } /// Scheme of the request. @@ -171,59 +166,59 @@ impl ConnectionInfo { mod tests { use super::*; use http::header::HeaderValue; + use test::TestRequest; #[test] fn test_forwarded() { - let req = HttpRequest::default(); - let info = ConnectionInfo::new(&req); + let req = TestRequest::default().request(); + let mut info = ConnectionInfo::default(); + info.update(&req); assert_eq!(info.scheme(), "http"); - assert_eq!(info.host(), "localhost"); + assert_eq!(info.host(), "localhost:8080"); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - header::FORWARDED, - HeaderValue::from_static( + let req = TestRequest::default() + .header( + header::FORWARDED, "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.host(), "rust-lang.org"); assert_eq!(info.remote(), Some("192.0.2.60")); - let mut req = HttpRequest::default(); - req.headers_mut() - .insert(header::HOST, HeaderValue::from_static("rust-lang.org")); + let req = TestRequest::default() + .header(header::HOST, "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.host(), "rust-lang.org"); assert_eq!(info.remote(), None); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - HeaderName::from_str(X_FORWARDED_FOR).unwrap(), - HeaderValue::from_static("192.0.2.60"), - ); - let info = ConnectionInfo::new(&req); + let req = TestRequest::default() + .header(X_FORWARDED_FOR, "192.0.2.60") + .request(); + let mut info = ConnectionInfo::default(); + info.update(&req); assert_eq!(info.remote(), Some("192.0.2.60")); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - HeaderName::from_str(X_FORWARDED_HOST).unwrap(), - HeaderValue::from_static("192.0.2.60"), - ); - let info = ConnectionInfo::new(&req); + let req = TestRequest::default() + .header(X_FORWARDED_HOST, "192.0.2.60") + .request(); + let mut info = ConnectionInfo::default(); + info.update(&req); assert_eq!(info.host(), "192.0.2.60"); assert_eq!(info.remote(), None); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - HeaderName::from_str(X_FORWARDED_PROTO).unwrap(), - HeaderValue::from_static("https"), - ); - let info = ConnectionInfo::new(&req); + let mut req = TestRequest::default() + .header(X_FORWARDED_PROTO, "https") + .request(); + let mut info = ConnectionInfo::default(); + info.update(&req); assert_eq!(info.scheme(), "https"); } } diff --git a/src/json.rs b/src/json.rs index 3f9188c14..e9083cdd1 100644 --- a/src/json.rs +++ b/src/json.rs @@ -140,12 +140,12 @@ where #[inline] fn from_request(req: &HttpRequest, cfg: &Self::Config) -> Self::Result { - let req = req.clone(); + let req2 = req.clone(); let err = Rc::clone(&cfg.ehandler); Box::new( - JsonBody::new(req.clone()) + JsonBody::new(req) .limit(cfg.limit) - .map_err(move |e| (*err)(e, req)) + .map_err(move |e| (*err)(e, &req2)) .map(Json), ) } @@ -183,7 +183,7 @@ where /// ``` pub struct JsonConfig { limit: usize, - ehandler: Rc) -> Error>, + ehandler: Rc) -> Error>, } impl JsonConfig { @@ -196,7 +196,7 @@ impl JsonConfig { /// Set custom error handler pub fn error_handler(&mut self, f: F) -> &mut Self where - F: Fn(JsonPayloadError, HttpRequest) -> Error + 'static, + F: Fn(JsonPayloadError, &HttpRequest) -> Error + 'static, { self.ehandler = Rc::new(f); self @@ -243,19 +243,48 @@ impl Default for JsonConfig { /// } /// # fn main() {} /// ``` -pub struct JsonBody { +pub struct JsonBody { limit: usize, - req: Option, + length: Option, + stream: Option, + err: Option, fut: Option>>, } -impl JsonBody { +impl JsonBody { /// 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::() { + len = Some(l) + } + } + } + JsonBody { limit: 262_144, - req: Some(req), + length: len, + stream: Some(req.payload()), fut: None, + err: None, } } @@ -266,56 +295,42 @@ impl JsonBody { } } -impl Future for JsonBody -where - T: HttpMessage + Stream + 'static, -{ +impl Future for JsonBody { type Item = U; type Error = JsonPayloadError; fn poll(&mut self) -> Poll { - if let Some(req) = self.req.take() { - if let Some(len) = req.headers().get(CONTENT_LENGTH) { - if let Ok(s) = len.to_str() { - if let Ok(len) = s.parse::() { - 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::(&body)?)); - self.fut = Some(Box::new(fut)); + if let Some(ref mut fut) = self.fut { + return fut.poll(); } - self.fut - .as_mut() + if let Some(err) = self.err.take() { + 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") - .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::(&body)?)); + self.fut = Some(Box::new(fut)); + self.poll() } } @@ -327,6 +342,7 @@ mod tests { use http::header; use handler::Handler; + use test::TestRequest; use with::With; impl PartialEq for JsonPayloadError { @@ -355,7 +371,7 @@ mod tests { let json = Json(MyObject { name: "test".to_owned(), }); - let resp = json.respond_to(&HttpRequest::default()).unwrap(); + let resp = json.respond_to(&TestRequest::default().finish()).unwrap(); assert_eq!( resp.headers().get(header::CONTENT_TYPE).unwrap(), "application/json" @@ -364,41 +380,44 @@ mod tests { #[test] fn test_json_body() { - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); let mut json = req.json::(); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/text"), - ); + let req = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/text"), + ) + .finish(); let mut json = req.json::(); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::ContentType); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ); - req.headers_mut().insert( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("10000"), - ); + let req = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("10000"), + ) + .finish(); let mut json = req.json::().limit(100); assert_eq!(json.poll().err().unwrap(), JsonPayloadError::Overflow); - let mut req = HttpRequest::default(); - req.headers_mut().insert( - header::CONTENT_TYPE, - header::HeaderValue::from_static("application/json"), - ); - req.headers_mut().insert( - header::CONTENT_LENGTH, - header::HeaderValue::from_static("16"), - ); - req.payload_mut() - .unread_data(Bytes::from_static(b"{\"name\": \"test\"}")); + let req = TestRequest::default() + .header( + header::CONTENT_TYPE, + header::HeaderValue::from_static("application/json"), + ) + .header( + header::CONTENT_LENGTH, + header::HeaderValue::from_static("16"), + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .finish(); + let mut json = req.json::(); assert_eq!( json.poll().ok().unwrap(), @@ -414,20 +433,18 @@ mod tests { cfg.limit(4096); let handler = With::new(|data: Json| data, cfg); - let req = HttpRequest::default(); - assert!(handler.handle(req).as_err().is_some()); + let req = TestRequest::default().finish(); + assert!(handler.handle(&req).as_err().is_some()); - let mut req = HttpRequest::default(); - req.headers_mut().insert( + let req = TestRequest::with_header( header::CONTENT_TYPE, header::HeaderValue::from_static("application/json"), - ); - req.headers_mut().insert( + ).header( header::CONTENT_LENGTH, header::HeaderValue::from_static("16"), - ); - req.payload_mut() - .unread_data(Bytes::from_static(b"{\"name\": \"test\"}")); - assert!(handler.handle(req).as_err().is_none()) + ) + .set_payload(Bytes::from_static(b"{\"name\": \"test\"}")) + .finish(); + assert!(handler.handle(&req).as_err().is_none()) } } diff --git a/src/lib.rs b/src/lib.rs index 85df48dd9..5ed1bcef9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -84,6 +84,7 @@ allow(decimal_literal_representation, suspicious_arithmetic_impl) )] #![warn(missing_docs)] +#![allow(unused_mut, unused_imports, unused_variables, dead_code)] #[macro_use] extern crate log; @@ -199,6 +200,7 @@ pub use httprequest::HttpRequest; pub use httpresponse::HttpResponse; pub use json::Json; pub use scope::Scope; +pub use server::Request; pub mod actix { //! Re-exports [actix's](https://docs.rs/actix/) prelude diff --git a/src/middleware/cors.rs b/src/middleware/cors.rs index 734f7be4b..09ca81205 100644 --- a/src/middleware/cors.rs +++ b/src/middleware/cors.rs @@ -60,6 +60,7 @@ use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Response, Started}; use resource::ResourceHandler; +use server::Request; /// A set of errors that can occur during processing CORS #[derive(Debug, Fail)] @@ -279,11 +280,13 @@ impl Cors { /// `ResourceHandler::middleware()` method, but in that case *Cors* /// middleware wont be able to handle *OPTIONS* requests. pub fn register(self, resource: &mut ResourceHandler) { - resource.method(Method::OPTIONS).h(|_| HttpResponse::Ok()); + resource + .method(Method::OPTIONS) + .h(|_: &_| HttpResponse::Ok()); resource.middleware(self); } - fn validate_origin(&self, req: &mut HttpRequest) -> Result<(), CorsError> { + fn validate_origin(&self, req: &Request) -> Result<(), CorsError> { if let Some(hdr) = req.headers().get(header::ORIGIN) { if let Ok(origin) = hdr.to_str() { return match self.inner.origins { @@ -303,9 +306,7 @@ impl Cors { } } - fn validate_allowed_method( - &self, req: &mut HttpRequest, - ) -> Result<(), CorsError> { + fn validate_allowed_method(&self, req: &Request) -> Result<(), CorsError> { if let Some(hdr) = req.headers().get(header::ACCESS_CONTROL_REQUEST_METHOD) { if let Ok(meth) = hdr.to_str() { if let Ok(method) = Method::try_from(meth) { @@ -323,9 +324,7 @@ impl Cors { } } - fn validate_allowed_headers( - &self, req: &mut HttpRequest, - ) -> Result<(), CorsError> { + fn validate_allowed_headers(&self, req: &Request) -> Result<(), CorsError> { match self.inner.headers { AllOrSome::All => Ok(()), AllOrSome::Some(ref allowed_headers) => { @@ -356,11 +355,11 @@ impl Cors { } impl Middleware for Cors { - fn start(&self, req: &mut HttpRequest) -> Result { + fn start(&self, req: &HttpRequest) -> Result { if self.inner.preflight && Method::OPTIONS == *req.method() { self.validate_origin(req)?; - self.validate_allowed_method(req)?; - self.validate_allowed_headers(req)?; + self.validate_allowed_method(&req)?; + self.validate_allowed_headers(&req)?; // allowed headers let headers = if let Some(headers) = self.inner.headers.as_ref() { @@ -434,7 +433,7 @@ impl Middleware for Cors { } fn response( - &self, req: &mut HttpRequest, mut resp: HttpResponse, + &self, req: &HttpRequest, mut resp: HttpResponse, ) -> Result { match self.inner.origins { AllOrSome::All => { @@ -945,10 +944,9 @@ mod tests { #[test] fn validate_origin_allows_all_origins() { let cors = Cors::default(); - let mut req = - TestRequest::with_header("Origin", "https://www.example.com").finish(); + let req = 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] @@ -961,20 +959,20 @@ mod tests { .allowed_header(header::CONTENT_TYPE) .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) .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") .method(Method::OPTIONS) .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_HEADERS, @@ -983,7 +981,7 @@ mod tests { .method(Method::OPTIONS) .finish(); - let resp = cors.start(&mut req).unwrap().response(); + let resp = cors.start(&req).unwrap().response(); assert_eq!( &b"*"[..], resp.headers() @@ -1007,7 +1005,7 @@ mod tests { // as_bytes()); 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] @@ -1017,7 +1015,7 @@ mod tests { // .allowed_origin("https://www.example.com") // .finish(); // let mut req = HttpRequest::default(); - // cors.start(&mut req).unwrap(); + // cors.start(&req).unwrap(); // } #[test] @@ -1027,10 +1025,10 @@ mod tests { .allowed_origin("https://www.example.com") .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) .finish(); - cors.start(&mut req).unwrap(); + cors.start(&req).unwrap(); } #[test] @@ -1039,30 +1037,30 @@ mod tests { .allowed_origin("https://www.example.com") .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) .finish(); - assert!(cors.start(&mut req).unwrap().is_done()); + assert!(cors.start(&req).unwrap().is_done()); } #[test] fn test_no_origin_response() { 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 = cors.response(&mut req, resp).unwrap().response(); + let resp = cors.response(&req, resp).unwrap().response(); assert!( resp.headers() .get(header::ACCESS_CONTROL_ALLOW_ORIGIN) .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) .finish(); - let resp = cors.response(&mut req, resp).unwrap().response(); + let resp = cors.response(&req, resp).unwrap().response(); assert_eq!( &b"https://www.example.com"[..], resp.headers() @@ -1083,12 +1081,12 @@ mod tests { .allowed_header(header::CONTENT_TYPE) .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) .finish(); 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!( &b"*"[..], resp.headers() @@ -1103,7 +1101,7 @@ mod tests { let resp: HttpResponse = 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!( &b"Accept, Origin"[..], resp.headers().get(header::VARY).unwrap().as_bytes() @@ -1114,7 +1112,7 @@ mod tests { .allowed_origin("https://www.example.com") .finish(); 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!( &b"https://www.example.com"[..], resp.headers() diff --git a/src/middleware/csrf.rs b/src/middleware/csrf.rs index faa763e25..0062bd02f 100644 --- a/src/middleware/csrf.rs +++ b/src/middleware/csrf.rs @@ -25,7 +25,7 @@ //! use actix_web::middleware::csrf; //! 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" //! } //! @@ -54,6 +54,7 @@ use httpmessage::HttpMessage; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Started}; +use server::Request; /// Potential cross-site request forgery detected. #[derive(Debug, Fail)] @@ -187,7 +188,7 @@ impl CsrfFilter { self } - fn validate(&self, req: &mut HttpRequest) -> Result<(), CsrfError> { + fn validate(&self, req: &Request) -> Result<(), CsrfError> { let is_upgrade = req.headers().contains_key(header::UPGRADE); let is_safe = req.method().is_safe() && (self.allow_upgrade || !is_upgrade); @@ -209,7 +210,7 @@ impl CsrfFilter { } impl Middleware for CsrfFilter { - fn start(&self, req: &mut HttpRequest) -> Result { + fn start(&self, req: &HttpRequest) -> Result { self.validate(req)?; Ok(Started::Done) } @@ -225,35 +226,35 @@ mod tests { fn test_safe() { 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) .finish(); - assert!(csrf.start(&mut req).is_ok()); + assert!(csrf.start(&req).is_ok()); } #[test] fn test_csrf() { 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) .finish(); - assert!(csrf.start(&mut req).is_err()); + assert!(csrf.start(&req).is_err()); } #[test] fn test_referer() { let csrf = CsrfFilter::new().allowed_origin("https://www.example.com"); - let mut req = TestRequest::with_header( + let req = TestRequest::with_header( "Referer", "https://www.example.com/some/path?query=param", ).method(Method::POST) .finish(); - assert!(csrf.start(&mut req).is_ok()); + assert!(csrf.start(&req).is_ok()); } #[test] @@ -264,13 +265,13 @@ mod tests { .allowed_origin("https://www.example.com") .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("Upgrade", "websocket") .method(Method::GET) .finish(); - assert!(strict_csrf.start(&mut req).is_err()); - assert!(lax_csrf.start(&mut req).is_ok()); + assert!(strict_csrf.start(&req).is_err()); + assert!(lax_csrf.start(&req).is_ok()); } } diff --git a/src/middleware/defaultheaders.rs b/src/middleware/defaultheaders.rs index dca8dfbe1..a33fa6a33 100644 --- a/src/middleware/defaultheaders.rs +++ b/src/middleware/defaultheaders.rs @@ -74,9 +74,7 @@ impl DefaultHeaders { } impl Middleware for DefaultHeaders { - fn response( - &self, _: &mut HttpRequest, mut resp: HttpResponse, - ) -> Result { + fn response(&self, _: &HttpRequest, mut resp: HttpResponse) -> Result { for (key, value) in self.headers.iter() { if !resp.headers().contains_key(key) { resp.headers_mut().insert(key, value.clone()); @@ -97,22 +95,23 @@ impl Middleware for DefaultHeaders { mod tests { use super::*; use http::header::CONTENT_TYPE; + use test::TestRequest; #[test] fn test_default_headers() { 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 = match mw.response(&mut req, resp) { + let resp = match mw.response(&req, resp) { Ok(Response::Done(resp)) => resp, _ => panic!(), }; assert_eq!(resp.headers().get(CONTENT_TYPE).unwrap(), "0001"); 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, _ => panic!(), }; diff --git a/src/middleware/errhandlers.rs b/src/middleware/errhandlers.rs index fe148fdd0..a9ebe21ca 100644 --- a/src/middleware/errhandlers.rs +++ b/src/middleware/errhandlers.rs @@ -6,7 +6,7 @@ use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Response}; -type ErrorHandler = Fn(&mut HttpRequest, HttpResponse) -> Result; +type ErrorHandler = Fn(&HttpRequest, HttpResponse) -> Result; /// `Middleware` for allowing custom handlers for responses. /// @@ -21,7 +21,7 @@ type ErrorHandler = Fn(&mut HttpRequest, HttpResponse) -> Result /// use actix_web::middleware::{ErrorHandlers, Response}; /// use actix_web::{http, App, HttpRequest, HttpResponse, Result}; /// -/// fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { +/// fn render_500(_: &HttpRequest, resp: HttpResponse) -> Result { /// let mut builder = resp.into_builder(); /// builder.header(http::header::CONTENT_TYPE, "application/json"); /// Ok(Response::Done(builder.into())) @@ -62,7 +62,7 @@ impl ErrorHandlers { /// Register error handler for specified status code pub fn handler(mut self, status: StatusCode, handler: F) -> Self where - F: Fn(&mut HttpRequest, HttpResponse) -> Result + 'static, + F: Fn(&HttpRequest, HttpResponse) -> Result + 'static, { self.handlers.insert(status, Box::new(handler)); self @@ -70,9 +70,7 @@ impl ErrorHandlers { } impl Middleware for ErrorHandlers { - fn response( - &self, req: &mut HttpRequest, resp: HttpResponse, - ) -> Result { + fn response(&self, req: &HttpRequest, resp: HttpResponse) -> Result { if let Some(handler) = self.handlers.get(&resp.status()) { handler(req, resp) } else { @@ -91,7 +89,10 @@ mod tests { use middleware::Started; use test; - fn render_500(_: &mut HttpRequest, resp: HttpResponse) -> Result { + use server::Request; + use test::TestRequest; + + fn render_500(_: &HttpRequest, resp: HttpResponse) -> Result { let mut builder = resp.into_builder(); builder.header(CONTENT_TYPE, "0001"); Ok(Response::Done(builder.into())) @@ -102,7 +103,7 @@ mod tests { let mw = 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 = match mw.response(&mut req, resp) { Ok(Response::Done(resp)) => resp, @@ -121,7 +122,7 @@ mod tests { struct MiddlewareOne; impl Middleware for MiddlewareOne { - fn start(&self, _req: &mut HttpRequest) -> Result { + fn start(&self, _: &HttpRequest) -> Result { Err(ErrorInternalServerError("middleware error")) } } diff --git a/src/middleware/identity.rs b/src/middleware/identity.rs index f40894289..c85542441 100644 --- a/src/middleware/identity.rs +++ b/src/middleware/identity.rs @@ -46,6 +46,7 @@ //! )); //! } //! ``` +use std::cell::RefCell; use std::rc::Rc; use cookie::{Cookie, CookieJar, Key}; @@ -58,6 +59,7 @@ use http::header::{self, HeaderValue}; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Response, Started}; +use server::Request; /// The helper trait to obtain your identity from a request. /// @@ -88,32 +90,32 @@ use middleware::{Middleware, Response, Started}; pub trait RequestIdentity { /// Return the claimed identity of the user associated request or /// ``None`` if no identity can be found associated with the request. - fn identity(&self) -> Option<&str>; + fn identity(&self) -> Option; /// Remember identity. - fn remember(&mut self, identity: String); + fn remember(&self, identity: String); /// This method is used to 'forget' the current identity on subsequent /// requests. - fn forget(&mut self); + fn forget(&self); } impl RequestIdentity for HttpRequest { - fn identity(&self) -> Option<&str> { + fn identity(&self) -> Option { if let Some(id) = self.extensions().get::() { - return id.0.identity(); + return id.0.identity().map(|s| s.to_owned()); } None } - fn remember(&mut self, identity: String) { - if let Some(id) = self.extensions_mut().get_mut::() { - return id.0.remember(identity); + fn remember(&self, identity: String) { + if let Some(mut id) = self.extensions_mut().get_mut::() { + return id.0.as_mut().remember(identity); } } - fn forget(&mut self) { - if let Some(id) = self.extensions_mut().get_mut::() { + fn forget(&self) { + if let Some(mut id) = self.extensions_mut().get_mut::() { return id.0.forget(); } } @@ -145,7 +147,7 @@ pub trait IdentityPolicy: Sized + 'static { type Future: Future; /// Parse the session from request and load data from a service identity. - fn from_request(&self, request: &mut HttpRequest) -> Self::Future; + fn from_request(&self, request: &HttpRequest) -> Self::Future; } /// Request identity middleware @@ -178,27 +180,21 @@ impl IdentityService { struct IdentityBox(Box); impl> Middleware for IdentityService { - fn start(&self, req: &mut HttpRequest) -> Result { - let mut req = req.clone(); - - let fut = self - .backend - .from_request(&mut req) - .then(move |res| match res { - Ok(id) => { - req.extensions_mut().insert(IdentityBox(Box::new(id))); - FutOk(None) - } - Err(err) => FutErr(err), - }); + fn start(&self, req: &HttpRequest) -> Result { + let req = req.clone(); + let fut = self.backend.from_request(&req).then(move |res| match res { + Ok(id) => { + req.extensions_mut().insert(IdentityBox(Box::new(id))); + FutOk(None) + } + Err(err) => FutErr(err), + }); Ok(Started::Future(Box::new(fut))) } - fn response( - &self, req: &mut HttpRequest, resp: HttpResponse, - ) -> Result { - if let Some(mut id) = req.extensions_mut().remove::() { - id.0.write(resp) + fn response(&self, req: &HttpRequest, resp: HttpResponse) -> Result { + if let Some(ref mut id) = req.extensions_mut().get_mut::() { + id.0.as_mut().write(resp) } else { Ok(Response::Done(resp)) } @@ -291,9 +287,9 @@ impl CookieIdentityInner { Ok(()) } - fn load(&self, req: &mut HttpRequest) -> Option { + fn load(&self, req: &HttpRequest) -> Option { if let Ok(cookies) = req.cookies() { - for cookie in cookies { + for cookie in cookies.iter() { if cookie.name() == self.name { let mut jar = CookieJar::new(); jar.add_original(cookie.clone()); @@ -382,7 +378,7 @@ impl IdentityPolicy for CookieIdentityPolicy { type Identity = CookieIdentity; type Future = FutureResult; - fn from_request(&self, req: &mut HttpRequest) -> Self::Future { + fn from_request(&self, req: &HttpRequest) -> Self::Future { let identity = self.0.load(req); FutOk(CookieIdentity { identity, diff --git a/src/middleware/logger.rs b/src/middleware/logger.rs index c5701ef8f..dbad60a19 100644 --- a/src/middleware/logger.rs +++ b/src/middleware/logger.rs @@ -11,6 +11,7 @@ use httpmessage::HttpMessage; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Finished, Middleware, Started}; +use server::Request; /// `Middleware` for logging request and response info to the terminal. /// @@ -107,7 +108,7 @@ impl Default for Logger { struct StartTime(time::Tm); impl Logger { - fn log(&self, req: &mut HttpRequest, resp: &HttpResponse) { + fn log(&self, req: &HttpRequest, resp: &HttpResponse) { if let Some(entry_time) = req.extensions().get::() { let render = |fmt: &mut Formatter| { for unit in &self.format.0 { @@ -121,14 +122,14 @@ impl Logger { } impl Middleware for Logger { - fn start(&self, req: &mut HttpRequest) -> Result { + fn start(&self, req: &HttpRequest) -> Result { if !self.exclude.contains(req.path()) { req.extensions_mut().insert(StartTime(time::now())); } Ok(Started::Done) } - fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished { + fn finish(&self, req: &HttpRequest, resp: &HttpResponse) -> Finished { self.log(req, resp); Finished::Done } @@ -312,34 +313,27 @@ mod tests { use http::header::{self, HeaderMap}; use http::{Method, StatusCode, Uri, Version}; use std::str::FromStr; + use test::TestRequest; use time; #[test] fn test_logger() { let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test"); - let mut headers = HeaderMap::new(); - headers.insert( + let req = TestRequest::with_header( header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"), - ); - let mut req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + ).finish(); let resp = HttpResponse::build(StatusCode::OK) .header("X-Test", "ttt") .force_close() .finish(); - match logger.start(&mut req) { + match logger.start(&req) { Ok(Started::Done) => (), _ => panic!(), }; - match logger.finish(&mut req, &resp) { + match logger.finish(&req, &resp) { Finished::Done => (), _ => panic!(), } @@ -358,18 +352,10 @@ mod tests { fn test_default_format() { let format = Format::default(); - let mut headers = HeaderMap::new(); - headers.insert( + let req = TestRequest::with_header( header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + ).finish(); let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); let entry_time = time::now(); @@ -384,13 +370,7 @@ mod tests { assert!(s.contains("200 0")); assert!(s.contains("ACTIX-WEB")); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/?test").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); + let req = TestRequest::with_uri("/?test").finish(); let resp = HttpResponse::build(StatusCode::OK).force_close().finish(); let entry_time = time::now(); diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index 2551ded15..237a0eb3c 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -4,6 +4,7 @@ use futures::Future; use error::{Error, Result}; use httprequest::HttpRequest; use httpresponse::HttpResponse; +use server::Request; mod logger; @@ -51,20 +52,18 @@ pub enum Finished { pub trait Middleware: 'static { /// Method is called when request is ready. It may return /// future, which should resolve before next middleware get called. - fn start(&self, req: &mut HttpRequest) -> Result { + fn start(&self, req: &HttpRequest) -> Result { Ok(Started::Done) } /// Method is called when handler returns response, /// but before sending http message to peer. - fn response( - &self, req: &mut HttpRequest, resp: HttpResponse, - ) -> Result { + fn response(&self, req: &HttpRequest, resp: HttpResponse) -> Result { Ok(Response::Done(resp)) } /// Method is called after body stream get sent to peer. - fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished { + fn finish(&self, req: &HttpRequest, resp: &HttpResponse) -> Finished { Finished::Done } } diff --git a/src/middleware/session.rs b/src/middleware/session.rs index bd10b3c23..af3d03cce 100644 --- a/src/middleware/session.rs +++ b/src/middleware/session.rs @@ -83,6 +83,7 @@ use handler::FromRequest; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Middleware, Response, Started}; +use server::Request; /// The helper trait to obtain your session data from a request. /// @@ -246,7 +247,7 @@ impl> SessionStorage { } impl> Middleware for SessionStorage { - fn start(&self, req: &mut HttpRequest) -> Result { + fn start(&self, req: &HttpRequest) -> Result { let mut req = req.clone(); let fut = self.0.from_request(&mut req).then(move |res| match res { @@ -260,10 +261,8 @@ impl> Middleware for SessionStorage { Ok(Started::Future(Box::new(fut))) } - fn response( - &self, req: &mut HttpRequest, resp: HttpResponse, - ) -> Result { - if let Some(s_box) = req.extensions_mut().remove::>() { + fn response(&self, req: &HttpRequest, resp: HttpResponse) -> Result { + if let Some(s_box) = req.extensions().get::>() { s_box.0.borrow_mut().write(resp) } else { Ok(Response::Done(resp)) @@ -421,7 +420,7 @@ impl CookieSessionInner { fn load(&self, req: &mut HttpRequest) -> HashMap { if let Ok(cookies) = req.cookies() { - for cookie in cookies { + for cookie in cookies.iter() { if cookie.name() == self.name { let mut jar = CookieJar::new(); jar.add_original(cookie.clone()); diff --git a/src/param.rs b/src/param.rs index 76262c2ac..649345ee0 100644 --- a/src/param.rs +++ b/src/param.rs @@ -1,7 +1,7 @@ use std; -use std::rc::Rc; use std::ops::Index; use std::path::PathBuf; +use std::rc::Rc; use std::str::FromStr; use http::StatusCode; @@ -29,11 +29,11 @@ pub(crate) enum ParamItem { /// Route match information /// /// If resource path contains variable patterns, `Params` stores this variables. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Params { url: Url, pub(crate) tail: u16, - segments: SmallVec<[(Rc, ParamItem); 3]>, + pub(crate) segments: SmallVec<[(Rc, ParamItem); 3]>, } 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) { self.segments.clear(); } @@ -62,7 +70,8 @@ impl Params { } 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 @@ -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; - fn index(&self, name: &'b str) -> &str { + fn index(&self, name: &'a str) -> &str { self.get(name) .expect("Value for parameter is not available") } } -impl<'a> Index for &'a Params { +impl Index for Params { type Output = str; fn index(&self, idx: usize) -> &str { diff --git a/src/payload.rs b/src/payload.rs index 12a4ae268..fd4e57af3 100644 --- a/src/payload.rs +++ b/src/payload.rs @@ -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 - #[inline] + #[cfg(test)] pub fn len(&self) -> usize { self.inner.borrow().len() } /// Is payload empty - #[inline] + #[cfg(test)] pub fn is_empty(&self) -> bool { self.inner.borrow().len() == 0 } @@ -225,12 +219,7 @@ impl Inner { } } - #[inline] - fn eof(&self) -> bool { - self.items.is_empty() && self.eof - } - - #[inline] + #[cfg(test)] fn len(&self) -> usize { self.len } diff --git a/src/pipeline.rs b/src/pipeline.rs index 192759442..6f3d48077 100644 --- a/src/pipeline.rs +++ b/src/pipeline.rs @@ -15,7 +15,7 @@ use header::ContentEncoding; use httprequest::HttpRequest; use httpresponse::HttpResponse; use middleware::{Finished, Middleware, Response, Started}; -use server::{HttpHandlerTask, Writer, WriterState}; +use server::{HttpHandlerTask, Request, Writer, WriterState}; #[doc(hidden)] #[derive(Debug, Clone, Copy)] @@ -29,13 +29,11 @@ pub enum HandlerType { pub trait PipelineHandler { fn encoding(&self) -> ContentEncoding; - fn handle( - &self, req: HttpRequest, htype: HandlerType, - ) -> AsyncResult; + fn handle(&self, &HttpRequest, HandlerType) -> AsyncResult; } #[doc(hidden)] -pub struct Pipeline( +pub struct Pipeline( PipelineInfo, PipelineState, Rc>>>, @@ -76,7 +74,7 @@ impl> PipelineState { } } -struct PipelineInfo { +struct PipelineInfo { req: HttpRequest, count: u16, context: Option>, @@ -85,7 +83,7 @@ struct PipelineInfo { encoding: ContentEncoding, } -impl PipelineInfo { +impl PipelineInfo { fn new(req: HttpRequest) -> PipelineInfo { PipelineInfo { req, @@ -129,16 +127,6 @@ impl> Pipeline { } } -impl Pipeline<(), Inner<()>> { - pub fn error>(err: R) -> Box { - Box::new(Pipeline::<(), Inner<()>>( - PipelineInfo::new(HttpRequest::default()), - ProcessResponse::init(err.into()), - Rc::new(Vec::new()), - )) - } -} - impl Pipeline { #[inline] fn is_done(&self) -> bool { @@ -241,16 +229,16 @@ impl> StartMiddlewares { // execute middlewares, we need this stage because middlewares could be // non-async and we can move to next state immediately let len = mws.len() as u16; + loop { 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); } else { - let state = mws[info.count as usize].start(&mut info.req); - match state { + match mws[info.count as usize].start(&info.req) { Ok(Started::Done) => info.count += 1, Ok(Started::Response(resp)) => { - return RunMiddlewares::init(info, mws, resp) + return RunMiddlewares::init(info, mws, resp); } Ok(Started::Future(fut)) => { return PipelineState::Starting(StartMiddlewares { @@ -260,7 +248,9 @@ impl> StartMiddlewares { _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> StartMiddlewares { &mut self, info: &mut PipelineInfo, mws: &[Box>], ) -> Option> { let len = mws.len() as u16; + 'outer: loop { match self.fut.as_mut().unwrap().poll() { - Ok(Async::NotReady) => return None, + Ok(Async::NotReady) => { + return None; + } Ok(Async::Ready(resp)) => { info.count += 1; if let Some(resp) = resp { @@ -280,11 +273,11 @@ impl> StartMiddlewares { } loop { 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)); } else { - let state = mws[info.count as usize].start(&mut info.req); - match state { + let res = mws[info.count as usize].start(&info.req); + match res { Ok(Started::Done) => info.count += 1, Ok(Started::Response(resp)) => { return Some(RunMiddlewares::init(info, mws, resp)); @@ -298,13 +291,15 @@ impl> StartMiddlewares { info, mws, 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 WaitingResponse { reply: AsyncResult, ) -> PipelineState { match reply.into() { - AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()), AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, mws, resp), + AsyncResultItem::Err(err) => RunMiddlewares::init(info, mws, err.into()), AsyncResultItem::Future(fut) => PipelineState::Handler(WaitingResponse { fut, _s: PhantomData, @@ -339,9 +334,7 @@ impl WaitingResponse { ) -> Option> { match self.fut.poll() { Ok(Async::NotReady) => None, - Ok(Async::Ready(response)) => { - Some(RunMiddlewares::init(info, mws, response)) - } + Ok(Async::Ready(resp)) => Some(RunMiddlewares::init(info, mws, resp)), Err(err) => Some(RunMiddlewares::init(info, mws, err.into())), } } @@ -367,7 +360,7 @@ impl RunMiddlewares { let len = mws.len(); loop { - let state = mws[curr].response(&mut info.req, resp); + let state = mws[curr].response(&info.req, resp); resp = match state { Err(err) => { info.count = (curr + 1) as u16; @@ -387,7 +380,7 @@ impl RunMiddlewares { fut: Some(fut), _s: PhantomData, _h: PhantomData, - }) + }); } }; } @@ -413,7 +406,7 @@ impl RunMiddlewares { if self.curr == len { return Some(ProcessResponse::init(resp)); } else { - let state = mws[self.curr].response(&mut info.req, resp); + let state = mws[self.curr].response(&info.req, resp); match state { Err(err) => return Some(ProcessResponse::init(err.into())), Ok(Response::Done(r)) => { @@ -495,19 +488,16 @@ impl ProcessResponse { let encoding = self.resp.content_encoding().unwrap_or(info.encoding); - let result = match io.start( - info.req.as_mut(), - &mut self.resp, - encoding, - ) { - Ok(res) => res, - Err(err) => { - info.error = Some(err.into()); - return Ok(FinishingMiddlewares::init( - info, mws, self.resp, - )); - } - }; + let result = + match io.start(&info.req, &mut self.resp, encoding) { + Ok(res) => res, + Err(err) => { + info.error = Some(err.into()); + return Ok(FinishingMiddlewares::init( + info, mws, self.resp, + )); + } + }; if let Some(err) = self.resp.error() { if self.resp.status().is_server_error() { @@ -747,8 +737,8 @@ impl FinishingMiddlewares { } info.count -= 1; - let state = mws[info.count as usize] - .finish(&mut info.req, self.resp.as_ref().unwrap()); + let state = + mws[info.count as usize].finish(&info.req, self.resp.as_ref().unwrap()); match state { Finished::Done => { if info.count == 0 { @@ -797,9 +787,10 @@ mod tests { use actix::*; use context::HttpContext; use futures::future::{lazy, result}; - use http::StatusCode; use tokio::runtime::current_thread::Runtime; + use test::TestRequest; + impl PipelineState { fn is_none(&self) -> Option { if let PipelineState::None = *self { @@ -827,12 +818,13 @@ mod tests { Runtime::new() .unwrap() .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) .is_none() .unwrap(); - let req = HttpRequest::default(); + let req = TestRequest::default().finish(); let ctx = HttpContext::new(req.clone(), MyActor); let addr = ctx.address(); let mut info = PipelineInfo::new(req); diff --git a/src/pred.rs b/src/pred.rs index 020052e25..5d47922fa 100644 --- a/src/pred.rs +++ b/src/pred.rs @@ -1,10 +1,12 @@ //! Route match predicates #![allow(non_snake_case)] +use std::marker::PhantomData; + use http; use http::{header, HttpTryFrom}; use httpmessage::HttpMessage; use httprequest::HttpRequest; -use std::marker::PhantomData; +use server::message::Request; /// Trait defines resource route predicate. /// 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. pub trait Predicate { /// Check if request matches predicate - fn check(&self, &mut HttpRequest) -> bool; + fn check(&self, &Request, &S) -> bool; } /// Return predicate that matches if any of supplied predicate matches. @@ -45,9 +47,9 @@ impl AnyPredicate { } impl Predicate for AnyPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { + fn check(&self, req: &Request, state: &S) -> bool { for p in &self.0 { - if p.check(req) { + if p.check(req, state) { return true; } } @@ -88,9 +90,9 @@ impl AllPredicate { } impl Predicate for AllPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { + fn check(&self, req: &Request, state: &S) -> bool { for p in &self.0 { - if !p.check(req) { + if !p.check(req, state) { return false; } } @@ -107,8 +109,8 @@ pub fn Not + 'static>(pred: P) -> NotPredicate { pub struct NotPredicate(Box>); impl Predicate for NotPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { - !self.0.check(req) + fn check(&self, req: &Request, state: &S) -> bool { + !self.0.check(req, state) } } @@ -117,7 +119,7 @@ impl Predicate for NotPredicate { pub struct MethodPredicate(http::Method, PhantomData); impl Predicate for MethodPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { + fn check(&self, req: &Request, _: &S) -> bool { *req.method() == self.0 } } @@ -188,7 +190,7 @@ pub fn Header( pub struct HeaderPredicate(header::HeaderName, header::HeaderValue, PhantomData); impl Predicate for HeaderPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { + fn check(&self, req: &Request, _: &S) -> bool { if let Some(val) = req.headers().get(&self.0) { return val == self.1; } @@ -225,7 +227,7 @@ impl HostPredicate { } impl Predicate for HostPredicate { - fn check(&self, req: &mut HttpRequest) -> bool { + fn check(&self, req: &Request, _: &S) -> bool { let info = req.connection_info(); if let Some(ref scheme) = self.1 { self.0 == info.host() && scheme == info.scheme() @@ -237,168 +239,96 @@ impl Predicate for HostPredicate { #[cfg(test)] mod tests { + use std::str::FromStr; + use super::*; use http::header::{self, HeaderMap}; use http::{Method, Uri, Version}; - use std::str::FromStr; + use test::TestRequest; #[test] fn test_header() { - let mut headers = HeaderMap::new(); - headers.insert( + let req = TestRequest::with_header( header::TRANSFER_ENCODING, header::HeaderValue::from_static("chunked"), - ); - let mut req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + ).finish(); let pred = Header("transfer-encoding", "chunked"); - assert!(pred.check(&mut req)); + assert!(pred.check(&req, req.state())); let pred = Header("transfer-encoding", "other"); - assert!(!pred.check(&mut req)); + assert!(!pred.check(&req, req.state())); let pred = Header("content-type", "other"); - assert!(!pred.check(&mut req)); + assert!(!pred.check(&req, req.state())); } #[test] fn test_host() { - let mut headers = HeaderMap::new(); - headers.insert( - header::HOST, - header::HeaderValue::from_static("www.rust-lang.org"), - ); - let mut req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::HOST, + header::HeaderValue::from_static("www.rust-lang.org"), + ) + .finish(); let pred = Host("www.rust-lang.org"); - assert!(pred.check(&mut req)); + assert!(pred.check(&req, req.state())); let pred = Host("localhost"); - assert!(!pred.check(&mut req)); + assert!(!pred.check(&req, req.state())); } #[test] fn test_methods() { - let mut req = HttpRequest::new( - Method::GET, - 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, - ); + let req = TestRequest::default().finish(); + let req2 = TestRequest::default().method(Method::POST).finish(); - assert!(Get().check(&mut req)); - assert!(!Get().check(&mut req2)); - assert!(Post().check(&mut req2)); - assert!(!Post().check(&mut req)); + assert!(Get().check(&req, req.state())); + assert!(!Get().check(&req2, req2.state())); + assert!(Post().check(&req2, req2.state())); + assert!(!Post().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::PUT, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Put().check(&mut r)); - assert!(!Put().check(&mut req)); + let r = TestRequest::default().method(Method::PUT).finish(); + assert!(Put().check(&r, r.state())); + assert!(!Put().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::DELETE, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Delete().check(&mut r)); - assert!(!Delete().check(&mut req)); + let r = TestRequest::default().method(Method::DELETE).finish(); + assert!(Delete().check(&r, r.state())); + assert!(!Delete().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::HEAD, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Head().check(&mut r)); - assert!(!Head().check(&mut req)); + let r = TestRequest::default().method(Method::HEAD).finish(); + assert!(Head().check(&r, r.state())); + assert!(!Head().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::OPTIONS, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Options().check(&mut r)); - assert!(!Options().check(&mut req)); + let r = TestRequest::default().method(Method::OPTIONS).finish(); + assert!(Options().check(&r, r.state())); + assert!(!Options().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::CONNECT, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Connect().check(&mut r)); - assert!(!Connect().check(&mut req)); + let r = TestRequest::default().method(Method::CONNECT).finish(); + assert!(Connect().check(&r, r.state())); + assert!(!Connect().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::PATCH, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Patch().check(&mut r)); - assert!(!Patch().check(&mut req)); + let r = TestRequest::default().method(Method::PATCH).finish(); + assert!(Patch().check(&r, r.state())); + assert!(!Patch().check(&req, req.state())); - let mut r = HttpRequest::new( - Method::TRACE, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); - assert!(Trace().check(&mut r)); - assert!(!Trace().check(&mut req)); + let r = TestRequest::default().method(Method::TRACE).finish(); + assert!(Trace().check(&r, r.state())); + assert!(!Trace().check(&req, req.state())); } #[test] fn test_preds() { - let mut r = HttpRequest::new( - Method::TRACE, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); + let r = TestRequest::default().method(Method::TRACE).finish(); - assert!(Not(Get()).check(&mut r)); - assert!(!Not(Trace()).check(&mut r)); + assert!(Not(Get()).check(&r, r.state())); + assert!(!Not(Trace()).check(&r, r.state())); - assert!(All(Trace()).and(Trace()).check(&mut r)); - assert!(!All(Get()).and(Trace()).check(&mut r)); + assert!(All(Trace()).and(Trace()).check(&r, r.state())); + assert!(!All(Get()).and(Trace()).check(&r, r.state())); - assert!(Any(Get()).or(Trace()).check(&mut r)); - assert!(!Any(Get()).or(Get()).check(&mut r)); + assert!(Any(Get()).or(Trace()).check(&r, r.state())); + assert!(!Any(Get()).or(Get()).check(&r, r.state())); } } diff --git a/src/resource.rs b/src/resource.rs index 2eae570c8..4e8ecf55f 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -12,6 +12,10 @@ use httpresponse::HttpResponse; use middleware::Middleware; use pred; 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. /// @@ -131,7 +135,7 @@ impl ResourceHandler { /// ```rust /// # extern crate 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)); /// ``` @@ -141,7 +145,7 @@ impl ResourceHandler { /// ```rust /// # extern crate 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)); /// ``` pub fn method(&mut self, method: Method) -> &mut Route { @@ -154,7 +158,7 @@ impl ResourceHandler { /// ```rust /// # extern crate actix_web; /// use actix_web::*; - /// fn handler(req: HttpRequest) -> HttpResponse { unimplemented!() } + /// fn handler(req: &HttpRequest) -> HttpResponse { unimplemented!() } /// /// App::new().resource("/", |r| r.h(handler)); /// ``` @@ -164,7 +168,7 @@ impl ResourceHandler { /// ```rust /// # extern crate 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)); /// ``` pub fn h>(&mut self, handler: H) { @@ -177,7 +181,7 @@ impl ResourceHandler { /// ```rust /// # extern crate actix_web; /// use actix_web::*; - /// fn index(req: HttpRequest) -> HttpResponse { unimplemented!() } + /// fn index(req: &HttpRequest) -> HttpResponse { unimplemented!() } /// /// App::new().resource("/", |r| r.f(index)); /// ``` @@ -187,12 +191,12 @@ impl ResourceHandler { /// ```rust /// # extern crate 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)); /// ``` pub fn f(&mut self, handler: F) where - F: Fn(HttpRequest) -> R + 'static, + F: Fn(&HttpRequest) -> R + 'static, R: Responder + 'static, { self.routes.push(Route::default()); @@ -279,19 +283,24 @@ impl ResourceHandler { .push(Box::new(mw)); } - pub(crate) fn handle( - &self, mut req: HttpRequest, - ) -> Result, HttpRequest> { - for route in &self.routes { - if route.check(&mut req) { - return if self.middlewares.is_empty() { - Ok(route.handle(req)) - } else { - Ok(route.compose(req, Rc::clone(&self.middlewares))) - }; + #[inline] + pub(crate) fn get_route_id(&self, req: &HttpRequest) -> Option { + for idx in 0..self.routes.len() { + if (&self.routes[idx]).check(req) { + return Some(RouteId(idx)); } } + None + } - Err(req) + #[inline] + pub(crate) fn handle( + &self, id: RouteId, req: &HttpRequest, + ) -> AsyncResult { + if self.middlewares.is_empty() { + (&self.routes[id.0]).handle(req) + } else { + (&self.routes[id.0]).compose(req.clone(), Rc::clone(&self.middlewares)) + } } } diff --git a/src/route.rs b/src/route.rs index fed4deb56..bf880a3ca 100644 --- a/src/route.rs +++ b/src/route.rs @@ -16,6 +16,7 @@ use middleware::{ Started as MiddlewareStarted, }; use pred::Predicate; +use server::Request; use with::{With, WithAsync}; /// Resource route definition @@ -31,16 +32,17 @@ impl Default for Route { fn default() -> Route { Route { preds: Vec::new(), - handler: InnerHandler::new(|_| HttpResponse::new(StatusCode::NOT_FOUND)), + handler: InnerHandler::new(|_: &_| HttpResponse::new(StatusCode::NOT_FOUND)), } } } impl Route { #[inline] - pub(crate) fn check(&self, req: &mut HttpRequest) -> bool { + pub(crate) fn check(&self, req: &HttpRequest) -> bool { + let state = req.state(); for pred in &self.preds { - if !pred.check(req) { + if !pred.check(req, state) { return false; } } @@ -48,7 +50,7 @@ impl Route { } #[inline] - pub(crate) fn handle(&self, req: HttpRequest) -> AsyncResult { + pub(crate) fn handle(&self, req: &HttpRequest) -> AsyncResult { self.handler.handle(req) } @@ -89,7 +91,7 @@ impl Route { /// during route configuration, so it does not return reference to self. pub fn f(&mut self, handler: F) where - F: Fn(HttpRequest) -> R + 'static, + F: Fn(&HttpRequest) -> R + 'static, R: Responder + 'static, { self.handler = InnerHandler::new(handler); @@ -98,7 +100,7 @@ impl Route { /// Set async handler function. pub fn a(&mut self, handler: H) where - H: Fn(HttpRequest) -> F + 'static, + H: Fn(&HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, @@ -307,7 +309,7 @@ impl InnerHandler { #[inline] fn async(h: H) -> Self where - H: Fn(HttpRequest) -> F + 'static, + H: Fn(&HttpRequest) -> F + 'static, F: Future + 'static, R: Responder + 'static, E: Into + 'static, @@ -316,7 +318,7 @@ impl InnerHandler { } #[inline] - pub fn handle(&self, req: HttpRequest) -> AsyncResult { + pub fn handle(&self, req: &HttpRequest) -> AsyncResult { self.0.handle(req) } } @@ -407,24 +409,27 @@ type Fut = Box, Error = Error>>; impl StartMiddlewares { fn init(info: &mut ComposeInfo) -> ComposeState { let len = info.mws.len(); + loop { if info.count == len { - let reply = info.handler.handle(info.req.clone()); + let reply = info.handler.handle(&info.req); return WaitingResponse::init(info, reply); } else { - let state = info.mws[info.count].start(&mut info.req); - match state { + let result = info.mws[info.count].start(&info.req); + match result { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => { - return RunMiddlewares::init(info, resp) + return RunMiddlewares::init(info, resp); } Ok(MiddlewareStarted::Future(fut)) => { return ComposeState::Starting(StartMiddlewares { fut: Some(fut), _s: PhantomData, - }) + }); + } + Err(err) => { + return RunMiddlewares::init(info, err.into()); } - Err(err) => return RunMiddlewares::init(info, err.into()), } } } @@ -432,9 +437,12 @@ impl StartMiddlewares { fn poll(&mut self, info: &mut ComposeInfo) -> Option> { let len = info.mws.len(); + 'outer: loop { match self.fut.as_mut().unwrap().poll() { - Ok(Async::NotReady) => return None, + Ok(Async::NotReady) => { + return None; + } Ok(Async::Ready(resp)) => { info.count += 1; if let Some(resp) = resp { @@ -442,11 +450,11 @@ impl StartMiddlewares { } loop { 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)); } else { - let state = info.mws[info.count].start(&mut info.req); - match state { + let result = info.mws[info.count].start(&info.req); + match result { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => { return Some(RunMiddlewares::init(info, resp)); @@ -456,21 +464,25 @@ impl StartMiddlewares { continue 'outer; } 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; + // waiting for response struct WaitingResponse { - fut: Box>, + fut: Box, _s: PhantomData, } @@ -480,8 +492,8 @@ impl WaitingResponse { info: &mut ComposeInfo, reply: AsyncResult, ) -> ComposeState { match reply.into() { - AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()), AsyncResultItem::Ok(resp) => RunMiddlewares::init(info, resp), + AsyncResultItem::Err(err) => RunMiddlewares::init(info, err.into()), AsyncResultItem::Future(fut) => ComposeState::Handler(WaitingResponse { fut, _s: PhantomData, @@ -492,7 +504,7 @@ impl WaitingResponse { fn poll(&mut self, info: &mut ComposeInfo) -> Option> { match self.fut.poll() { 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())), } } @@ -511,7 +523,7 @@ impl RunMiddlewares { let len = info.mws.len(); loop { - let state = info.mws[curr].response(&mut info.req, resp); + let state = info.mws[curr].response(&info.req, resp); resp = match state { Err(err) => { info.count = curr + 1; @@ -530,7 +542,7 @@ impl RunMiddlewares { curr, fut: Some(fut), _s: PhantomData, - }) + }); } }; } @@ -554,7 +566,7 @@ impl RunMiddlewares { if self.curr == len { return Some(FinishingMiddlewares::init(info, resp)); } else { - let state = info.mws[self.curr].response(&mut info.req, resp); + let state = info.mws[self.curr].response(&info.req, resp); match state { Err(err) => { return Some(FinishingMiddlewares::init(info, err.into())) @@ -625,7 +637,7 @@ impl FinishingMiddlewares { info.count -= 1; 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 { MiddlewareFinished::Done => { if info.count == 0 { diff --git a/src/router.rs b/src/router.rs index fcbb0d44e..f4da7da02 100644 --- a/src/router.rs +++ b/src/router.rs @@ -4,29 +4,133 @@ use std::rc::Rc; use regex::{escape, Regex}; use smallvec::SmallVec; +use url::Url; use error::UrlGenerationError; use httprequest::HttpRequest; -use param::ParamItem; +use param::{ParamItem, Params}; 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. pub struct Router(Rc); +#[derive(Clone)] +pub struct RouteInfo { + router: Rc, + 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 ¶ms.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( + &self, req: &Request, name: &str, elements: U, + ) -> Result + where + U: IntoIterator, + I: AsRef, + { + 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 { prefix: String, prefix_len: usize, named: HashMap, patterns: Vec, - srv: ServerSettings, } impl Router { /// Create new router pub fn new( - prefix: &str, settings: ServerSettings, - map: Vec<(Resource, Option>)>, + prefix: &str, map: Vec<(Resource, Option>)>, ) -> (Router, Vec>) { let prefix = prefix.trim().trim_right_matches('/').to_owned(); let mut named = HashMap::new(); @@ -52,7 +156,6 @@ impl Router { prefix_len, named, patterns, - srv: settings, })), resources, ) @@ -64,67 +167,61 @@ impl Router { &self.0.prefix } - /// Server settings - #[inline] - pub fn server_settings(&self) -> &ServerSettings { - &self.0.srv - } - pub(crate) fn get_resource(&self, idx: usize) -> &Resource { &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 - pub fn recognize(&self, req: &mut HttpRequest) -> Option { + pub fn recognize(&self, req: &Request) -> Option<(usize, RouteInfo)> { if self.0.prefix_len > req.path().len() { return None; } for (idx, pattern) in self.0.patterns.iter().enumerate() { - if pattern.match_with_params(req, self.0.prefix_len, true) { - let url = req.url().clone(); - req.match_info_mut().set_url(url); - req.set_resource(idx); - req.set_prefix_len(self.0.prefix_len as u16); - return Some(idx); + if let Some(params) = pattern.match_with_params(req, self.0.prefix_len, true) + { + return Some(( + idx, + RouteInfo { + params, + router: self.0.clone(), + resource: RouterResource::Normal(idx as u16), + prefix: self.0.prefix_len as u16, + }, + )); } } 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( - &self, name: &str, elements: U, - ) -> Result - where - U: IntoIterator, - I: AsRef, - { - if let Some(pattern) = self.0.named.get(name) { - pattern.0.resource_path(self, elements) - } else { - Err(UrlGenerationError::ResourceNotFound) - } - } } impl Clone for Router { @@ -160,7 +257,7 @@ pub enum ResourceType { } /// Resource type describes an entry in resources table -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct Resource { tp: PatternType, rtp: ResourceType, @@ -208,9 +305,7 @@ impl Resource { // actix creates one router per thread let names = re .capture_names() - .filter_map(|name| { - name.map(|name| Rc::new(name.to_owned())) - }) + .filter_map(|name| name.map(|name| Rc::new(name.to_owned()))) .collect(); PatternType::Dynamic(re, names, len) } else if for_prefix { @@ -253,127 +348,128 @@ impl Resource { } /// Are the given path and parameters a match against this resource? - pub fn match_with_params( - &self, req: &mut HttpRequest, plen: usize, insert: bool, - ) -> bool { - let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new(); - - let names = { - let path = &req.path()[plen..]; - if insert { - if path.is_empty() { - "/" - } else { - path - } + pub fn match_with_params( + &self, req: &Request, plen: usize, insert: bool, + ) -> Option { + let path = &req.path()[plen..]; + if insert { + if path.is_empty() { + "/" } else { path - }; - - match self.tp { - PatternType::Static(ref s) => return s == path, - PatternType::Dynamic(ref re, ref names, _) => { - if let Some(captures) = re.captures(path) { - let mut 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(); - let params = req.match_info_mut(); - params.set_tail(len as u16); - for (idx, segment) in segments.into_iter().enumerate() { - params.add(names[idx].clone(), segment); + match self.tp { + PatternType::Static(ref s) => if s != path { + None + } else { + 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? - pub fn match_prefix_with_params( - &self, req: &mut HttpRequest, plen: usize, - ) -> Option { - let mut segments: SmallVec<[ParamItem; 5]> = SmallVec::new(); + pub fn match_prefix_with_params( + &self, req: &Request, plen: usize, + ) -> Option { + let path = &req.path()[plen..]; + let path = if path.is_empty() { "/" } else { path }; - let (names, tail_len) = { - let path = &req.path()[plen..]; - let path = if path.is_empty() { "/" } else { path }; + match self.tp { + PatternType::Static(ref s) => if s == 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 { - PatternType::Static(ref s) => if s == path { - return Some(s.len()); - } 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( + params.add( + names[idx].clone(), + ParamItem::UrlSegment( (plen + m.start()) 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 } } - }; - - let params = req.match_info_mut(); - params.set_tail(tail_len as u16); - for (idx, segment) in segments.into_iter().enumerate() { - params.add(names[idx].clone(), segment); + PatternType::Prefix(ref s) => { + let len = if path == s { + s.len() + } else if path.starts_with(s) + && (s.ends_with('/') || path.split_at(s.len()).1.starts_with('/')) + { + 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. pub fn resource_path( - &self, router: &Router, elements: U, + &self, elements: U, prefix: &str, ) -> Result where U: IntoIterator, @@ -402,7 +498,6 @@ impl Resource { }; if self.rtp != ResourceType::External { - let prefix = router.prefix(); if prefix.ends_with('/') { if path.starts_with('/') { path.insert_str(0, &prefix[..prefix.len() - 1]); @@ -546,44 +641,57 @@ mod tests { Some(ResourceHandler::default()), ), ]; - let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); + let (rec, _) = Router::new::<()>("", routes); - let mut req = TestRequest::with_uri("/name").finish(); - assert_eq!(rec.recognize(&mut req), Some(0)); + let req = TestRequest::with_uri("/name").finish(); + assert_eq!(rec.recognize(&req).unwrap().0, 0); assert!(req.match_info().is_empty()); - let mut req = TestRequest::with_uri("/name/value").finish(); - assert_eq!(rec.recognize(&mut req), Some(1)); + let req = TestRequest::with_uri("/name/value").finish(); + 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()["val"], "value"); - let mut req = TestRequest::with_uri("/name/value2/index.html").finish(); - assert_eq!(rec.recognize(&mut req), Some(2)); + let req = TestRequest::with_uri("/name/value2/index.html").finish(); + 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"); - let mut req = TestRequest::with_uri("/file/file.gz").finish(); - assert_eq!(rec.recognize(&mut req), Some(3)); + let req = TestRequest::with_uri("/file/file.gz").finish(); + 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("ext").unwrap(), "gz"); - let mut req = TestRequest::with_uri("/vtest/ttt/index.html").finish(); - assert_eq!(rec.recognize(&mut req), Some(4)); + let req = TestRequest::with_uri("/vtest/ttt/index.html").finish(); + 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("val2").unwrap(), "ttt"); - let mut req = TestRequest::with_uri("/v/blah-blah/index.html").finish(); - assert_eq!(rec.recognize(&mut req), Some(5)); + let req = TestRequest::with_uri("/v/blah-blah/index.html").finish(); + let info = rec.recognize(&req).unwrap(); + assert_eq!(info.0, 5); + let req = req.with_route_info(info.1); assert_eq!( req.match_info().get("tail").unwrap(), "blah-blah/index.html" ); - let mut req = TestRequest::with_uri("/test2/index.html").finish(); - assert_eq!(rec.recognize(&mut req), Some(6)); + let req = TestRequest::with_uri("/test2/index.html").finish(); + 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"); - let mut req = TestRequest::with_uri("/bbb/index.html").finish(); - assert_eq!(rec.recognize(&mut req), Some(7)); + let req = TestRequest::with_uri("/bbb/index.html").finish(); + 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"); } @@ -599,13 +707,13 @@ mod tests { Some(ResourceHandler::default()), ), ]; - let (rec, _) = Router::new::<()>("", ServerSettings::default(), routes); + let (rec, _) = Router::new::<()>("", routes); - let mut req = TestRequest::with_uri("/index.json").finish(); - assert_eq!(rec.recognize(&mut req), Some(0)); + let req = TestRequest::with_uri("/index.json").finish(); + assert_eq!(rec.recognize(&req).unwrap().0, 0); - let mut req = TestRequest::with_uri("/test.json").finish(); - assert_eq!(rec.recognize(&mut req), Some(1)); + let req = TestRequest::with_uri("/test.json").finish(); + assert_eq!(rec.recognize(&req).unwrap().0, 1); } #[test] @@ -617,16 +725,18 @@ mod tests { 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(); - assert!(rec.recognize(&mut req).is_none()); + let req = TestRequest::with_uri("/name").finish(); + assert!(rec.recognize(&req).is_none()); - let mut req = TestRequest::with_uri("/test/name").finish(); - assert_eq!(rec.recognize(&mut req), Some(0)); + let req = TestRequest::with_uri("/test/name").finish(); + assert_eq!(rec.recognize(&req).unwrap().0, 0); - let mut req = TestRequest::with_uri("/test/name/value").finish(); - assert_eq!(rec.recognize(&mut req), Some(1)); + let req = TestRequest::with_uri("/test/name/value").finish(); + 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()["val"], "value"); @@ -638,16 +748,18 @@ mod tests { 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(); - assert!(rec.recognize(&mut req).is_none()); - let mut req = TestRequest::with_uri("/test2/name").finish(); - assert_eq!(rec.recognize(&mut req), Some(0)); - let mut req = TestRequest::with_uri("/test2/name-test").finish(); - assert!(rec.recognize(&mut req).is_none()); - let mut req = TestRequest::with_uri("/test2/name/ttt").finish(); - assert_eq!(rec.recognize(&mut req), Some(1)); + let req = TestRequest::with_uri("/name").finish(); + assert!(rec.recognize(&req).is_none()); + let req = TestRequest::with_uri("/test2/name").finish(); + assert_eq!(rec.recognize(&req).unwrap().0, 0); + let req = TestRequest::with_uri("/test2/name-test").finish(); + assert!(rec.recognize(&req).is_none()); + let req = TestRequest::with_uri("/test2/name/ttt").finish(); + 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"); } @@ -681,29 +793,23 @@ mod tests { assert!(!re.is_match("/user/2345/")); assert!(!re.is_match("/user/2345/sdg")); - let mut req = TestRequest::with_uri("/user/profile").finish(); - let url = req.url().clone(); - req.match_info_mut().set_url(url); - assert!(re.match_with_params(&mut req, 0, true)); - assert_eq!(req.match_info().get("id").unwrap(), "profile"); + let req = TestRequest::with_uri("/user/profile").finish(); + let info = re.match_with_params(&req, 0, true).unwrap(); + assert_eq!(info.get("id").unwrap(), "profile"); - let mut req = TestRequest::with_uri("/user/1245125").finish(); - let url = req.url().clone(); - req.match_info_mut().set_url(url); - assert!(re.match_with_params(&mut req, 0, true)); - assert_eq!(req.match_info().get("id").unwrap(), "1245125"); + let req = TestRequest::with_uri("/user/1245125").finish(); + let info = re.match_with_params(&req, 0, true).unwrap(); + assert_eq!(info.get("id").unwrap(), "1245125"); let re = Resource::new("test", "/v{version}/resource/{id}"); assert!(re.is_match("/v1/resource/320120")); assert!(!re.is_match("/v/resource/1")); assert!(!re.is_match("/resource")); - let mut req = TestRequest::with_uri("/v151/resource/adahg32").finish(); - let url = req.url().clone(); - req.match_info_mut().set_url(url); - assert!(re.match_with_params(&mut req, 0, true)); - assert_eq!(req.match_info().get("version").unwrap(), "151"); - assert_eq!(req.match_info().get("id").unwrap(), "adahg32"); + let req = TestRequest::with_uri("/v151/resource/adahg32").finish(); + let info = re.match_with_params(&req, 0, true).unwrap(); + assert_eq!(info.get("version").unwrap(), "151"); + assert_eq!(info.get("id").unwrap(), "adahg32"); } #[test] @@ -728,18 +834,15 @@ mod tests { assert!(re.is_match("/name/gs")); assert!(!re.is_match("/name")); - let mut req = TestRequest::with_uri("/test2/").finish(); - let url = req.url().clone(); - req.match_info_mut().set_url(url); - assert!(re.match_with_params(&mut req, 0, true)); - assert_eq!(&req.match_info()["name"], "test2"); + let req = TestRequest::with_uri("/test2/").finish(); + let info = re.match_with_params(&req, 0, true).unwrap(); + assert_eq!(&info["name"], "test2"); + assert_eq!(&info[0], "test2"); - let mut req = - TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish(); - let url = req.url().clone(); - req.match_info_mut().set_url(url); - assert!(re.match_with_params(&mut req, 0, true)); - assert_eq!(&req.match_info()["name"], "test2"); + let req = TestRequest::with_uri("/test2/subpath1/subpath2/index.html").finish(); + let info = re.match_with_params(&req, 0, true).unwrap(); + assert_eq!(&info["name"], "test2"); + assert_eq!(&info[0], "test2"); } #[test] @@ -754,18 +857,18 @@ mod tests { Some(ResourceHandler::default()), ), ]; - let (router, _) = Router::new::<()>("", ServerSettings::default(), routes); + let (router, _) = Router::new::<()>("", routes); - let mut req = - TestRequest::with_uri("/index.json").finish_with_router(router.clone()); - assert_eq!(router.recognize(&mut req), Some(0)); - let resource = req.resource().unwrap(); + let req = TestRequest::with_uri("/index.json").finish(); + assert_eq!(router.recognize(&req).unwrap().0, 0); + let info = router.recognize(&req).unwrap().1; + let resource = info.resource().unwrap(); assert_eq!(resource.name(), "r1"); - let mut req = - TestRequest::with_uri("/test.json").finish_with_router(router.clone()); - assert_eq!(router.recognize(&mut req), Some(1)); - let resource = req.resource().unwrap(); + let req = TestRequest::with_uri("/test.json").finish(); + assert_eq!(router.recognize(&req).unwrap().0, 1); + let info = router.recognize(&req).unwrap().1; + let resource = info.resource().unwrap(); assert_eq!(resource.name(), "r2"); } } diff --git a/src/scope.rs b/src/scope.rs index c9e7dfb93..5db1562b1 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -14,8 +14,9 @@ use middleware::{ Started as MiddlewareStarted, }; use pred::Predicate; -use resource::ResourceHandler; +use resource::{ResourceHandler, RouteId}; use router::Resource; +use server::Request; type ScopeResource = Rc>; type Route = Box>; @@ -114,7 +115,7 @@ impl Scope { /// /// struct AppState; /// - /// fn index(req: HttpRequest) -> &'static str { + /// fn index(req: &HttpRequest) -> &'static str { /// "Welcome!" /// } /// @@ -159,7 +160,7 @@ impl Scope { /// /// struct AppState; /// - /// fn index(req: HttpRequest) -> &'static str { + /// fn index(req: &HttpRequest) -> &'static str { /// "Welcome!" /// } /// @@ -334,75 +335,61 @@ impl Scope { } impl RouteHandler for Scope { - fn handle(&self, mut req: HttpRequest) -> AsyncResult { + fn handle(&self, req: &HttpRequest) -> AsyncResult { let tail = req.match_info().tail as usize; // recognize resources for &(ref pattern, ref resource) in self.resources.iter() { - if pattern.match_with_params(&mut req, tail, false) { - if self.middlewares.is_empty() { - return match resource.handle(req) { - Ok(result) => result, - Err(req) => { - if let Some(ref default) = self.default { - match default.handle(req) { - Ok(result) => result, - Err(_) => AsyncResult::ok(HttpResponse::new( - StatusCode::NOT_FOUND, - )), - } - } else { - AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)) - } - } - }; - } else { - return AsyncResult::async(Box::new(Compose::new( - req, - Rc::clone(&self.middlewares), - Rc::clone(&resource), - ))); + if let Some(params) = pattern.match_with_params(req, tail, false) { + let req2 = req.with_route_info(req.route().merge(params)); + if let Some(id) = resource.get_route_id(&req2) { + if self.middlewares.is_empty() { + return resource.handle(id, &req2); + } else { + return AsyncResult::async(Box::new(Compose::new( + id, + req2, + Rc::clone(&self.middlewares), + Rc::clone(&resource), + ))); + } } } } // 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 { - 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 { - if !filter.check(&mut req) { + if !filter.check(&req2, state) { continue 'outer; } } - let url = req.url().clone(); - 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); + return handler.handle(&req2); } } // default handler - if self.middlewares.is_empty() { - if let Some(ref default) = self.default { - match default.handle(req) { - Ok(result) => result, - Err(_) => AsyncResult::ok(HttpResponse::new(StatusCode::NOT_FOUND)), + if let Some(ref resource) = self.default { + if let Some(id) = resource.get_route_id(req) { + if self.middlewares.is_empty() { + return resource.handle(id, req); + } 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 { @@ -420,8 +407,9 @@ struct Wrapper { } impl RouteHandler for Wrapper { - fn handle(&self, req: HttpRequest) -> AsyncResult { - self.scope.handle(req.change_state(Rc::clone(&self.state))) + fn handle(&self, req: &HttpRequest) -> AsyncResult { + let req = req.with_state(Rc::clone(&self.state)); + self.scope.handle(&req) } } @@ -431,10 +419,9 @@ struct FiltersWrapper { } impl Predicate for FiltersWrapper { - fn check(&self, req: &mut HttpRequest) -> bool { - let mut req = req.change_state(Rc::clone(&self.state)); + fn check(&self, req: &Request, _: &S2) -> bool { for filter in &self.filters { - if !filter.check(&mut req) { + if !filter.check(&req, &self.state) { return false; } } @@ -450,6 +437,7 @@ struct Compose { struct ComposeInfo { count: usize, + id: RouteId, req: HttpRequest, mws: Rc>>>, resource: Rc>, @@ -477,14 +465,15 @@ impl ComposeState { impl Compose { fn new( - req: HttpRequest, mws: Rc>>>, + id: RouteId, req: HttpRequest, mws: Rc>>>, resource: Rc>, ) -> Self { let mut info = ComposeInfo { - count: 0, - req, + id, mws, + req, resource, + count: 0, }; let state = StartMiddlewares::init(&mut info); @@ -522,27 +511,27 @@ type Fut = Box, Error = Error>>; impl StartMiddlewares { fn init(info: &mut ComposeInfo) -> ComposeState { let len = info.mws.len(); + loop { if info.count == len { - let reply = { - let req = info.req.clone(); - info.resource.handle(req).unwrap() - }; + let reply = info.resource.handle(info.id, &info.req); return WaitingResponse::init(info, reply); } else { - let state = info.mws[info.count].start(&mut info.req); - match state { + let result = info.mws[info.count].start(&info.req); + match result { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => { - return RunMiddlewares::init(info, resp) + return RunMiddlewares::init(info, resp); } Ok(MiddlewareStarted::Future(fut)) => { return ComposeState::Starting(StartMiddlewares { fut: Some(fut), _s: PhantomData, - }) + }); + } + Err(err) => { + return RunMiddlewares::init(info, err.into()); } - Err(err) => return RunMiddlewares::init(info, err.into()), } } } @@ -550,24 +539,25 @@ impl StartMiddlewares { fn poll(&mut self, info: &mut ComposeInfo) -> Option> { let len = info.mws.len(); + 'outer: loop { match self.fut.as_mut().unwrap().poll() { - Ok(Async::NotReady) => return None, + Ok(Async::NotReady) => { + return None; + } Ok(Async::Ready(resp)) => { info.count += 1; + if let Some(resp) = resp { return Some(RunMiddlewares::init(info, resp)); } loop { if info.count == len { - let reply = { - let req = info.req.clone(); - info.resource.handle(req).unwrap() - }; + let reply = { info.resource.handle(info.id, &info.req) }; return Some(WaitingResponse::init(info, reply)); } else { - let state = info.mws[info.count].start(&mut info.req); - match state { + let result = info.mws[info.count].start(&info.req); + match result { Ok(MiddlewareStarted::Done) => info.count += 1, Ok(MiddlewareStarted::Response(resp)) => { return Some(RunMiddlewares::init(info, resp)); @@ -577,13 +567,15 @@ impl StartMiddlewares { continue 'outer; } 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 WaitingResponse { fn poll(&mut self, info: &mut ComposeInfo) -> Option> { match self.fut.poll() { 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())), } } @@ -632,7 +624,7 @@ impl RunMiddlewares { let len = info.mws.len(); loop { - let state = info.mws[curr].response(&mut info.req, resp); + let state = info.mws[curr].response(&info.req, resp); resp = match state { Err(err) => { info.count = curr + 1; @@ -651,7 +643,7 @@ impl RunMiddlewares { curr, fut: Some(fut), _s: PhantomData, - }) + }); } }; } @@ -675,7 +667,7 @@ impl RunMiddlewares { if self.curr == len { return Some(FinishingMiddlewares::init(info, resp)); } else { - let state = info.mws[self.curr].response(&mut info.req, resp); + let state = info.mws[self.curr].response(&info.req, resp); match state { Err(err) => { return Some(FinishingMiddlewares::init(info, err.into())) @@ -745,7 +737,7 @@ impl FinishingMiddlewares { info.count -= 1; 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 { MiddlewareFinished::Done => { if info.count == 0 { @@ -794,7 +786,7 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/path1").finish(); + let req = TestRequest::with_uri("/app/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -809,11 +801,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app").finish(); + let req = TestRequest::with_uri("/app").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } @@ -826,11 +818,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app").finish(); + let req = TestRequest::with_uri("/app").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -843,11 +835,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app").finish(); + let req = TestRequest::with_uri("/app").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -866,19 +858,19 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/path1").finish(); + let req = TestRequest::with_uri("/app/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); let req = TestRequest::with_uri("/app/path1") .method(Method::DELETE) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); let req = TestRequest::with_uri("/app/path1") .method(Method::POST) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -895,13 +887,13 @@ mod tests { let req = TestRequest::with_uri("/app/path1") .method(Method::POST) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); let req = TestRequest::with_uri("/app/path1") .method(Method::GET) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -919,7 +911,7 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/ab-project1/path1").finish(); + let req = TestRequest::with_uri("/ab-project1/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); @@ -931,7 +923,7 @@ mod tests { _ => panic!(), } - let req = TestRequest::with_uri("/aa-project1/path1").finish(); + let req = TestRequest::with_uri("/aa-project1/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -948,7 +940,7 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1/path1").finish(); + let req = TestRequest::with_uri("/app/t1/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } @@ -967,11 +959,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1").finish(); + let req = TestRequest::with_uri("/app/t1").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } @@ -988,11 +980,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1").finish(); + let req = TestRequest::with_uri("/app/t1").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -1009,11 +1001,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1").finish(); + let req = TestRequest::with_uri("/app/t1").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -1034,13 +1026,13 @@ mod tests { let req = TestRequest::with_uri("/app/t1/path1") .method(Method::POST) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); let req = TestRequest::with_uri("/app/t1/path1") .method(Method::GET) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -1055,7 +1047,7 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1/path1").finish(); + let req = TestRequest::with_uri("/app/t1/path1").request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } @@ -1072,11 +1064,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/t1").finish(); + let req = TestRequest::with_uri("/app/t1").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); } @@ -1095,13 +1087,13 @@ mod tests { let req = TestRequest::with_uri("/app/t1/path1") .method(Method::POST) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); let req = TestRequest::with_uri("/app/t1/path1") .method(Method::GET) - .finish(); + .request(); let resp = app.run(req); assert_eq!(resp.as_msg().status(), StatusCode::OK); } @@ -1123,7 +1115,7 @@ mod tests { }) .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); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); @@ -1156,7 +1148,7 @@ mod tests { }) .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); assert_eq!(resp.as_msg().status(), StatusCode::CREATED); @@ -1168,7 +1160,7 @@ mod tests { _ => 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -1183,11 +1175,11 @@ mod tests { }) .finish(); - let req = TestRequest::with_uri("/app/path2").finish(); + let req = TestRequest::with_uri("/app/path2").request(); let resp = app.run(req); 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); assert_eq!(resp.as_msg().status(), StatusCode::NOT_FOUND); } @@ -1202,15 +1194,15 @@ mod tests { .default_resource(|r| r.f(|_| HttpResponse::MethodNotAllowed())) .finish(); - let req = TestRequest::with_uri("/non-exist").finish(); + let req = TestRequest::with_uri("/non-exist").request(); let resp = app.run(req); 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); 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); assert_eq!(resp.as_msg().status(), StatusCode::METHOD_NOT_ALLOWED); } diff --git a/src/server/error.rs b/src/server/error.rs new file mode 100644 index 000000000..79b3e4f0b --- /dev/null +++ b/src/server/error.rs @@ -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 { + Box::new(ServerError(ver, status)) + } +} + +impl HttpHandlerTask for ServerError { + fn poll_io(&mut self, io: &mut Writer) -> Poll { + { + let mut bytes = io.buffer(); + helpers::write_status_line(self.0, self.1.as_u16(), bytes); + } + io.set_date(); + Ok(Async::Ready(true)) + } +} diff --git a/src/server/h1.rs b/src/server/h1.rs index e358f84b3..a9b3100fd 100644 --- a/src/server/h1.rs +++ b/src/server/h1.rs @@ -8,11 +8,13 @@ use futures::{Async, Future, Poll}; use tokio_timer::Delay; use error::{Error, PayloadError}; +use http::{StatusCode, Version}; use httprequest::HttpRequest; use httpresponse::HttpResponse; use payload::{Payload, PayloadStatus, PayloadWriter}; use pipeline::Pipeline; +use super::error::ServerError; use super::h1decoder::{DecoderError, H1Decoder, Message}; use super::h1writer::H1Writer; use super::input::PayloadType; @@ -180,9 +182,7 @@ where && self.tasks.len() < MAX_PIPELINED_MESSAGES && self.can_read() { - let res = self.stream.get_mut().read_available(&mut self.buf); - match res { - //self.stream.get_mut().read_available(&mut self.buf) { + match self.stream.get_mut().read_available(&mut self.buf) { Ok(Async::Ready(disconnected)) => { if disconnected { // notify all tasks @@ -363,22 +363,19 @@ where if payload { let (ps, pl) = Payload::new(false); - msg.get_mut().payload = Some(pl); - self.payload = - Some(PayloadType::new(&msg.get_ref().headers, ps)); + *msg.inner.payload.borrow_mut() = Some(pl); + self.payload = Some(PayloadType::new(&msg.inner.headers, ps)); } - let mut req = HttpRequest::from_message(msg); - // set remote addr - req.set_peer_addr(self.addr); + msg.inner.addr = self.addr; // stop keepalive timer self.keepalive_timer.take(); // search handler for request for h in self.settings.handlers().iter_mut() { - req = match h.handle(req) { + msg = match h.handle(msg) { Ok(mut pipe) => { if self.tasks.is_empty() { match pipe.poll_io(&mut self.stream) { @@ -415,15 +412,16 @@ where }); continue 'outer; } - Err(req) => req, + Err(msg) => msg, } } // handler is not found self.tasks.push_back(Entry { - pipe: EntryPipe::Error( - Pipeline::error(HttpResponse::NotFound()), - ), + pipe: EntryPipe::Error(ServerError::err( + Version::HTTP_11, + StatusCode::NOT_FOUND, + )), flags: EntryFlags::empty(), }); } @@ -475,12 +473,11 @@ mod tests { use application::HttpApplication; use httpmessage::HttpMessage; use server::h1decoder::Message; - use server::helpers::SharedHttpInnerMessage; - use server::settings::WorkerSettings; - use server::KeepAlive; + use server::settings::{ServerSettings, WorkerSettings}; + use server::{KeepAlive, Request}; impl Message { - fn message(self) -> SharedHttpInnerMessage { + fn message(self) -> Request { match self { Message::Message { msg, payload: _ } => msg, _ => panic!("error"), @@ -509,9 +506,9 @@ mod tests { macro_rules! parse_ready { ($e:expr) => {{ let settings: WorkerSettings = - WorkerSettings::new(Vec::new(), KeepAlive::Os); + WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default()); 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"), Err(err) => unreachable!("Error during parsing http request: {:?}", err), } @@ -521,7 +518,7 @@ mod tests { macro_rules! expect_parse_err { ($e:expr) => {{ let settings: WorkerSettings = - WorkerSettings::new(Vec::new(), KeepAlive::Os); + WorkerSettings::new(Vec::new(), KeepAlive::Os, ServerSettings::default()); match H1Decoder::new().decode($e, &settings) { Err(err) => match err { @@ -605,6 +602,7 @@ mod tests { let settings = Rc::new(WorkerSettings::::new( Vec::new(), KeepAlive::Os, + ServerSettings::default(), )); let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf); @@ -620,6 +618,7 @@ mod tests { let settings = Rc::new(WorkerSettings::::new( Vec::new(), KeepAlive::Os, + ServerSettings::default(), )); let mut h1 = Http1::new(Rc::clone(&settings), buf, None, readbuf); @@ -631,12 +630,16 @@ mod tests { #[test] fn test_parse() { let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n\r\n"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); match reader.decode(&mut buf, &settings) { Ok(Some(msg)) => { - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert_eq!(req.version(), Version::HTTP_11); assert_eq!(*req.method(), Method::GET); assert_eq!(req.path(), "/test"); @@ -648,7 +651,11 @@ mod tests { #[test] fn test_parse_partial() { let mut buf = BytesMut::from("PUT /test HTTP/1"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); match reader.decode(&mut buf, &settings) { @@ -659,7 +666,7 @@ mod tests { buf.extend(b".1\r\n\r\n"); match reader.decode(&mut buf, &settings) { 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.method(), Method::PUT); assert_eq!(req.path(), "/test"); @@ -671,12 +678,16 @@ mod tests { #[test] fn test_parse_post() { let mut buf = BytesMut::from("POST /test2 HTTP/1.0\r\n\r\n"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); match reader.decode(&mut buf, &settings) { 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.method(), Method::POST); assert_eq!(req.path(), "/test2"); @@ -689,12 +700,16 @@ mod tests { fn test_parse_body() { let mut buf = BytesMut::from("GET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); match reader.decode(&mut buf, &settings) { 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.method(), Method::GET); assert_eq!(req.path(), "/test"); @@ -716,12 +731,16 @@ mod tests { fn test_parse_body_crlf() { let mut buf = BytesMut::from("\r\nGET /test HTTP/1.1\r\nContent-Length: 4\r\n\r\nbody"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); match reader.decode(&mut buf, &settings) { 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.method(), Method::GET); assert_eq!(req.path(), "/test"); @@ -742,14 +761,18 @@ mod tests { #[test] fn test_parse_partial_eof() { let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); assert!(reader.decode(&mut buf, &settings).unwrap().is_none()); buf.extend(b"\r\n"); match reader.decode(&mut buf, &settings) { Ok(Some(msg)) => { - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert_eq!(req.version(), Version::HTTP_11); assert_eq!(*req.method(), Method::GET); assert_eq!(req.path(), "/test"); @@ -761,7 +784,11 @@ mod tests { #[test] fn test_headers_split_field() { let mut buf = BytesMut::from("GET /test HTTP/1.1\r\n"); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); assert!{ reader.decode(&mut buf, &settings).unwrap().is_none() } @@ -775,7 +802,7 @@ mod tests { buf.extend(b"t: value\r\n\r\n"); match reader.decode(&mut buf, &settings) { Ok(Some(msg)) => { - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert_eq!(req.version(), Version::HTTP_11); assert_eq!(*req.method(), Method::GET); assert_eq!(req.path(), "/test"); @@ -792,10 +819,14 @@ mod tests { Set-Cookie: c1=cookie1\r\n\ Set-Cookie: c2=cookie2\r\n\r\n", ); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); let val: Vec<_> = req .headers() @@ -988,7 +1019,11 @@ mod tests { #[test] fn test_http_request_upgrade() { - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut buf = BytesMut::from( "GET /test HTTP/1.1\r\n\ connection: upgrade\r\n\ @@ -998,7 +1033,7 @@ mod tests { let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert!(!req.keep_alive()); assert!(req.upgrade()); assert_eq!( @@ -1054,12 +1089,16 @@ mod tests { "GET /test HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\r\n", ); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert!(req.chunked().unwrap()); 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\ transfer-encoding: chunked\r\n\r\n", ); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert!(req.chunked().unwrap()); buf.extend( @@ -1112,7 +1155,7 @@ mod tests { let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req2 = HttpRequest::from_message(msg.message()); + let req2 = msg.message(); assert!(req2.chunked().unwrap()); assert_eq!(*req2.method(), Method::POST); assert!(req2.chunked().unwrap()); @@ -1124,12 +1167,16 @@ mod tests { "GET /test HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\r\n", ); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req = HttpRequest::from_message(msg.message()); + let req = msg.message(); assert!(req.chunked().unwrap()); buf.extend(b"4\r\n1111\r\n"); @@ -1171,13 +1218,16 @@ mod tests { &"GET /test HTTP/1.1\r\n\ transfer-encoding: chunked\r\n\r\n"[..], ); - let settings = WorkerSettings::::new(Vec::new(), KeepAlive::Os); + let settings = WorkerSettings::::new( + Vec::new(), + KeepAlive::Os, + ServerSettings::default(), + ); let mut reader = H1Decoder::new(); let msg = reader.decode(&mut buf, &settings).unwrap().unwrap(); assert!(msg.is_payload()); - let req = HttpRequest::from_message(msg.message()); - assert!(req.chunked().unwrap()); + assert!(msg.message().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") let chunk = reader.decode(&mut buf, &settings).unwrap().unwrap().chunk(); diff --git a/src/server/h1decoder.rs b/src/server/h1decoder.rs index 9815b9364..9f14bb478 100644 --- a/src/server/h1decoder.rs +++ b/src/server/h1decoder.rs @@ -4,12 +4,11 @@ use bytes::{Bytes, BytesMut}; use futures::{Async, Poll}; use httparse; -use super::helpers::SharedHttpInnerMessage; +use super::message::{MessageFlags, Request}; use super::settings::WorkerSettings; use error::ParseError; use http::header::{HeaderName, HeaderValue}; use http::{header, HttpTryFrom, Method, Uri, Version}; -use httprequest::MessageFlags; use uri::Url; const MAX_BUFFER_SIZE: usize = 131_072; @@ -20,10 +19,7 @@ pub(crate) struct H1Decoder { } pub(crate) enum Message { - Message { - msg: SharedHttpInnerMessage, - payload: bool, - }, + Message { msg: Request, payload: bool }, Chunk(Bytes), Eof, } @@ -84,7 +80,7 @@ impl H1Decoder { fn parse_message( &self, buf: &mut BytesMut, settings: &WorkerSettings, - ) -> Poll<(SharedHttpInnerMessage, Option), ParseError> { + ) -> Poll<(Request, Option), ParseError> { // Parse http message let mut has_upgrade = false; let mut chunked = false; @@ -122,11 +118,12 @@ impl H1Decoder { let slice = buf.split_to(len).freeze(); // convert headers - let mut msg = settings.get_http_message(); + let mut msg = settings.get_request_context(); { - let msg_mut = msg.get_mut(); - msg_mut + let inner = &mut msg.inner; + inner .flags + .get_mut() .set(MessageFlags::KEEPALIVE, version != Version::HTTP_10); for idx in headers[..headers_len].iter() { @@ -177,20 +174,20 @@ impl H1Decoder { } else { 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 { return Err(ParseError::Header); } } - msg_mut.url = path; - msg_mut.method = method; - msg_mut.version = version; + inner.url = path; + inner.method = method; + inner.version = version; } msg }; @@ -202,7 +199,7 @@ impl H1Decoder { } else if let Some(len) = content_length { // Content-Length 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 Some(EncodingDecoder::eof()) } else { diff --git a/src/server/h1writer.rs b/src/server/h1writer.rs index b87891c20..70797804f 100644 --- a/src/server/h1writer.rs +++ b/src/server/h1writer.rs @@ -7,14 +7,16 @@ use std::rc::Rc; use tokio_io::AsyncWrite; use super::helpers; -use super::output::Output; +use super::output::{Output, ResponseInfo, ResponseLength}; use super::settings::WorkerSettings; +use super::Request; use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE}; use body::{Binary, Body}; 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 httprequest::HttpInnerMessage; use httpresponse::HttpResponse; const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific @@ -101,8 +103,8 @@ impl Writer for H1Writer { } #[inline] - fn set_date(&self, dst: &mut BytesMut) { - self.settings.set_date(dst, true) + fn set_date(&mut self) { + self.settings.set_date(self.buffer.as_mut(), true) } #[inline] @@ -111,11 +113,11 @@ impl Writer for H1Writer { } fn start( - &mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, - encoding: ContentEncoding, + &mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding, ) -> io::Result { // 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()) { self.flags = Flags::STARTED | Flags::KEEPALIVE; } else { @@ -123,7 +125,7 @@ impl Writer for H1Writer { } // Connection upgrade - let version = msg.version().unwrap_or_else(|| req.version); + let version = msg.version().unwrap_or_else(|| req.inner.version); if msg.upgrade() { self.flags.insert(Flags::UPGRADE); msg.headers_mut() @@ -166,16 +168,29 @@ impl Writer for H1Writer { helpers::write_status_line(version, msg.status().as_u16(), &mut buffer); buffer.extend_from_slice(reason); - match body { - Body::Empty => if req.method != Method::HEAD { - buffer.extend_from_slice(b"\r\ncontent-length: 0\r\n"); - } else { - buffer.extend_from_slice(b"\r\n"); - }, - Body::Binary(ref bytes) => { - helpers::write_content_length(bytes.len(), &mut buffer) + // content length + match info.length { + ResponseLength::Chunked => { + buffer.extend_from_slice(b"\r\ntransfer-encoding: chunked\r\n") } - _ => 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 @@ -185,9 +200,13 @@ impl Writer for H1Writer { unsafe { let mut buf = &mut *(buffer.bytes_mut() as *mut [u8]); for (key, value) in msg.headers() { - if is_bin && key == CONTENT_LENGTH { - is_bin = false; - continue; + match *key { + TRANSFER_ENCODING | CONTENT_ENCODING => continue, + CONTENT_LENGTH => match info.length { + ResponseLength::None => (), + _ => continue, + }, + _ => (), } has_date = has_date || key == DATE; let v = value.as_ref(); diff --git a/src/server/h2.rs b/src/server/h2.rs index c2a385725..a8f28fbfe 100644 --- a/src/server/h2.rs +++ b/src/server/h2.rs @@ -14,6 +14,7 @@ use tokio_io::{AsyncRead, AsyncWrite}; use tokio_timer::Delay; use error::{Error, PayloadError}; +use http::{StatusCode, Version}; use httpmessage::HttpMessage; use httprequest::HttpRequest; use httpresponse::HttpResponse; @@ -21,6 +22,7 @@ use payload::{Payload, PayloadStatus, PayloadWriter}; use pipeline::Pipeline; use uri::Url; +use super::error::ServerError; use super::h2writer::H2Writer; use super::input::PayloadType; use super::settings::WorkerSettings; @@ -331,34 +333,35 @@ impl Entry { // Payload and Content-Encoding let (psender, payload) = Payload::new(false); - let mut msg = settings.get_http_message(); - msg.get_mut().url = Url::new(parts.uri); - msg.get_mut().method = parts.method; - msg.get_mut().version = parts.version; - msg.get_mut().headers = parts.headers; - msg.get_mut().payload = Some(payload); - msg.get_mut().addr = addr; - - let mut req = HttpRequest::from_message(msg); + let mut msg = settings.get_request_context(); + msg.inner.url = Url::new(parts.uri); + msg.inner.method = parts.method; + msg.inner.version = parts.version; + msg.inner.headers = parts.headers; + *msg.inner.payload.borrow_mut() = Some(payload); + msg.inner.addr = addr; // Payload sender - let psender = PayloadType::new(req.headers(), psender); + let psender = PayloadType::new(msg.headers(), psender); // start request processing let mut task = None; for h in settings.handlers().iter_mut() { - req = match h.handle(req) { + msg = match h.handle(msg) { Ok(t) => { task = Some(t); break; } - Err(req) => req, + Err(msg) => msg, } } Entry { 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, stream: H2Writer::new(resp, Rc::clone(settings)), diff --git a/src/server/h2writer.rs b/src/server/h2writer.rs index 9a02bbf47..c4fc59972 100644 --- a/src/server/h2writer.rs +++ b/src/server/h2writer.rs @@ -9,15 +9,15 @@ use std::rc::Rc; use std::{cmp, io}; use http::header::{HeaderValue, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}; -use http::{HttpTryFrom, Version}; +use http::{HttpTryFrom, Method, Version}; use super::helpers; -use super::output::Output; +use super::message::Request; +use super::output::{Output, ResponseInfo}; use super::settings::WorkerSettings; use super::{Writer, WriterState, MAX_WRITE_BUFFER_SIZE}; use body::{Binary, Body}; use header::ContentEncoding; -use httprequest::HttpInnerMessage; use httpresponse::HttpResponse; const CHUNK_SIZE: usize = 16_384; @@ -75,8 +75,8 @@ impl Writer for H2Writer { } #[inline] - fn set_date(&self, dst: &mut BytesMut) { - self.settings.set_date(dst, true) + fn set_date(&mut self) { + self.settings.set_date(self.buffer.as_mut(), true) } #[inline] @@ -85,12 +85,12 @@ impl Writer for H2Writer { } fn start( - &mut self, req: &mut HttpInnerMessage, msg: &mut HttpResponse, - encoding: ContentEncoding, + &mut self, req: &Request, msg: &mut HttpResponse, encoding: ContentEncoding, ) -> io::Result { // prepare response 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 msg.headers_mut().remove(CONNECTION); diff --git a/src/server/helpers.rs b/src/server/helpers.rs index 939785f4c..03bbc8310 100644 --- a/src/server/helpers.rs +++ b/src/server/helpers.rs @@ -1,91 +1,7 @@ use bytes::{BufMut, BytesMut}; use http::Version; -use std::cell::RefCell; -use std::collections::VecDeque; -use std::rc::Rc; use std::{mem, ptr, slice}; -use httprequest::HttpInnerMessage; - -/// Internal use only! -pub(crate) struct SharedMessagePool(RefCell>>); - -impl SharedMessagePool { - pub fn new() -> SharedMessagePool { - SharedMessagePool(RefCell::new(VecDeque::with_capacity(128))) - } - - #[inline] - pub fn get(&self) -> Rc { - if let Some(msg) = self.0.borrow_mut().pop_front() { - msg - } else { - Rc::new(HttpInnerMessage::default()) - } - } - - #[inline] - pub fn release(&self, mut msg: Rc) { - 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>, - Option>, -); - -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, pool: Rc, - ) -> 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\ 2021222324252627282930313233343536373839\ 4041424344454647484950515253545556575859\ diff --git a/src/server/message.rs b/src/server/message.rs new file mode 100644 index 000000000..73b238730 --- /dev/null +++ b/src/server/message.rs @@ -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, +} + +pub(crate) struct InnerRequest { + pub(crate) version: Version, + pub(crate) method: Method, + pub(crate) url: InnerUrl, + pub(crate) flags: Cell, + pub(crate) headers: HeaderMap, + pub(crate) extensions: RefCell, + pub(crate) addr: Option, + pub(crate) info: RefCell, + pub(crate) payload: RefCell>, + 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 { + 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 { + self.inner.extensions.borrow() + } + + /// Mutable reference to a the request's extensions + #[inline] + pub fn extensions_mut(&self) -> RefMut { + 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 { + 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>, RefCell); + +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); + } + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index f10dacc2e..4f95ffb7f 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -8,6 +8,7 @@ use tokio_io::{AsyncRead, AsyncWrite}; use tokio_tcp::TcpStream; mod channel; +mod error; pub(crate) mod h1; pub(crate) mod h1decoder; mod h1writer; @@ -15,11 +16,13 @@ mod h2; mod h2writer; pub(crate) mod helpers; pub(crate) mod input; +pub(crate) mod message; pub(crate) mod output; pub(crate) mod settings; mod srv; mod worker; +pub use self::message::Request; pub use self::settings::ServerSettings; pub use self::srv::HttpServer; @@ -30,7 +33,7 @@ use actix::Message; use body::Binary; use error::Error; use header::ContentEncoding; -use httprequest::{HttpInnerMessage, HttpRequest}; +use httprequest::HttpRequest; use httpresponse::HttpResponse; /// max buffer size 64k @@ -128,13 +131,13 @@ pub trait HttpHandler: 'static { type Task: HttpHandlerTask; /// Handle request - fn handle(&self, req: HttpRequest) -> Result; + fn handle(&self, req: Request) -> Result; } impl HttpHandler for Box>> { type Task = Box; - fn handle(&self, req: HttpRequest) -> Result, HttpRequest> { + fn handle(&self, req: Request) -> Result, Request> { self.as_ref().handle(req) } } @@ -165,13 +168,13 @@ pub trait IntoHttpHandler { type Handler: HttpHandler; /// Convert into `HttpHandler` object. - fn into_handler(self, settings: ServerSettings) -> Self::Handler; + fn into_handler(self) -> Self::Handler; } impl IntoHttpHandler for T { type Handler = T; - fn into_handler(self, _: ServerSettings) -> Self::Handler { + fn into_handler(self) -> Self::Handler { self } } @@ -190,14 +193,13 @@ pub trait Writer { fn written(&self) -> u64; #[doc(hidden)] - fn set_date(&self, st: &mut BytesMut); + fn set_date(&mut self); #[doc(hidden)] fn buffer(&mut self) -> &mut BytesMut; fn start( - &mut self, req: &mut HttpInnerMessage, resp: &mut HttpResponse, - encoding: ContentEncoding, + &mut self, req: &Request, resp: &mut HttpResponse, encoding: ContentEncoding, ) -> io::Result; fn write(&mut self, payload: &Binary) -> io::Result; diff --git a/src/server/output.rs b/src/server/output.rs index ad0b80b63..1d9ee92a4 100644 --- a/src/server/output.rs +++ b/src/server/output.rs @@ -15,11 +15,37 @@ use http::header::{ }; use http::{HttpTryFrom, Method, Version}; +use super::message::{InnerRequest, Request}; use body::{Binary, Body}; use header::ContentEncoding; -use httprequest::HttpInnerMessage; 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)] pub(crate) enum Output { Empty(BytesMut), @@ -119,13 +145,12 @@ impl Output { } } - pub fn for_server( - &mut self, req: &HttpInnerMessage, resp: &mut HttpResponse, + pub(crate) fn for_server( + &mut self, info: &mut ResponseInfo, req: &InnerRequest, resp: &mut HttpResponse, response_encoding: ContentEncoding, ) { let buf = self.take(); let version = resp.version().unwrap_or_else(|| req.version); - let is_head = req.method == Method::HEAD; let mut len = 0; #[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] @@ -158,10 +183,7 @@ impl Output { encoding => encoding, }; if encoding.is_compression() { - resp.headers_mut().insert( - CONTENT_ENCODING, - HeaderValue::from_static(encoding.as_str()), - ); + info.content_encoding = Some(encoding.as_str()); } encoding } else { @@ -173,8 +195,8 @@ impl Output { #[cfg_attr(feature = "cargo-clippy", allow(match_ref_pats))] let transfer = match resp.body() { &Body::Empty => { - if req.method != Method::HEAD { - resp.headers_mut().remove(CONTENT_LENGTH); + if !info.head { + info.length = ResponseLength::Zero; } *self = Output::Empty(buf); return; @@ -216,13 +238,8 @@ impl Output { } } - if is_head { - let mut b = BytesMut::new(); - let _ = write!(b, "{}", len); - resp.headers_mut().insert( - CONTENT_LENGTH, - HeaderValue::try_from(b.freeze()).unwrap(), - ); + info.length = ResponseLength::Length(len); + if info.head { *self = Output::Empty(buf); } else { *self = Output::Buffer(buf); @@ -236,7 +253,7 @@ impl Output { } if encoding != ContentEncoding::Identity { encoding = ContentEncoding::Identity; - resp.headers_mut().remove(CONTENT_ENCODING); + info.content_encoding.take(); } TransferEncoding::eof(buf) } else { @@ -245,12 +262,12 @@ impl Output { { resp.headers_mut().remove(CONTENT_LENGTH); } - Output::streaming_encoding(buf, version, resp) + Output::streaming_encoding(info, buf, version, resp) } } }; // check for head response - if is_head { + if info.head { resp.set_body(Body::Empty); *self = Output::Empty(transfer.buf.unwrap()); return; @@ -277,18 +294,17 @@ impl Output { } fn streaming_encoding( - buf: BytesMut, version: Version, resp: &mut HttpResponse, + info: &mut ResponseInfo, buf: BytesMut, version: Version, + resp: &mut HttpResponse, ) -> TransferEncoding { match resp.chunked() { Some(true) => { // Enable transfer encoding - resp.headers_mut().remove(CONTENT_LENGTH); if version == Version::HTTP_2 { - resp.headers_mut().remove(TRANSFER_ENCODING); + info.length = ResponseLength::None; TransferEncoding::eof(buf) } else { - resp.headers_mut() - .insert(TRANSFER_ENCODING, HeaderValue::from_static("chunked")); + info.length = ResponseLength::Chunked; TransferEncoding::chunked(buf) } } @@ -315,6 +331,7 @@ impl Output { if !chunked { if let Some(len) = len { + info.length = ResponseLength::Length64(len); TransferEncoding::length(len, buf) } else { TransferEncoding::eof(buf) @@ -323,14 +340,11 @@ impl Output { // Enable transfer encoding match version { Version::HTTP_11 => { - resp.headers_mut().insert( - TRANSFER_ENCODING, - HeaderValue::from_static("chunked"), - ); + info.length = ResponseLength::Chunked; TransferEncoding::chunked(buf) } _ => { - resp.headers_mut().remove(TRANSFER_ENCODING); + info.length = ResponseLength::None; TransferEncoding::eof(buf) } } diff --git a/src/server/settings.rs b/src/server/settings.rs index 31750b220..7f38088c8 100644 --- a/src/server/settings.rs +++ b/src/server/settings.rs @@ -12,6 +12,7 @@ use time; use super::channel::Node; use super::helpers; +use super::message::{Request, RequestPool}; use super::KeepAlive; use body::Body; use httpresponse::{HttpResponse, HttpResponseBuilder, HttpResponsePool}; @@ -156,14 +157,17 @@ pub(crate) struct WorkerSettings { keep_alive: u64, ka_enabled: bool, bytes: Rc, - messages: Rc, + messages: &'static RequestPool, channels: Cell, node: Box>, date: UnsafeCell, + settings: ServerSettings, } impl WorkerSettings { - pub(crate) fn new(h: Vec, keep_alive: KeepAlive) -> WorkerSettings { + pub(crate) fn new( + h: Vec, keep_alive: KeepAlive, settings: ServerSettings, + ) -> WorkerSettings { let (keep_alive, ka_enabled) = match keep_alive { KeepAlive::Timeout(val) => (val as u64, true), KeepAlive::Os | KeepAlive::Tcp(_) => (0, true), @@ -171,14 +175,15 @@ impl WorkerSettings { }; WorkerSettings { - keep_alive, - ka_enabled, h: RefCell::new(h), bytes: Rc::new(SharedBytesPool::new()), - messages: Rc::new(helpers::SharedMessagePool::new()), + messages: RequestPool::pool(settings.clone()), channels: Cell::new(0), node: Box::new(Node::head()), date: UnsafeCell::new(Date::new()), + keep_alive, + ka_enabled, + settings, } } @@ -210,11 +215,8 @@ impl WorkerSettings { self.bytes.release_bytes(bytes) } - pub fn get_http_message(&self) -> helpers::SharedHttpInnerMessage { - helpers::SharedHttpInnerMessage::new( - self.messages.get(), - Rc::clone(&self.messages), - ) + pub fn get_request_context(&self) -> Request { + self.messages.get() } pub fn add_channel(&self) { @@ -316,7 +318,11 @@ mod tests { #[test] 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); settings.set_date(&mut buf1, true); let mut buf2 = BytesMut::with_capacity(DATE_VALUE_LENGTH + 10); diff --git a/src/server/srv.rs b/src/server/srv.rs index d5c94ea8c..772238927 100644 --- a/src/server/srv.rs +++ b/src/server/srv.rs @@ -358,12 +358,10 @@ where let addr = Arbiter::start(move |ctx: &mut Context<_>| { let s = ServerSettings::from_parts(parts); - let apps: Vec<_> = (*factory)() - .into_iter() - .map(|h| h.into_handler(s.clone())) - .collect(); + let apps: Vec<_> = + (*factory)().into_iter().map(|h| h.into_handler()).collect(); ctx.add_message_stream(rx); - Worker::new(apps, socks, ka) + Worker::new(apps, socks, ka, s) }); workers.push((idx, tx)); self.workers.push((idx, addr)); @@ -404,7 +402,7 @@ impl HttpServer { /// fn main() { /// 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") /// .expect("Can not bind to 127.0.0.1:0") /// .start(); @@ -559,9 +557,13 @@ impl HttpServer { let settings = ServerSettings::new(Some(addr), &self.host, secure); let apps: Vec<_> = (*self.factory)() .into_iter() - .map(|h| h.into_handler(settings.clone())) + .map(|h| h.into_handler()) .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 let signals = self.subscribe_to_signals(); @@ -645,12 +647,10 @@ impl StreamHandler2 for HttpServer { let addr = Arbiter::start(move |ctx: &mut Context<_>| { let settings = ServerSettings::new(Some(addr), &host, false); - let apps: Vec<_> = (*factory)() - .into_iter() - .map(|h| h.into_handler(settings.clone())) - .collect(); + let apps: Vec<_> = + (*factory)().into_iter().map(|h| h.into_handler()).collect(); ctx.add_message_stream(rx); - Worker::new(apps, socks, ka) + Worker::new(apps, socks, ka, settings) }); for item in &self.accept { let _ = item.1.send(Command::Worker(new_idx, tx.clone())); diff --git a/src/server/worker.rs b/src/server/worker.rs index 3d4ee8633..8fd3fe601 100644 --- a/src/server/worker.rs +++ b/src/server/worker.rs @@ -25,7 +25,7 @@ use actix::msgs::StopArbiter; use actix::{Actor, Arbiter, AsyncContext, Context, Handler, Message, Response}; use server::channel::HttpChannel; -use server::settings::WorkerSettings; +use server::settings::{ServerSettings, WorkerSettings}; use server::{HttpHandler, KeepAlive}; #[derive(Message)] @@ -68,6 +68,7 @@ where impl Worker { pub(crate) fn new( h: Vec, socks: Slab, keep_alive: KeepAlive, + settings: ServerSettings, ) -> Worker { let tcp_ka = if let KeepAlive::Tcp(val) = keep_alive { Some(time::Duration::new(val as u64, 0)) @@ -76,7 +77,7 @@ impl Worker { }; Worker { - settings: Rc::new(WorkerSettings::new(h, keep_alive)), + settings: Rc::new(WorkerSettings::new(h, keep_alive, settings)), socks, tcp_ka, } diff --git a/src/test.rs b/src/test.rs index b7ed4e79d..5fc06f65c 100644 --- a/src/test.rs +++ b/src/test.rs @@ -22,14 +22,15 @@ use client::{ClientConnector, ClientRequest, ClientRequestBuilder}; use error::Error; use handler::{AsyncResultItem, Handler, Responder}; use header::{Header, IntoHeaderValue}; -use httprequest::HttpRequest; +use httprequest::{HttpRequest, RouterResource}; use httpresponse::HttpResponse; use middleware::Middleware; use param::Params; use payload::Payload; use resource::ResourceHandler; use router::Router; -use server::{HttpServer, IntoHttpHandler, ServerSettings}; +use server::{HttpServer, IntoHttpHandler, Request, ServerSettings}; +use uri::Url as InnerUrl; use ws; /// The `TestServer` type. @@ -43,7 +44,7 @@ use ws; /// # extern crate actix_web; /// # use actix_web::*; /// # -/// # fn my_handler(req: HttpRequest) -> HttpResponse { +/// # fn my_handler(req: &HttpRequest) -> HttpResponse { /// # HttpResponse::Ok().into() /// # } /// # @@ -330,8 +331,12 @@ impl TestApp { } /// Register handler for "/" - pub fn handler>(&mut self, handler: H) { - self.app = Some(self.app.take().unwrap().resource("/", |r| r.h(handler))); + pub fn handler(&mut self, handler: F) + where + F: Fn(&HttpRequest) -> R + 'static, + R: Responder + 'static, + { + self.app = Some(self.app.take().unwrap().resource("/", |r| r.f(handler))); } /// Register middleware @@ -357,8 +362,8 @@ impl TestApp { impl IntoHttpHandler for TestApp { type Handler = HttpApplication; - fn into_handler(mut self, settings: ServerSettings) -> HttpApplication { - self.app.take().unwrap().into_handler(settings) + fn into_handler(mut self) -> HttpApplication { + self.app.take().unwrap().into_handler() } } @@ -384,7 +389,7 @@ impl Iterator for TestApp { /// # use actix_web::*; /// 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) { /// HttpResponse::Ok().into() /// } else { @@ -533,11 +538,19 @@ impl TestRequest { cookies, payload, } = self; - let mut req = HttpRequest::new(method, uri, version, headers, payload); + let (router, _) = Router::new::("/", 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.as_mut().params = params; - let (router, _) = Router::new::("/", ServerSettings::default(), Vec::new()); - req.with_state(Rc::new(state), router) + req } #[cfg(test)] @@ -554,10 +567,37 @@ impl TestRequest { payload, } = 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.as_mut().params = params; - req.with_state(Rc::new(state), router) + req + } + + /// 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 @@ -566,7 +606,7 @@ impl TestRequest { /// This method panics is handler returns actor or async result. pub fn run>(self, h: &H) -> Result { let req = self.finish(); - let resp = h.handle(req.clone()); + let resp = h.handle(&req); match resp.respond_to(&req) { Ok(resp) => match resp.into().into() { diff --git a/src/with.rs b/src/with.rs index c475ca011..0af626c8b 100644 --- a/src/with.rs +++ b/src/with.rs @@ -42,9 +42,9 @@ where { type Result = AsyncResult; - fn handle(&self, req: HttpRequest) -> Self::Result { + fn handle(&self, req: &HttpRequest) -> Self::Result { let mut fut = WithHandlerFut { - req, + req: req.clone(), started: false, hnd: Rc::clone(&self.hnd), cfg: self.cfg.clone(), @@ -167,9 +167,9 @@ where { type Result = AsyncResult; - fn handle(&self, req: HttpRequest) -> Self::Result { + fn handle(&self, req: &HttpRequest) -> Self::Result { let mut fut = WithAsyncHandlerFut { - req, + req: req.clone(), started: false, hnd: Rc::clone(&self.hnd), cfg: Rc::clone(&self.cfg), diff --git a/src/ws/client.rs b/src/ws/client.rs index 6a4fcf7c3..251b0edd6 100644 --- a/src/ws/client.rs +++ b/src/ws/client.rs @@ -24,7 +24,7 @@ use payload::PayloadHelper; use client::{ ClientConnector, ClientRequest, ClientRequestBuilder, ClientResponse, - HttpResponseParserError, SendRequest, SendRequestError, + HttpResponseParserError, Pipeline, SendRequest, SendRequestError, }; use super::frame::Frame; @@ -275,7 +275,7 @@ impl Client { struct Inner { tx: UnboundedSender, - rx: PayloadHelper, + rx: PayloadHelper>, closed: bool, } @@ -431,7 +431,7 @@ impl Future for ClientHandshake { let inner = Inner { tx: self.tx.take().unwrap(), - rx: PayloadHelper::new(resp), + rx: PayloadHelper::new(resp.payload()), closed: false, }; diff --git a/src/ws/mod.rs b/src/ws/mod.rs index 558ecb515..bc99414d3 100644 --- a/src/ws/mod.rs +++ b/src/ws/mod.rs @@ -13,7 +13,7 @@ //! use actix_web::{ws, HttpRequest, HttpResponse}; //! //! // do websocket handshake and start actor -//! fn ws_index(req: HttpRequest) -> Result { +//! fn ws_index(req: &HttpRequest) -> Result { //! ws::start(req, Ws) //! } //! @@ -171,15 +171,15 @@ pub enum Message { } /// Do websocket handshake and start actor -pub fn start(req: HttpRequest, actor: A) -> Result +pub fn start(req: &HttpRequest, actor: A) -> Result where A: Actor> + StreamHandler, S: 'static, { - let mut resp = handshake(&req)?; - let stream = WsStream::new(req.clone()); + let mut resp = handshake(req)?; + 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); Ok(resp.body(ctx)) @@ -359,162 +359,116 @@ pub trait WsWriter { #[cfg(test)] mod tests { + use std::str::FromStr; + use super::*; use http::{header, HeaderMap, Method, Uri, Version}; - use std::str::FromStr; + use test::TestRequest; #[test] fn test_handshake() { - let req = HttpRequest::new( - Method::POST, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); + let req = TestRequest::default().method(Method::POST).finish(); assert_eq!( HandshakeError::GetMethodRequired, handshake(&req).err().unwrap() ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - HeaderMap::new(), - None, - ); + let req = TestRequest::default().finish(); assert_eq!( HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert(header::UPGRADE, header::HeaderValue::from_static("test")); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header(header::UPGRADE, header::HeaderValue::from_static("test")) + .finish(); assert_eq!( HandshakeError::NoWebsocketUpgrade, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert( - header::UPGRADE, - header::HeaderValue::from_static("websocket"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .finish(); assert_eq!( HandshakeError::NoConnectionUpgrade, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert( - header::UPGRADE, - header::HeaderValue::from_static("websocket"), - ); - headers.insert( - header::CONNECTION, - header::HeaderValue::from_static("upgrade"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .header( + header::CONNECTION, + header::HeaderValue::from_static("upgrade"), + ) + .finish(); assert_eq!( HandshakeError::NoVersionHeader, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert( - header::UPGRADE, - header::HeaderValue::from_static("websocket"), - ); - headers.insert( - header::CONNECTION, - header::HeaderValue::from_static("upgrade"), - ); - headers.insert( - header::SEC_WEBSOCKET_VERSION, - header::HeaderValue::from_static("5"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .header( + header::CONNECTION, + header::HeaderValue::from_static("upgrade"), + ) + .header( + header::SEC_WEBSOCKET_VERSION, + header::HeaderValue::from_static("5"), + ) + .finish(); assert_eq!( HandshakeError::UnsupportedVersion, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert( - header::UPGRADE, - header::HeaderValue::from_static("websocket"), - ); - headers.insert( - header::CONNECTION, - header::HeaderValue::from_static("upgrade"), - ); - headers.insert( - header::SEC_WEBSOCKET_VERSION, - header::HeaderValue::from_static("13"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .header( + header::CONNECTION, + header::HeaderValue::from_static("upgrade"), + ) + .header( + header::SEC_WEBSOCKET_VERSION, + header::HeaderValue::from_static("13"), + ) + .finish(); assert_eq!( HandshakeError::BadWebsocketKey, handshake(&req).err().unwrap() ); - let mut headers = HeaderMap::new(); - headers.insert( - header::UPGRADE, - header::HeaderValue::from_static("websocket"), - ); - headers.insert( - header::CONNECTION, - header::HeaderValue::from_static("upgrade"), - ); - headers.insert( - header::SEC_WEBSOCKET_VERSION, - header::HeaderValue::from_static("13"), - ); - headers.insert( - header::SEC_WEBSOCKET_KEY, - header::HeaderValue::from_static("13"), - ); - let req = HttpRequest::new( - Method::GET, - Uri::from_str("/").unwrap(), - Version::HTTP_11, - headers, - None, - ); + let req = TestRequest::default() + .header( + header::UPGRADE, + header::HeaderValue::from_static("websocket"), + ) + .header( + header::CONNECTION, + header::HeaderValue::from_static("upgrade"), + ) + .header( + header::SEC_WEBSOCKET_VERSION, + header::HeaderValue::from_static("13"), + ) + .header( + header::SEC_WEBSOCKET_KEY, + header::HeaderValue::from_static("13"), + ) + .finish(); assert_eq!( StatusCode::SWITCHING_PROTOCOLS, handshake(&req).unwrap().finish().status() diff --git a/tests/test_client.rs b/tests/test_client.rs index dc147ccd3..c4575c878 100644 --- a/tests/test_client.rs +++ b/tests/test_client.rs @@ -67,7 +67,7 @@ fn test_simple() { #[test] fn test_with_query_parameter() { 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(), None => HttpResponse::BadRequest().finish(), }) @@ -110,7 +110,7 @@ fn test_no_decompress() { #[test] fn test_client_gzip_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -140,7 +140,7 @@ fn test_client_gzip_encoding_large() { let data = STR.repeat(10); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -173,7 +173,7 @@ fn test_client_gzip_encoding_large_random() { .collect::(); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -202,7 +202,7 @@ fn test_client_gzip_encoding_large_random() { #[test] fn test_client_brotli_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -236,7 +236,7 @@ fn test_client_brotli_encoding_large_random() { .collect::(); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(move |bytes: Bytes| { Ok(HttpResponse::Ok() @@ -266,7 +266,7 @@ fn test_client_brotli_encoding_large_random() { #[test] fn test_client_deflate_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -300,7 +300,7 @@ fn test_client_deflate_encoding_large_random() { .collect::(); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -328,7 +328,7 @@ fn test_client_deflate_encoding_large_random() { #[test] fn test_client_streaming_explicit() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .map_err(Error::from) .and_then(|body| { @@ -393,7 +393,7 @@ fn test_client_cookie_handling() { let mut srv = test::TestServer::new(move |app| { let cookie1 = cookie1b.clone(); let cookie2 = cookie2b.clone(); - app.handler(move |req: HttpRequest| { + app.handler(move |req: &HttpRequest| { // Check cookies were sent correctly req.cookie("cookie1").ok_or_else(err) .and_then(|c1| if c1.value() == "value1" { diff --git a/tests/test_middleware.rs b/tests/test_middleware.rs index 806211ea8..170495c6e 100644 --- a/tests/test_middleware.rs +++ b/tests/test_middleware.rs @@ -20,21 +20,21 @@ struct MiddlewareTest { } impl middleware::Middleware for MiddlewareTest { - fn start(&self, _: &mut HttpRequest) -> Result { + fn start(&self, _: &HttpRequest) -> Result { self.start .store(self.start.load(Ordering::Relaxed) + 1, Ordering::Relaxed); Ok(middleware::Started::Done) } fn response( - &self, _: &mut HttpRequest, resp: HttpResponse, + &self, _: &HttpRequest, resp: HttpResponse, ) -> Result { self.response .store(self.response.load(Ordering::Relaxed) + 1, Ordering::Relaxed); Ok(middleware::Response::Done(resp)) } - fn finish(&self, _: &mut HttpRequest, _: &HttpResponse) -> middleware::Finished { + fn finish(&self, _: &HttpRequest, _: &HttpResponse) -> middleware::Finished { self.finish .store(self.finish.load(Ordering::Relaxed) + 1, Ordering::Relaxed); middleware::Finished::Done @@ -331,7 +331,7 @@ fn test_scope_middleware_async_handler() { assert_eq!(num3.load(Ordering::Relaxed), 1); } -fn index_test_middleware_async_error(_: HttpRequest) -> FutureResponse { +fn index_test_middleware_async_error(_: &HttpRequest) -> FutureResponse { future::result(Err(error::ErrorBadRequest("TEST"))).responder() } @@ -412,7 +412,7 @@ fn test_resource_middleware_async_error() { App::new().resource("/test", move |r| { r.middleware(mw); - r.h(index_test_middleware_async_error); + r.f(index_test_middleware_async_error); }) }); @@ -432,7 +432,7 @@ struct MiddlewareAsyncTest { } impl middleware::Middleware for MiddlewareAsyncTest { - fn start(&self, _: &mut HttpRequest) -> Result { + fn start(&self, _: &HttpRequest) -> Result { let to = Delay::new(Instant::now() + Duration::from_millis(10)); let start = Arc::clone(&self.start); @@ -445,7 +445,7 @@ impl middleware::Middleware for MiddlewareAsyncTest { } fn response( - &self, _: &mut HttpRequest, resp: HttpResponse, + &self, _: &HttpRequest, resp: HttpResponse, ) -> Result { let to = Delay::new(Instant::now() + Duration::from_millis(10)); @@ -458,7 +458,7 @@ impl middleware::Middleware for MiddlewareAsyncTest { ))) } - fn finish(&self, _: &mut HttpRequest, _: &HttpResponse) -> middleware::Finished { + fn finish(&self, _: &HttpRequest, _: &HttpResponse) -> middleware::Finished { let to = Delay::new(Instant::now() + Duration::from_millis(10)); let finish = Arc::clone(&self.finish); @@ -697,7 +697,7 @@ fn test_async_resource_middleware() { }; App::new().resource("/test", move |r| { 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| { r.middleware(mw1); 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| { r.middleware(mw1); r.middleware(mw2); - r.h(|_| HttpResponse::Ok()); + r.f(|_| HttpResponse::Ok()); }) }); @@ -793,7 +793,7 @@ fn test_async_sync_resource_middleware_multiple() { struct MiddlewareWithErr; impl middleware::Middleware for MiddlewareWithErr { - fn start(&self, _req: &mut HttpRequest) -> Result { + fn start(&self, _: &HttpRequest) -> Result { Err(ErrorInternalServerError("middleware error")) } } @@ -801,7 +801,7 @@ impl middleware::Middleware for MiddlewareWithErr { struct MiddlewareAsyncWithErr; impl middleware::Middleware for MiddlewareAsyncWithErr { - fn start(&self, _req: &mut HttpRequest) -> Result { + fn start(&self, _: &HttpRequest) -> Result { Ok(middleware::Started::Future(Box::new(future::err( ErrorInternalServerError("middleware error"), )))) @@ -827,7 +827,7 @@ fn test_middleware_chain_with_error() { App::new() .middleware(mw1) .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(); @@ -857,7 +857,7 @@ fn test_middleware_async_chain_with_error() { App::new() .middleware(mw1) .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(); @@ -888,7 +888,7 @@ fn test_scope_middleware_chain_with_error() { scope .middleware(mw1) .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 .middleware(mw1) .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| { r.middleware(mw1); 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| { r.middleware(mw1); r.middleware(MiddlewareAsyncWithErr); - r.h(|_| HttpResponse::Ok()); + r.f(|_| HttpResponse::Ok()); }) }); diff --git a/tests/test_server.rs b/tests/test_server.rs index 508647357..4fb73a6a5 100644 --- a/tests/test_server.rs +++ b/tests/test_server.rs @@ -368,8 +368,8 @@ fn test_head_empty() { } // read response - //let bytes = srv.execute(response.body()).unwrap(); - //assert!(bytes.is_empty()); + // let bytes = srv.execute(response.body()).unwrap(); + // assert!(bytes.is_empty()); } #[test] @@ -524,7 +524,7 @@ fn test_body_brotli() { #[test] fn test_gzip_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -557,7 +557,7 @@ fn test_gzip_encoding() { fn test_gzip_encoding_large() { let data = STR.repeat(10); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -594,7 +594,7 @@ fn test_reading_gzip_encoding_large_random() { .collect::(); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -627,7 +627,7 @@ fn test_reading_gzip_encoding_large_random() { #[test] fn test_reading_deflate_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -660,7 +660,7 @@ fn test_reading_deflate_encoding() { fn test_reading_deflate_encoding_large() { let data = STR.repeat(10); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -697,7 +697,7 @@ fn test_reading_deflate_encoding_large_random() { .collect::(); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -731,7 +731,7 @@ fn test_reading_deflate_encoding_large_random() { #[test] fn test_brotli_encoding() { let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok() @@ -765,7 +765,7 @@ fn test_brotli_encoding() { fn test_brotli_encoding_large() { let data = STR.repeat(10); let mut srv = test::TestServer::new(|app| { - app.handler(|req: HttpRequest| { + app.handler(|req: &HttpRequest| { req.body() .and_then(|bytes: Bytes| { Ok(HttpResponse::Ok()