diff --git a/src/httpcodes.rs b/src/httpcodes.rs index d3b803dc..a725706d 100644 --- a/src/httpcodes.rs +++ b/src/httpcodes.rs @@ -186,6 +186,9 @@ impl StaticResponse { pub fn build(&self) -> HttpResponseBuilder { HttpResponse::build(self.0) } + pub fn build_from(&self, req: &HttpRequest) -> HttpResponseBuilder { + req.build_response(self.0) + } pub fn with_reason(self, reason: &'static str) -> HttpResponse { let mut resp = HttpResponse::new(self.0, Body::Empty); resp.set_reason(reason); diff --git a/src/httprequest.rs b/src/httprequest.rs index c141590b..214861ed 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -8,14 +8,16 @@ use futures::{Async, Stream, Poll}; use futures_cpupool::CpuPool; use failure; use url::{Url, form_urlencoded}; -use http::{header, Uri, Method, Version, HeaderMap, Extensions}; +use http::{header, Uri, Method, Version, HeaderMap, Extensions, StatusCode}; use tokio_io::AsyncRead; +use body::Body; use info::ConnectionInfo; use param::Params; use router::Router; use payload::Payload; use httpmessage::HttpMessage; +use httpresponse::{HttpResponse, HttpResponseBuilder}; use helpers::SharedHttpInnerMessage; use error::{UrlGenerationError, CookieParseError, PayloadError}; @@ -194,6 +196,24 @@ impl HttpRequest { .server_settings().cpu_pool() } + /// 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::new(status, body) + } + } + + /// 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) -> usize { if let Some(router) = self.router() { router.prefix().len() } else { 0 } diff --git a/src/httpresponse.rs b/src/httpresponse.rs index eafe4207..21000de6 100644 --- a/src/httpresponse.rs +++ b/src/httpresponse.rs @@ -37,12 +37,12 @@ pub enum ConnectionType { } /// An HTTP Response -pub struct HttpResponse(Option>, Rc>); +pub struct HttpResponse(Option>, Rc>); impl Drop for HttpResponse { fn drop(&mut self) { if let Some(inner) = self.0.take() { - Pool::release(&self.1, inner) + HttpResponsePool::release(&self.1, inner) } } } @@ -64,13 +64,7 @@ impl HttpResponse { /// Create http response builder with specific status. #[inline] pub fn build(status: StatusCode) -> HttpResponseBuilder { - let (msg, pool) = Pool::get(status); - HttpResponseBuilder { - response: Some(msg), - pool: Some(pool), - err: None, - cookies: None, - } + HttpResponsePool::get(status) } /// Create http response builder @@ -82,8 +76,7 @@ impl HttpResponse { /// Constructs a response #[inline] pub fn new(status: StatusCode, body: Body) -> HttpResponse { - let (msg, pool) = Pool::with_body(status, body); - HttpResponse(Some(msg), pool) + HttpResponsePool::with_body(status, body) } /// Constructs a error response @@ -246,7 +239,7 @@ impl fmt::Debug for HttpResponse { /// builder-like pattern. pub struct HttpResponseBuilder { response: Option>, - pool: Option>>, + pool: Option>>, err: Option, cookies: Option, } @@ -738,6 +731,16 @@ 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::build(StatusCode::OK) + } + } +} + #[derive(Debug)] struct InnerHttpResponse { version: Option, @@ -774,45 +777,67 @@ impl InnerHttpResponse { } /// Internal use only! unsafe -struct Pool(VecDeque>); +pub(crate) struct HttpResponsePool(VecDeque>); -thread_local!(static POOL: Rc> = - Rc::new(UnsafeCell::new(Pool(VecDeque::with_capacity(128))))); +thread_local!(static POOL: Rc> = HttpResponsePool::pool()); -impl Pool { +impl HttpResponsePool { - #[inline] - fn get(status: StatusCode) -> (Box, Rc>) { - POOL.with(|pool| { - let p = unsafe{&mut *pool.as_ref().get()}; - if let Some(mut resp) = p.0.pop_front() { - resp.body = Body::Empty; - resp.status = status; - (resp, Rc::clone(pool)) - } else { - (Box::new(InnerHttpResponse::new(status, Body::Empty)), Rc::clone(pool)) - } - }) + pub fn pool() -> Rc> { + Rc::new(UnsafeCell::new(HttpResponsePool(VecDeque::with_capacity(128)))) } #[inline] - fn with_body(status: StatusCode, body: Body) - -> (Box, Rc>) { - POOL.with(|pool| { - let p = unsafe{&mut *pool.as_ref().get()}; - if let Some(mut resp) = p.0.pop_front() { - resp.status = status; - resp.body = body; - (resp, Rc::clone(pool)) - } else { - (Box::new(InnerHttpResponse::new(status, body)), Rc::clone(pool)) - } - }) + pub fn get_builder(pool: &Rc>, status: StatusCode) + -> HttpResponseBuilder + { + let p = unsafe{&mut *pool.as_ref().get()}; + if let Some(mut msg) = p.0.pop_front() { + msg.status = status; + HttpResponseBuilder { + response: Some(msg), + pool: Some(Rc::clone(pool)), + err: None, + cookies: None } + } else { + let msg = Box::new(InnerHttpResponse::new(status, Body::Empty)); + HttpResponseBuilder { + response: Some(msg), + pool: Some(Rc::clone(pool)), + err: None, + cookies: None } + } + } + + #[inline] + pub fn get_response(pool: &Rc>, + status: StatusCode, body: Body) -> HttpResponse + { + let p = unsafe{&mut *pool.as_ref().get()}; + if let Some(mut msg) = p.0.pop_front() { + msg.status = status; + msg.body = body; + HttpResponse(Some(msg), Rc::clone(pool)) + } else { + let msg = Box::new(InnerHttpResponse::new(status, body)); + HttpResponse(Some(msg), Rc::clone(pool)) + } + } + + #[inline] + fn get(status: StatusCode) -> HttpResponseBuilder { + POOL.with(|pool| HttpResponsePool::get_builder(pool, status)) + } + + #[inline] + fn with_body(status: StatusCode, body: Body) -> HttpResponse { + POOL.with(|pool| HttpResponsePool::get_response(pool, status, body)) } #[inline(always)] #[cfg_attr(feature = "cargo-clippy", allow(boxed_local, inline_always))] - fn release(pool: &Rc>, mut inner: Box) { + fn release(pool: &Rc>, mut inner: Box) + { let pool = unsafe{&mut *pool.as_ref().get()}; if pool.0.len() < 128 { inner.headers.clear(); diff --git a/src/server/settings.rs b/src/server/settings.rs index 4255755f..8b37edb9 100644 --- a/src/server/settings.rs +++ b/src/server/settings.rs @@ -5,22 +5,29 @@ use std::sync::Arc; use std::cell::{Cell, RefCell, RefMut, UnsafeCell}; use time; use bytes::BytesMut; +use http::StatusCode; use futures_cpupool::{Builder, CpuPool}; use helpers; use super::KeepAlive; use super::channel::Node; use super::shared::{SharedBytes, SharedBytesPool}; +use body::Body; +use httpresponse::{HttpResponse, HttpResponsePool, HttpResponseBuilder}; /// Various server settings -#[derive(Debug, Clone)] +#[derive(Clone)] pub struct ServerSettings { addr: Option, secure: bool, host: String, cpu_pool: Arc, + responses: Rc>, } +unsafe impl Sync for ServerSettings {} +unsafe impl Send for ServerSettings {} + struct InnerCpuPool { cpu_pool: UnsafeCell>, } @@ -56,6 +63,7 @@ impl Default for ServerSettings { addr: None, secure: false, host: "localhost:8080".to_owned(), + responses: HttpResponsePool::pool(), cpu_pool: Arc::new(InnerCpuPool::new()), } } @@ -74,7 +82,8 @@ impl ServerSettings { "localhost".to_owned() }; let cpu_pool = Arc::new(InnerCpuPool::new()); - ServerSettings { addr, secure, host, cpu_pool } + let responses = HttpResponsePool::pool(); + ServerSettings { addr, secure, host, cpu_pool, responses } } /// Returns the socket address of the local half of this TCP connection @@ -96,8 +105,19 @@ impl ServerSettings { pub fn cpu_pool(&self) -> &CpuPool { self.cpu_pool.cpu_pool() } + + #[inline] + pub(crate) fn get_response(&self, status: StatusCode, body: Body) -> HttpResponse { + HttpResponsePool::get_response(&self.responses, status, body) + } + + #[inline] + pub(crate) fn get_response_builder(&self, status: StatusCode) -> HttpResponseBuilder { + HttpResponsePool::get_builder(&self.responses, status) + } } + // "Sun, 06 Nov 1994 08:49:37 GMT".len() const DATE_VALUE_LENGTH: usize = 29;