1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-12-18 01:43:58 +01:00

add helper method for response creation

This commit is contained in:
Nikolay Kim 2018-03-22 21:14:57 -07:00
parent 449709dd7e
commit 47f836cd1b
4 changed files with 112 additions and 44 deletions

View File

@ -186,6 +186,9 @@ impl StaticResponse {
pub fn build(&self) -> HttpResponseBuilder { pub fn build(&self) -> HttpResponseBuilder {
HttpResponse::build(self.0) HttpResponse::build(self.0)
} }
pub fn build_from<S>(&self, req: &HttpRequest<S>) -> HttpResponseBuilder {
req.build_response(self.0)
}
pub fn with_reason(self, reason: &'static str) -> HttpResponse { pub fn with_reason(self, reason: &'static str) -> HttpResponse {
let mut resp = HttpResponse::new(self.0, Body::Empty); let mut resp = HttpResponse::new(self.0, Body::Empty);
resp.set_reason(reason); resp.set_reason(reason);

View File

@ -8,14 +8,16 @@ use futures::{Async, Stream, Poll};
use futures_cpupool::CpuPool; use futures_cpupool::CpuPool;
use failure; use failure;
use url::{Url, form_urlencoded}; 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 tokio_io::AsyncRead;
use body::Body;
use info::ConnectionInfo; use info::ConnectionInfo;
use param::Params; use param::Params;
use router::Router; use router::Router;
use payload::Payload; use payload::Payload;
use httpmessage::HttpMessage; use httpmessage::HttpMessage;
use httpresponse::{HttpResponse, HttpResponseBuilder};
use helpers::SharedHttpInnerMessage; use helpers::SharedHttpInnerMessage;
use error::{UrlGenerationError, CookieParseError, PayloadError}; use error::{UrlGenerationError, CookieParseError, PayloadError};
@ -194,6 +196,24 @@ impl<S> HttpRequest<S> {
.server_settings().cpu_pool() .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)] #[doc(hidden)]
pub fn prefix_len(&self) -> usize { pub fn prefix_len(&self) -> usize {
if let Some(router) = self.router() { router.prefix().len() } else { 0 } if let Some(router) = self.router() { router.prefix().len() } else { 0 }

View File

@ -37,12 +37,12 @@ pub enum ConnectionType {
} }
/// An HTTP Response /// An HTTP Response
pub struct HttpResponse(Option<Box<InnerHttpResponse>>, Rc<UnsafeCell<Pool>>); pub struct HttpResponse(Option<Box<InnerHttpResponse>>, Rc<UnsafeCell<HttpResponsePool>>);
impl Drop for HttpResponse { impl Drop for HttpResponse {
fn drop(&mut self) { fn drop(&mut self) {
if let Some(inner) = self.0.take() { 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. /// Create http response builder with specific status.
#[inline] #[inline]
pub fn build(status: StatusCode) -> HttpResponseBuilder { pub fn build(status: StatusCode) -> HttpResponseBuilder {
let (msg, pool) = Pool::get(status); HttpResponsePool::get(status)
HttpResponseBuilder {
response: Some(msg),
pool: Some(pool),
err: None,
cookies: None,
}
} }
/// Create http response builder /// Create http response builder
@ -82,8 +76,7 @@ impl HttpResponse {
/// Constructs a response /// Constructs a response
#[inline] #[inline]
pub fn new(status: StatusCode, body: Body) -> HttpResponse { pub fn new(status: StatusCode, body: Body) -> HttpResponse {
let (msg, pool) = Pool::with_body(status, body); HttpResponsePool::with_body(status, body)
HttpResponse(Some(msg), pool)
} }
/// Constructs a error response /// Constructs a error response
@ -246,7 +239,7 @@ impl fmt::Debug for HttpResponse {
/// builder-like pattern. /// builder-like pattern.
pub struct HttpResponseBuilder { pub struct HttpResponseBuilder {
response: Option<Box<InnerHttpResponse>>, response: Option<Box<InnerHttpResponse>>,
pool: Option<Rc<UnsafeCell<Pool>>>, pool: Option<Rc<UnsafeCell<HttpResponsePool>>>,
err: Option<HttpError>, err: Option<HttpError>,
cookies: Option<CookieJar>, cookies: Option<CookieJar>,
} }
@ -738,6 +731,16 @@ impl<'a> From<&'a ClientResponse> for HttpResponseBuilder {
} }
} }
impl<'a, S> From<&'a HttpRequest<S>> for HttpResponseBuilder {
fn from(req: &'a HttpRequest<S>) -> HttpResponseBuilder {
if let Some(router) = req.router() {
router.server_settings().get_response_builder(StatusCode::OK)
} else {
HttpResponse::build(StatusCode::OK)
}
}
}
#[derive(Debug)] #[derive(Debug)]
struct InnerHttpResponse { struct InnerHttpResponse {
version: Option<Version>, version: Option<Version>,
@ -774,45 +777,67 @@ impl InnerHttpResponse {
} }
/// Internal use only! unsafe /// Internal use only! unsafe
struct Pool(VecDeque<Box<InnerHttpResponse>>); pub(crate) struct HttpResponsePool(VecDeque<Box<InnerHttpResponse>>);
thread_local!(static POOL: Rc<UnsafeCell<Pool>> = thread_local!(static POOL: Rc<UnsafeCell<HttpResponsePool>> = HttpResponsePool::pool());
Rc::new(UnsafeCell::new(Pool(VecDeque::with_capacity(128)))));
impl Pool { impl HttpResponsePool {
#[inline] pub fn pool() -> Rc<UnsafeCell<HttpResponsePool>> {
fn get(status: StatusCode) -> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) { Rc::new(UnsafeCell::new(HttpResponsePool(VecDeque::with_capacity(128))))
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))
}
})
} }
#[inline] #[inline]
fn with_body(status: StatusCode, body: Body) pub fn get_builder(pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode)
-> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) { -> HttpResponseBuilder
POOL.with(|pool| { {
let p = unsafe{&mut *pool.as_ref().get()}; let p = unsafe{&mut *pool.as_ref().get()};
if let Some(mut resp) = p.0.pop_front() { if let Some(mut msg) = p.0.pop_front() {
resp.status = status; msg.status = status;
resp.body = body; HttpResponseBuilder {
(resp, Rc::clone(pool)) response: Some(msg),
pool: Some(Rc::clone(pool)),
err: None,
cookies: None }
} else { } else {
(Box::new(InnerHttpResponse::new(status, body)), Rc::clone(pool)) 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<UnsafeCell<HttpResponsePool>>,
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)] #[inline(always)]
#[cfg_attr(feature = "cargo-clippy", allow(boxed_local, inline_always))] #[cfg_attr(feature = "cargo-clippy", allow(boxed_local, inline_always))]
fn release(pool: &Rc<UnsafeCell<Pool>>, mut inner: Box<InnerHttpResponse>) { fn release(pool: &Rc<UnsafeCell<HttpResponsePool>>, mut inner: Box<InnerHttpResponse>)
{
let pool = unsafe{&mut *pool.as_ref().get()}; let pool = unsafe{&mut *pool.as_ref().get()};
if pool.0.len() < 128 { if pool.0.len() < 128 {
inner.headers.clear(); inner.headers.clear();

View File

@ -5,22 +5,29 @@ use std::sync::Arc;
use std::cell::{Cell, RefCell, RefMut, UnsafeCell}; use std::cell::{Cell, RefCell, RefMut, UnsafeCell};
use time; use time;
use bytes::BytesMut; use bytes::BytesMut;
use http::StatusCode;
use futures_cpupool::{Builder, CpuPool}; use futures_cpupool::{Builder, CpuPool};
use helpers; use helpers;
use super::KeepAlive; use super::KeepAlive;
use super::channel::Node; use super::channel::Node;
use super::shared::{SharedBytes, SharedBytesPool}; use super::shared::{SharedBytes, SharedBytesPool};
use body::Body;
use httpresponse::{HttpResponse, HttpResponsePool, HttpResponseBuilder};
/// Various server settings /// Various server settings
#[derive(Debug, Clone)] #[derive(Clone)]
pub struct ServerSettings { pub struct ServerSettings {
addr: Option<net::SocketAddr>, addr: Option<net::SocketAddr>,
secure: bool, secure: bool,
host: String, host: String,
cpu_pool: Arc<InnerCpuPool>, cpu_pool: Arc<InnerCpuPool>,
responses: Rc<UnsafeCell<HttpResponsePool>>,
} }
unsafe impl Sync for ServerSettings {}
unsafe impl Send for ServerSettings {}
struct InnerCpuPool { struct InnerCpuPool {
cpu_pool: UnsafeCell<Option<CpuPool>>, cpu_pool: UnsafeCell<Option<CpuPool>>,
} }
@ -56,6 +63,7 @@ impl Default for ServerSettings {
addr: None, addr: None,
secure: false, secure: false,
host: "localhost:8080".to_owned(), host: "localhost:8080".to_owned(),
responses: HttpResponsePool::pool(),
cpu_pool: Arc::new(InnerCpuPool::new()), cpu_pool: Arc::new(InnerCpuPool::new()),
} }
} }
@ -74,7 +82,8 @@ impl ServerSettings {
"localhost".to_owned() "localhost".to_owned()
}; };
let cpu_pool = Arc::new(InnerCpuPool::new()); 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 /// Returns the socket address of the local half of this TCP connection
@ -96,8 +105,19 @@ impl ServerSettings {
pub fn cpu_pool(&self) -> &CpuPool { pub fn cpu_pool(&self) -> &CpuPool {
self.cpu_pool.cpu_pool() 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() // "Sun, 06 Nov 1994 08:49:37 GMT".len()
const DATE_VALUE_LENGTH: usize = 29; const DATE_VALUE_LENGTH: usize = 29;