diff --git a/Cargo.toml b/Cargo.toml index 86a168093..8318ada5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,9 +68,9 @@ rust-tls = ["rustls", "actix-server/rust-tls"] [dependencies] actix-codec = "0.1.1" -actix-service = "0.3.4" +actix-service = "0.3.6" actix-utils = "0.3.4" -actix-router = "0.1.1" +actix-router = "0.1.2" actix-rt = "0.2.2" actix-web-codegen = "0.1.0-alpha.1" actix-http = { version = "0.1.0-alpha.3", features=["fail"] } @@ -124,4 +124,4 @@ actix-web-codegen = { path = "actix-web-codegen" } actix-web-actors = { path = "actix-web-actors" } actix-session = { path = "actix-session" } actix-files = { path = "actix-files" } -awc = { path = "awc" } +awc = { path = "awc" } \ No newline at end of file diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs index 6820d3622..e8eb8afda 100644 --- a/actix-files/src/lib.rs +++ b/actix-files/src/lib.rs @@ -423,7 +423,7 @@ impl

FilesService

{ > { log::debug!("Files: Failed to handle {}: {}", req.path(), e); if let Some(ref mut default) = self.default { - Either::B(default.call(ServiceRequest::from_parts(req, payload))) + default.call(ServiceRequest::from_parts(req, payload)) } else { Either::A(ok(ServiceResponse::from_err(e, req.clone()))) } @@ -955,6 +955,7 @@ mod tests { .method(Method::POST) .to_http_request(); let resp = file.respond_to(&req).unwrap(); + println!("RES: {:?}", resp); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); let file = NamedFile::open("Cargo.toml").unwrap(); diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs index 222e442c5..da70a878e 100644 --- a/actix-http/src/client/h2proto.rs +++ b/actix-http/src/client/h2proto.rs @@ -104,9 +104,8 @@ where let (parts, body) = resp.into_parts(); let payload = if head_req { Payload::None } else { body.into() }; - let mut head = ResponseHead::default(); + let mut head = ResponseHead::new(parts.status); head.version = parts.version; - head.status = parts.status; head.headers = parts.headers.into(); Ok((head, payload)) diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs index aff11181b..2d1785381 100644 --- a/actix-http/src/client/pool.rs +++ b/actix-http/src/client/pool.rs @@ -21,6 +21,7 @@ use tokio_timer::{sleep, Delay}; use super::connection::{ConnectionType, IoConnection}; use super::error::ConnectError; +#[allow(dead_code)] #[derive(Clone, Copy, PartialEq)] pub enum Protocol { Http1, diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 417441c6a..411649fc1 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -73,78 +73,75 @@ pub(crate) trait MessageType: Sized { let headers = self.headers_mut(); for idx in raw_headers.iter() { - if let Ok(name) = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]) - { - // Unsafe: httparse check header value for valid utf-8 - let value = unsafe { - HeaderValue::from_shared_unchecked( - slice.slice(idx.value.0, idx.value.1), - ) - }; - match name { - header::CONTENT_LENGTH => { - if let Ok(s) = value.to_str() { - if let Ok(len) = s.parse::() { - if len != 0 { - content_length = Some(len); - } - } else { - debug!("illegal Content-Length: {:?}", s); - return Err(ParseError::Header); + let name = + HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap(); + + // Unsafe: httparse check header value for valid utf-8 + let value = unsafe { + HeaderValue::from_shared_unchecked( + slice.slice(idx.value.0, idx.value.1), + ) + }; + match name { + header::CONTENT_LENGTH => { + if let Ok(s) = value.to_str() { + if let Ok(len) = s.parse::() { + if len != 0 { + content_length = Some(len); } } else { - debug!("illegal Content-Length: {:?}", value); + debug!("illegal Content-Length: {:?}", s); return Err(ParseError::Header); } + } else { + debug!("illegal Content-Length: {:?}", value); + return Err(ParseError::Header); } - // transfer-encoding - header::TRANSFER_ENCODING => { - if let Ok(s) = value.to_str().map(|s| s.trim()) { - chunked = s.eq_ignore_ascii_case("chunked"); - } else { - return Err(ParseError::Header); - } + } + // transfer-encoding + header::TRANSFER_ENCODING => { + if let Ok(s) = value.to_str().map(|s| s.trim()) { + chunked = s.eq_ignore_ascii_case("chunked"); + } else { + return Err(ParseError::Header); } - // connection keep-alive state - header::CONNECTION => { - ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) - { - if conn.eq_ignore_ascii_case("keep-alive") { - Some(ConnectionType::KeepAlive) - } else if conn.eq_ignore_ascii_case("close") { - Some(ConnectionType::Close) - } else if conn.eq_ignore_ascii_case("upgrade") { - Some(ConnectionType::Upgrade) - } else { - None - } + } + // connection keep-alive state + header::CONNECTION => { + ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) { + if conn.eq_ignore_ascii_case("keep-alive") { + Some(ConnectionType::KeepAlive) + } else if conn.eq_ignore_ascii_case("close") { + Some(ConnectionType::Close) + } else if conn.eq_ignore_ascii_case("upgrade") { + Some(ConnectionType::Upgrade) } else { None - }; - } - header::UPGRADE => { - has_upgrade = true; - // check content-length, some clients (dart) - // sends "content-length: 0" with websocket upgrade - if let Ok(val) = value.to_str().map(|val| val.trim()) { - if val.eq_ignore_ascii_case("websocket") { - content_length = None; - } } - } - header::EXPECT => { - let bytes = value.as_bytes(); - if bytes.len() >= 4 && &bytes[0..4] == b"100-" { - expect = true; - } - } - _ => (), + } else { + None + }; } - - headers.append(name, value); - } else { - return Err(ParseError::Header); + header::UPGRADE => { + has_upgrade = true; + // check content-length, some clients (dart) + // sends "content-length: 0" with websocket upgrade + if let Ok(val) = value.to_str().map(|val| val.trim()) { + if val.eq_ignore_ascii_case("websocket") { + content_length = None; + } + } + } + header::EXPECT => { + let bytes = value.as_bytes(); + if bytes.len() >= 4 && &bytes[0..4] == b"100-" { + expect = true; + } + } + _ => (), } + + headers.append(name, value); } } self.set_connection_type(ka); @@ -217,10 +214,10 @@ impl MessageType for Request { let mut msg = Request::new(); // convert headers - let len = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; + let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; // payload decoder - let decoder = match len { + let decoder = match length { PayloadLength::Payload(pl) => pl, PayloadLength::Upgrade => { // upgrade(websocket) @@ -287,13 +284,14 @@ impl MessageType for ResponseHead { } }; - let mut msg = ResponseHead::default(); + let mut msg = ResponseHead::new(status); + msg.version = ver; // convert headers - let len = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; + let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; // message payload - let decoder = if let PayloadLength::Payload(pl) = len { + let decoder = if let PayloadLength::Payload(pl) = length { pl } else if status == StatusCode::SWITCHING_PROTOCOLS { // switching protocol or connect @@ -305,9 +303,6 @@ impl MessageType for ResponseHead { PayloadType::None }; - msg.status = status; - msg.version = ver; - Ok(Some((msg, decoder))) } } diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs index bff05ab5c..a223161f9 100644 --- a/actix-http/src/h1/dispatcher.rs +++ b/actix-http/src/h1/dispatcher.rs @@ -218,7 +218,7 @@ where { fn can_read(&self) -> bool { if self.flags.contains(Flags::READ_DISCONNECT) { - return false; + false } else if let Some(ref info) = self.payload { info.need_read() == PayloadStatus::Read } else { diff --git a/actix-http/src/httpcodes.rs b/actix-http/src/httpcodes.rs index 5dfeefa9e..85c384374 100644 --- a/actix-http/src/httpcodes.rs +++ b/actix-http/src/httpcodes.rs @@ -8,7 +8,7 @@ macro_rules! STATIC_RESP { ($name:ident, $status:expr) => { #[allow(non_snake_case, missing_docs)] pub fn $name() -> ResponseBuilder { - Response::build($status) + ResponseBuilder::new($status) } }; } diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 5879e1915..5af802601 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -1,5 +1,9 @@ //! Basic http primitives for actix-net framework. -#![allow(clippy::type_complexity, clippy::new_without_default)] +#![allow( + clippy::type_complexity, + clippy::new_without_default, + clippy::borrow_interior_mutable_const +)] #[macro_use] extern crate log; diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index e1fb3a111..61ca5161e 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -1,5 +1,4 @@ use std::cell::{Ref, RefCell, RefMut}; -use std::collections::VecDeque; use std::rc::Rc; use bitflags::bitflags; @@ -171,20 +170,20 @@ pub struct ResponseHead { flags: Flags, } -impl Default for ResponseHead { - fn default() -> ResponseHead { +impl ResponseHead { + /// Create new instance of `ResponseHead` type + #[inline] + pub fn new(status: StatusCode) -> ResponseHead { ResponseHead { + status, version: Version::default(), - status: StatusCode::OK, headers: HeaderMap::with_capacity(12), reason: None, flags: Flags::empty(), extensions: RefCell::new(Extensions::new()), } } -} -impl ResponseHead { /// Message extensions #[inline] pub fn extensions(&self) -> Ref { @@ -335,8 +334,8 @@ pub(crate) struct BoxedResponseHead { impl BoxedResponseHead { /// Get new message from the pool of objects - pub fn new() -> Self { - RESPONSE_POOL.with(|p| p.get_message()) + pub fn new(status: StatusCode) -> Self { + RESPONSE_POOL.with(|p| p.get_message(status)) } } @@ -362,25 +361,25 @@ impl Drop for BoxedResponseHead { #[doc(hidden)] /// Request's objects pool -pub struct MessagePool(RefCell>>); +pub struct MessagePool(RefCell>>); #[doc(hidden)] /// Request's objects pool -pub struct BoxedResponsePool(RefCell>>); +pub struct BoxedResponsePool(RefCell>>); thread_local!(static REQUEST_POOL: &'static MessagePool = MessagePool::::create()); thread_local!(static RESPONSE_POOL: &'static BoxedResponsePool = BoxedResponsePool::create()); impl MessagePool { fn create() -> &'static MessagePool { - let pool = MessagePool(RefCell::new(VecDeque::with_capacity(128))); + let pool = MessagePool(RefCell::new(Vec::with_capacity(128))); Box::leak(Box::new(pool)) } /// Get message from the pool #[inline] fn get_message(&'static self) -> Message { - if let Some(mut msg) = self.0.borrow_mut().pop_front() { + if let Some(mut msg) = self.0.borrow_mut().pop() { if let Some(r) = Rc::get_mut(&mut msg) { r.clear(); } @@ -397,28 +396,29 @@ impl MessagePool { fn release(&self, msg: Rc) { let v = &mut self.0.borrow_mut(); if v.len() < 128 { - v.push_front(msg); + v.push(msg); } } } impl BoxedResponsePool { fn create() -> &'static BoxedResponsePool { - let pool = BoxedResponsePool(RefCell::new(VecDeque::with_capacity(128))); + let pool = BoxedResponsePool(RefCell::new(Vec::with_capacity(128))); Box::leak(Box::new(pool)) } /// Get message from the pool #[inline] - fn get_message(&'static self) -> BoxedResponseHead { - if let Some(mut head) = self.0.borrow_mut().pop_front() { + fn get_message(&'static self, status: StatusCode) -> BoxedResponseHead { + if let Some(mut head) = self.0.borrow_mut().pop() { head.reason = None; + head.status = status; head.headers.clear(); head.flags = Flags::empty(); BoxedResponseHead { head: Some(head) } } else { BoxedResponseHead { - head: Some(Box::alloc().init(ResponseHead::default())), + head: Some(Box::alloc().init(ResponseHead::new(status))), } } } @@ -428,7 +428,7 @@ impl BoxedResponsePool { fn release(&self, msg: Box) { let v = &mut self.0.borrow_mut(); if v.len() < 128 { - v.push_front(msg); + v.push(msg); } } } diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs index 707c9af63..330d33a45 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/response.rs @@ -41,11 +41,8 @@ impl Response { /// Constructs a response #[inline] pub fn new(status: StatusCode) -> Response { - let mut head = BoxedResponseHead::new(); - head.status = status; - Response { - head, + head: BoxedResponseHead::new(status), body: ResponseBody::Body(Body::Empty), error: None, } @@ -78,6 +75,16 @@ impl Response { } impl Response { + /// Constructs a response with body + #[inline] + pub fn with_body(status: StatusCode, body: B) -> Response { + Response { + head: BoxedResponseHead::new(status), + body: ResponseBody::Body(body), + error: None, + } + } + #[inline] /// Http message part of the response pub fn head(&self) -> &ResponseHead { @@ -90,18 +97,6 @@ impl Response { &mut *self.head } - /// Constructs a response with body - #[inline] - pub fn with_body(status: StatusCode, body: B) -> Response { - let mut head = BoxedResponseHead::new(); - head.status = status; - Response { - head, - body: ResponseBody::Body(body), - error: None, - } - } - /// The source `error` for this response #[inline] pub fn error(&self) -> Option<&Error> { @@ -325,13 +320,11 @@ pub struct ResponseBuilder { } impl ResponseBuilder { + #[inline] /// Create response builder pub fn new(status: StatusCode) -> Self { - let mut head = BoxedResponseHead::new(); - head.status = status; - ResponseBuilder { - head: Some(head), + head: Some(BoxedResponseHead::new(status)), err: None, cookies: None, } @@ -555,15 +548,13 @@ impl ResponseBuilder { /// } /// ``` pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self { - { - if self.cookies.is_none() { - self.cookies = Some(CookieJar::new()) - } - let jar = self.cookies.as_mut().unwrap(); - let cookie = cookie.clone().into_owned(); - jar.add_original(cookie.clone()); - jar.remove(cookie); + if self.cookies.is_none() { + self.cookies = Some(CookieJar::new()) } + let jar = self.cookies.as_mut().unwrap(); + let cookie = cookie.clone().into_owned(); + jar.add_original(cookie.clone()); + jar.remove(cookie); self } @@ -605,6 +596,7 @@ impl ResponseBuilder { head.extensions.borrow_mut() } + #[inline] /// Set a body and generate `Response`. /// /// `ResponseBuilder` can not be used after this call. @@ -625,9 +617,7 @@ impl ResponseBuilder { if let Some(ref jar) = self.cookies { for cookie in jar.delta() { match HeaderValue::from_str(&cookie.to_string()) { - Ok(val) => { - let _ = response.headers.append(header::SET_COOKIE, val); - } + Ok(val) => response.headers.append(header::SET_COOKIE, val), Err(e) => return Response::from(Error::from(e)).into_body(), }; } @@ -652,6 +642,7 @@ impl ResponseBuilder { self.body(Body::from_message(BodyStream::new(stream))) } + #[inline] /// Set a json body and generate `Response` /// /// `ResponseBuilder` can not be used after this call. @@ -751,11 +742,12 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder { } } - let mut msg = BoxedResponseHead::new(); + let mut msg = BoxedResponseHead::new(head.status); msg.version = head.version; - msg.status = head.status; msg.reason = head.reason; - // msg.headers = head.headers.clone(); + for (k, v) in &head.headers { + msg.headers.append(k.clone(), v.clone()); + } msg.no_chunking(!head.chunked()); ResponseBuilder { diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs index 302d75d73..2c5dc502b 100644 --- a/actix-http/src/test.rs +++ b/actix-http/src/test.rs @@ -178,6 +178,6 @@ impl TestRequest { } #[inline] -fn parts<'a>(parts: &'a mut Option) -> &'a mut Inner { +fn parts(parts: &mut Option) -> &mut Inner { parts.as_mut().expect("cannot reuse test request builder") } diff --git a/awc/src/test.rs b/awc/src/test.rs index 1c772905e..fbbadef3a 100644 --- a/awc/src/test.rs +++ b/awc/src/test.rs @@ -3,7 +3,7 @@ use std::fmt::Write as FmtWrite; use actix_http::cookie::{Cookie, CookieJar}; use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue}; -use actix_http::http::{HeaderName, HttpTryFrom, Version}; +use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version}; use actix_http::{h1, Payload, ResponseHead}; use bytes::Bytes; #[cfg(test)] @@ -49,7 +49,7 @@ pub struct TestResponse { impl Default for TestResponse { fn default() -> TestResponse { TestResponse { - head: ResponseHead::default(), + head: ResponseHead::new(StatusCode::OK), cookies: CookieJar::new(), payload: None, } diff --git a/src/app_service.rs b/src/app_service.rs index 236eed9f9..593fbe673 100644 --- a/src/app_service.rs +++ b/src/app_service.rs @@ -14,6 +14,7 @@ use crate::config::{AppConfig, ServiceConfig}; use crate::data::{DataFactory, DataFactoryResult}; use crate::error::Error; use crate::guard::Guard; +use crate::request::{HttpRequest, HttpRequestPool}; use crate::rmap::ResourceMap; use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse}; @@ -21,7 +22,10 @@ type Guards = Vec>; type HttpService

= BoxedService, ServiceResponse, Error>; type HttpNewService

= BoxedNewService<(), ServiceRequest

, ServiceResponse, Error, ()>; -type BoxedResponse = Box>; +type BoxedResponse = Either< + FutureResult, + Box>, +>; /// Service factory to convert `Request` to a `ServiceRequest`. /// It also executes data factories. @@ -191,6 +195,7 @@ where chain: self.chain.take().unwrap(), rmap: self.rmap.clone(), config: self.config.clone(), + pool: HttpRequestPool::create(), } .and_then(self.endpoint.take().unwrap()), )) @@ -208,6 +213,7 @@ where chain: C, rmap: Rc, config: AppConfig, + pool: &'static HttpRequestPool, } impl Service for AppInitService @@ -224,13 +230,24 @@ where } fn call(&mut self, req: Request) -> Self::Future { - let req = ServiceRequest::new( - Path::new(Url::new(req.uri().clone())), - req, - self.rmap.clone(), - self.config.clone(), - ); - self.chain.call(req) + let (head, payload) = req.into_parts(); + + let req = if let Some(mut req) = self.pool.get_request() { + let inner = Rc::get_mut(&mut req.0).unwrap(); + inner.path.get_mut().update(&head.uri); + inner.path.reset(); + inner.head = head; + req + } else { + HttpRequest::new( + Path::new(Url::new(head.uri.clone())), + head, + self.rmap.clone(), + self.config.clone(), + self.pool, + ) + }; + self.chain.call(ServiceRequest::from_parts(req, payload)) } } @@ -353,7 +370,7 @@ impl

Service for AppRouting

{ type Request = ServiceRequest

; type Response = ServiceResponse; type Error = Error; - type Future = Either>; + type Future = BoxedResponse; fn poll_ready(&mut self) -> Poll<(), Self::Error> { if self.ready.is_none() { @@ -376,12 +393,12 @@ impl

Service for AppRouting

{ }); if let Some((srv, _info)) = res { - Either::A(srv.call(req)) + srv.call(req) } else if let Some(ref mut default) = self.default { - Either::A(default.call(req)) + default.call(req) } else { let req = req.into_parts().0; - Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish()))) + Either::A(ok(ServiceResponse::new(req, Response::NotFound().finish()))) } } } diff --git a/src/handler.rs b/src/handler.rs index 921b8334d..42a9d88d7 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -52,37 +52,21 @@ where } } } -impl NewService for Handler + +impl Clone for Handler where F: Factory, R: Responder, { - type Request = (T, HttpRequest); - type Response = ServiceResponse; - type Error = Void; - type InitError = (); - type Service = HandlerService; - type Future = FutureResult; - - fn new_service(&self, _: &()) -> Self::Future { - ok(HandlerService { + fn clone(&self) -> Self { + Self { hnd: self.hnd.clone(), _t: PhantomData, - }) + } } } -#[doc(hidden)] -pub struct HandlerService -where - F: Factory, - R: Responder, -{ - hnd: F, - _t: PhantomData<(T, R)>, -} - -impl Service for HandlerService +impl Service for Handler where F: Factory, R: Responder, @@ -184,41 +168,23 @@ where } } } -impl NewService for AsyncHandler + +impl Clone for AsyncHandler where F: AsyncFactory, R: IntoFuture, R::Item: Into, R::Error: Into, { - type Request = (T, HttpRequest); - type Response = ServiceResponse; - type Error = Error; - type InitError = (); - type Service = AsyncHandlerService; - type Future = FutureResult; - - fn new_service(&self, _: &()) -> Self::Future { - ok(AsyncHandlerService { + fn clone(&self) -> Self { + AsyncHandler { hnd: self.hnd.clone(), _t: PhantomData, - }) + } } } -#[doc(hidden)] -pub struct AsyncHandlerService -where - F: AsyncFactory, - R: IntoFuture, - R::Item: Into, - R::Error: Into, -{ - hnd: F, - _t: PhantomData<(T, R)>, -} - -impl Service for AsyncHandlerService +impl Service for AsyncHandler where F: AsyncFactory, R: IntoFuture, @@ -227,7 +193,7 @@ where { type Request = (T, HttpRequest); type Response = ServiceResponse; - type Error = Error; + type Error = Void; type Future = AsyncHandlerServiceResponse; fn poll_ready(&mut self) -> Poll<(), Self::Error> { @@ -255,7 +221,7 @@ where T::Error: Into, { type Item = ServiceResponse; - type Error = Error; + type Error = Void; fn poll(&mut self) -> Poll { match self.fut.poll() { @@ -276,46 +242,58 @@ where } /// Extract arguments from request -pub struct Extract> { +pub struct Extract, S> { config: Rc>>>, + service: S, _t: PhantomData<(P, T)>, } -impl> Extract { - pub fn new(config: Rc>>>) -> Self { +impl, S> Extract { + pub fn new(config: Rc>>>, service: S) -> Self { Extract { config, + service, _t: PhantomData, } } } -impl> NewService for Extract { +impl, S> NewService for Extract +where + S: Service + + Clone, +{ type Request = ServiceRequest

; - type Response = (T, HttpRequest); + type Response = ServiceResponse; type Error = (Error, ServiceRequest

); type InitError = (); - type Service = ExtractService; + type Service = ExtractService; type Future = FutureResult; fn new_service(&self, _: &()) -> Self::Future { ok(ExtractService { _t: PhantomData, config: self.config.borrow().clone(), + service: self.service.clone(), }) } } -pub struct ExtractService> { +pub struct ExtractService, S> { config: Option>, + service: S, _t: PhantomData<(P, T)>, } -impl> Service for ExtractService { +impl, S> Service for ExtractService +where + S: Service + + Clone, +{ type Request = ServiceRequest

; - type Response = (T, HttpRequest); + type Response = ServiceResponse; type Error = (Error, ServiceRequest

); - type Future = ExtractResponse; + type Future = ExtractResponse; fn poll_ready(&mut self) -> Poll<(), Self::Error> { Ok(Async::Ready(())) @@ -328,28 +306,40 @@ impl> Service for ExtractService { ExtractResponse { fut, + fut_s: None, req: Some((req, payload)), + service: self.service.clone(), } } } -pub struct ExtractResponse> { +pub struct ExtractResponse, S: Service> { req: Option<(HttpRequest, Payload

)>, + service: S, fut: ::Future, + fut_s: Option, } -impl> Future for ExtractResponse { - type Item = (T, HttpRequest); +impl, S> Future for ExtractResponse +where + S: Service, +{ + type Item = ServiceResponse; type Error = (Error, ServiceRequest

); fn poll(&mut self) -> Poll { + if let Some(ref mut fut) = self.fut_s { + return fut.poll().map_err(|_| panic!()); + } + let item = try_ready!(self.fut.poll().map_err(|e| { let (req, payload) = self.req.take().unwrap(); let req = ServiceRequest::from_parts(req, payload); (e.into(), req) })); - Ok(Async::Ready((item, self.req.take().unwrap().0))) + self.fut_s = Some(self.service.call((item, self.req.take().unwrap().0))); + self.poll() } } diff --git a/src/request.rs b/src/request.rs index ff38c879c..53d848f0d 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,4 +1,4 @@ -use std::cell::{Ref, RefMut}; +use std::cell::{Ref, RefCell, RefMut}; use std::fmt; use std::rc::Rc; @@ -15,29 +15,34 @@ use crate::rmap::ResourceMap; #[derive(Clone)] /// An HTTP Request -pub struct HttpRequest { +pub struct HttpRequest(pub(crate) Rc); + +pub(crate) struct HttpRequestInner { pub(crate) head: Message, pub(crate) path: Path, rmap: Rc, config: AppConfig, route_data: Option>, + pool: &'static HttpRequestPool, } impl HttpRequest { #[inline] pub(crate) fn new( - head: Message, path: Path, + head: Message, rmap: Rc, config: AppConfig, + pool: &'static HttpRequestPool, ) -> HttpRequest { - HttpRequest { + HttpRequest(Rc::new(HttpRequestInner { head, path, rmap, config, + pool, route_data: None, - } + })) } } @@ -45,7 +50,14 @@ impl HttpRequest { /// This method returns reference to the request head #[inline] pub fn head(&self) -> &RequestHead { - &self.head + &self.0.head + } + + /// This method returns muttable reference to the request head. + /// panics if multiple references of http request exists. + #[inline] + pub(crate) fn head_mut(&mut self) -> &mut RequestHead { + &mut Rc::get_mut(&mut self.0).unwrap().head } /// Request's uri. @@ -98,7 +110,12 @@ impl HttpRequest { /// access the matched value for that segment. #[inline] pub fn match_info(&self) -> &Path { - &self.path + &self.0.path + } + + #[inline] + pub(crate) fn match_info_mut(&mut self) -> &mut Path { + &mut Rc::get_mut(&mut self.0).unwrap().path } /// Request extensions @@ -141,7 +158,7 @@ impl HttpRequest { U: IntoIterator, I: AsRef, { - self.rmap.url_for(&self, name, elements) + self.0.rmap.url_for(&self, name, elements) } /// Generate url for named resource @@ -162,13 +179,13 @@ impl HttpRequest { /// App config #[inline] pub fn app_config(&self) -> &AppConfig { - &self.config + &self.0.config } /// Get an application data stored with `App::data()` method during /// application configuration. pub fn app_data(&self) -> Option> { - if let Some(st) = self.config.extensions().get::>() { + if let Some(st) = self.0.config.extensions().get::>() { Some(st.clone()) } else { None @@ -178,7 +195,7 @@ impl HttpRequest { /// Load route data. Route data could be set during /// route configuration with `Route::data()` method. pub fn route_data(&self) -> Option<&RouteData> { - if let Some(ref ext) = self.route_data { + if let Some(ref ext) = self.0.route_data { ext.get::>() } else { None @@ -186,7 +203,7 @@ impl HttpRequest { } pub(crate) fn set_route_data(&mut self, data: Option>) { - self.route_data = data; + Rc::get_mut(&mut self.0).unwrap().route_data = data; } } @@ -202,13 +219,13 @@ impl HttpMessage for HttpRequest { /// Request extensions #[inline] fn extensions(&self) -> Ref { - self.head.extensions() + self.0.head.extensions() } /// Mutable reference to a the request's extensions #[inline] fn extensions_mut(&self) -> RefMut { - self.head.extensions_mut() + self.0.head.extensions_mut() } #[inline] @@ -217,6 +234,17 @@ impl HttpMessage for HttpRequest { } } +impl Drop for HttpRequest { + fn drop(&mut self) { + if Rc::strong_count(&self.0) == 1 { + let v = &mut self.0.pool.0.borrow_mut(); + if v.len() < 128 { + v.push(self.0.clone()); + } + } + } +} + /// It is possible to get `HttpRequest` as an extractor handler parameter /// /// ## Example @@ -252,8 +280,8 @@ impl fmt::Debug for HttpRequest { writeln!( f, "\nHttpRequest {:?} {}:{}", - self.head.version, - self.head.method, + self.0.head.version, + self.0.head.method, self.path() )?; if !self.query_string().is_empty() { @@ -270,6 +298,26 @@ impl fmt::Debug for HttpRequest { } } +/// Request's objects pool +pub(crate) struct HttpRequestPool(RefCell>>); + +impl HttpRequestPool { + pub(crate) fn create() -> &'static HttpRequestPool { + let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128))); + Box::leak(Box::new(pool)) + } + + /// Get message from the pool + #[inline] + pub(crate) fn get_request(&self) -> Option { + if let Some(inner) = self.0.borrow_mut().pop() { + Some(HttpRequest(inner)) + } else { + None + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/resource.rs b/src/resource.rs index 957795cd7..313a3bc06 100644 --- a/src/resource.rs +++ b/src/resource.rs @@ -487,11 +487,8 @@ impl

Service for ResourceService

{ type Response = ServiceResponse; type Error = Error; type Future = Either< + FutureResult, Box>, - Either< - Box>, - FutureResult, - >, >; fn poll_ready(&mut self) -> Poll<(), Self::Error> { @@ -501,17 +498,17 @@ impl

Service for ResourceService

{ fn call(&mut self, mut req: ServiceRequest

) -> Self::Future { for route in self.routes.iter_mut() { if route.check(&mut req) { - return Either::A(route.call(req)); + return route.call(req); } } if let Some(ref mut default) = self.default { - Either::B(Either::A(default.call(req))) + default.call(req) } else { let req = req.into_parts().0; - Either::B(Either::B(ok(ServiceResponse::new( + Either::A(ok(ServiceResponse::new( req, Response::MethodNotAllowed().finish(), - )))) + ))) } } } diff --git a/src/route.rs b/src/route.rs index 349668ef4..8bff863fe 100644 --- a/src/route.rs +++ b/src/route.rs @@ -4,6 +4,7 @@ use std::rc::Rc; use actix_http::{http::Method, Error, Extensions, Response}; use actix_service::{NewService, Service}; +use futures::future::{ok, Either, FutureResult}; use futures::{Async, Future, IntoFuture, Poll}; use crate::data::RouteData; @@ -19,7 +20,10 @@ type BoxedRouteService = Box< Request = Req, Response = Res, Error = Error, - Future = Box>, + Future = Either< + FutureResult, + Box>, + >, >, >; @@ -50,11 +54,10 @@ impl Route

{ pub fn new() -> Route

{ let data_ref = Rc::new(RefCell::new(None)); Route { - service: Box::new(RouteNewService::new( - Extract::new(data_ref.clone()).and_then( - Handler::new(HttpResponse::NotFound).map_err(|_| panic!()), - ), - )), + service: Box::new(RouteNewService::new(Extract::new( + data_ref.clone(), + Handler::new(|| HttpResponse::NotFound()), + ))), guards: Rc::new(Vec::new()), data: None, data_ref, @@ -131,7 +134,10 @@ impl

Service for RouteService

{ type Request = ServiceRequest

; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = Either< + FutureResult, + Box>, + >; fn poll_ready(&mut self) -> Poll<(), Self::Error> { self.service.poll_ready() @@ -235,10 +241,10 @@ impl Route

{ T: FromRequest

+ 'static, R: Responder + 'static, { - self.service = Box::new(RouteNewService::new( - Extract::new(self.data_ref.clone()) - .and_then(Handler::new(handler).map_err(|_| panic!())), - )); + self.service = Box::new(RouteNewService::new(Extract::new( + self.data_ref.clone(), + Handler::new(handler), + ))); self } @@ -277,10 +283,10 @@ impl Route

{ R::Item: Into, R::Error: Into, { - self.service = Box::new(RouteNewService::new( - Extract::new(self.data_ref.clone()) - .and_then(AsyncHandler::new(handler).map_err(|_| panic!())), - )); + self.service = Box::new(RouteNewService::new(Extract::new( + self.data_ref.clone(), + AsyncHandler::new(handler), + ))); self } @@ -394,17 +400,25 @@ where type Request = ServiceRequest

; type Response = ServiceResponse; type Error = Error; - type Future = Box>; + type Future = Either< + FutureResult, + Box>, + >; fn poll_ready(&mut self) -> Poll<(), Self::Error> { self.service.poll_ready().map_err(|(e, _)| e) } fn call(&mut self, req: ServiceRequest

) -> Self::Future { - Box::new(self.service.call(req).then(|res| match res { - Ok(res) => Ok(res), - Err((err, req)) => Ok(req.error_response(err)), - })) + let mut fut = self.service.call(req); + match fut.poll() { + Ok(Async::Ready(res)) => Either::A(ok(res)), + Err((e, req)) => Either::A(ok(req.error_response(e))), + Ok(Async::NotReady) => Either::B(Box::new(fut.then(|res| match res { + Ok(res) => Ok(res), + Err((err, req)) => Ok(req.error_response(err)), + }))), + } } } diff --git a/src/scope.rs b/src/scope.rs index 7ad2d95eb..2cb01961a 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -24,7 +24,10 @@ type Guards = Vec>; type HttpService

= BoxedService, ServiceResponse, Error>; type HttpNewService

= BoxedNewService<(), ServiceRequest

, ServiceResponse, Error, ()>; -type BoxedResponse = Box>; +type BoxedResponse = Either< + FutureResult, + Box>, +>; /// Resources scope. /// diff --git a/src/service.rs b/src/service.rs index 13eea9d14..f0ff02158 100644 --- a/src/service.rs +++ b/src/service.rs @@ -1,13 +1,12 @@ use std::cell::{Ref, RefMut}; use std::fmt; use std::marker::PhantomData; -use std::rc::Rc; use actix_http::body::{Body, MessageBody, ResponseBody}; use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version}; use actix_http::{ - Error, Extensions, HttpMessage, Payload, PayloadStream, Request, RequestHead, - Response, ResponseHead, + Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response, + ResponseHead, }; use actix_router::{Path, Resource, Url}; use futures::future::{ok, FutureResult, IntoFuture}; @@ -15,7 +14,6 @@ use futures::future::{ok, FutureResult, IntoFuture}; use crate::config::{AppConfig, ServiceConfig}; use crate::data::Data; use crate::request::HttpRequest; -use crate::rmap::ResourceMap; pub trait HttpServiceFactory

{ fn register(self, config: &mut ServiceConfig

); @@ -56,19 +54,6 @@ pub struct ServiceRequest

{ } impl

ServiceRequest

{ - pub(crate) fn new( - path: Path, - request: Request

, - rmap: Rc, - config: AppConfig, - ) -> Self { - let (head, payload) = request.into_parts(); - ServiceRequest { - payload, - req: HttpRequest::new(head, path, rmap, config), - } - } - /// Construct service request from parts pub fn from_parts(req: HttpRequest, payload: Payload

) -> Self { ServiceRequest { req, payload } @@ -95,13 +80,13 @@ impl

ServiceRequest

{ /// This method returns reference to the request head #[inline] pub fn head(&self) -> &RequestHead { - &self.req.head + &self.req.head() } /// This method returns reference to the request head #[inline] pub fn head_mut(&mut self) -> &mut RequestHead { - &mut self.req.head + self.req.head_mut() } /// Request's uri. @@ -160,12 +145,12 @@ impl

ServiceRequest

{ /// access the matched value for that segment. #[inline] pub fn match_info(&self) -> &Path { - &self.req.path + self.req.match_info() } #[inline] pub fn match_info_mut(&mut self) -> &mut Path { - &mut self.req.path + self.req.match_info_mut() } /// Service configuration @@ -203,13 +188,13 @@ impl

HttpMessage for ServiceRequest

{ /// Request extensions #[inline] fn extensions(&self) -> Ref { - self.req.head.extensions() + self.req.extensions() } /// Mutable reference to a the request's extensions #[inline] fn extensions_mut(&self) -> RefMut { - self.req.head.extensions_mut() + self.req.extensions_mut() } #[inline] diff --git a/src/test.rs b/src/test.rs index 58cb1211e..5444726e1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -17,6 +17,7 @@ use futures::future::{lazy, Future}; use crate::config::{AppConfig, AppConfigInner}; use crate::data::RouteData; use crate::dev::{Body, Payload}; +use crate::request::HttpRequestPool; use crate::rmap::ResourceMap; use crate::service::{ServiceRequest, ServiceResponse}; use crate::{Error, HttpRequest, HttpResponse}; @@ -326,14 +327,17 @@ impl TestRequest { /// Complete request creation and generate `ServiceRequest` instance pub fn to_srv_request(mut self) -> ServiceRequest { - let req = self.req.finish(); + let (head, payload) = self.req.finish().into_parts(); - ServiceRequest::new( - Path::new(Url::new(req.uri().clone())), - req, + let req = HttpRequest::new( + Path::new(Url::new(head.uri.clone())), + head, Rc::new(self.rmap), AppConfig::new(self.config), - ) + HttpRequestPool::create(), + ); + + ServiceRequest::from_parts(req, payload) } /// Complete request creation and generate `ServiceResponse` instance @@ -343,34 +347,32 @@ impl TestRequest { /// Complete request creation and generate `HttpRequest` instance pub fn to_http_request(mut self) -> HttpRequest { - let req = self.req.finish(); + let (head, _) = self.req.finish().into_parts(); - let mut req = ServiceRequest::new( - Path::new(Url::new(req.uri().clone())), - req, + let mut req = HttpRequest::new( + Path::new(Url::new(head.uri.clone())), + head, Rc::new(self.rmap), AppConfig::new(self.config), - ) - .into_parts() - .0; + HttpRequestPool::create(), + ); req.set_route_data(Some(Rc::new(self.route_data))); req } /// Complete request creation and generate `HttpRequest` and `Payload` instances pub fn to_http_parts(mut self) -> (HttpRequest, Payload) { - let req = self.req.finish(); + let (head, payload) = self.req.finish().into_parts(); - let (mut req, pl) = ServiceRequest::new( - Path::new(Url::new(req.uri().clone())), - req, + let mut req = HttpRequest::new( + Path::new(Url::new(head.uri.clone())), + head, Rc::new(self.rmap), AppConfig::new(self.config), - ) - .into_parts(); - + HttpRequestPool::create(), + ); req.set_route_data(Some(Rc::new(self.route_data))); - (req, pl) + (req, payload) } /// Runs the provided future, blocking the current thread until the future