1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +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 {
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 {
let mut resp = HttpResponse::new(self.0, Body::Empty);
resp.set_reason(reason);

View File

@ -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<S> HttpRequest<S> {
.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 }

View File

@ -37,12 +37,12 @@ pub enum ConnectionType {
}
/// 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 {
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<Box<InnerHttpResponse>>,
pool: Option<Rc<UnsafeCell<Pool>>>,
pool: Option<Rc<UnsafeCell<HttpResponsePool>>>,
err: Option<HttpError>,
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)]
struct InnerHttpResponse {
version: Option<Version>,
@ -774,45 +777,67 @@ impl InnerHttpResponse {
}
/// Internal use only! unsafe
struct Pool(VecDeque<Box<InnerHttpResponse>>);
pub(crate) struct HttpResponsePool(VecDeque<Box<InnerHttpResponse>>);
thread_local!(static POOL: Rc<UnsafeCell<Pool>> =
Rc::new(UnsafeCell::new(Pool(VecDeque::with_capacity(128)))));
thread_local!(static POOL: Rc<UnsafeCell<HttpResponsePool>> = HttpResponsePool::pool());
impl Pool {
impl HttpResponsePool {
#[inline]
fn get(status: StatusCode) -> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) {
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<UnsafeCell<HttpResponsePool>> {
Rc::new(UnsafeCell::new(HttpResponsePool(VecDeque::with_capacity(128))))
}
#[inline]
fn with_body(status: StatusCode, body: Body)
-> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) {
POOL.with(|pool| {
pub fn get_builder(pool: &Rc<UnsafeCell<HttpResponsePool>>, status: StatusCode)
-> HttpResponseBuilder
{
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))
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 {
(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)]
#[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()};
if pool.0.len() < 128 {
inner.headers.clear();

View File

@ -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<net::SocketAddr>,
secure: bool,
host: String,
cpu_pool: Arc<InnerCpuPool>,
responses: Rc<UnsafeCell<HttpResponsePool>>,
}
unsafe impl Sync for ServerSettings {}
unsafe impl Send for ServerSettings {}
struct InnerCpuPool {
cpu_pool: UnsafeCell<Option<CpuPool>>,
}
@ -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;