1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 08:22:59 +01:00

simplify httpresponse release

This commit is contained in:
Nikolay Kim 2018-03-20 15:51:19 -07:00
parent ee7d58dd7f
commit 70caa2552b
2 changed files with 47 additions and 41 deletions

View File

@ -1,7 +1,8 @@
//! Http response //! Http response
use std::{mem, str, fmt}; use std::{mem, str, fmt};
use std::rc::Rc;
use std::io::Write; use std::io::Write;
use std::cell::RefCell; use std::cell::UnsafeCell;
use std::collections::VecDeque; use std::collections::VecDeque;
use cookie::{Cookie, CookieJar}; use cookie::{Cookie, CookieJar};
@ -34,12 +35,12 @@ pub enum ConnectionType {
} }
/// An HTTP Response /// An HTTP Response
pub struct HttpResponse(Option<Box<InnerHttpResponse>>); pub struct HttpResponse(Option<Box<InnerHttpResponse>>, Rc<UnsafeCell<Pool>>);
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(inner) Pool::release(&self.1, inner)
} }
} }
} }
@ -61,8 +62,10 @@ 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);
HttpResponseBuilder { HttpResponseBuilder {
response: Some(Pool::get(status)), response: Some(msg),
pool: Some(pool),
err: None, err: None,
cookies: None, cookies: None,
} }
@ -71,7 +74,8 @@ 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 {
HttpResponse(Some(Pool::with_body(status, body))) let (msg, pool) = Pool::with_body(status, body);
HttpResponse(Some(msg), pool)
} }
/// Constructs a error response /// Constructs a error response
@ -232,9 +236,9 @@ impl fmt::Debug for HttpResponse {
/// ///
/// This type can be used to construct an instance of `HttpResponse` through a /// This type can be used to construct an instance of `HttpResponse` through a
/// builder-like pattern. /// builder-like pattern.
#[derive(Debug)]
pub struct HttpResponseBuilder { pub struct HttpResponseBuilder {
response: Option<Box<InnerHttpResponse>>, response: Option<Box<InnerHttpResponse>>,
pool: Option<Rc<UnsafeCell<Pool>>>,
err: Option<HttpError>, err: Option<HttpError>,
cookies: Option<CookieJar>, cookies: Option<CookieJar>,
} }
@ -506,7 +510,7 @@ impl HttpResponseBuilder {
} }
} }
response.body = body.into(); response.body = body.into();
Ok(HttpResponse(Some(response))) Ok(HttpResponse(Some(response), self.pool.take().unwrap()))
} }
/// Set a streaming body and generate `HttpResponse`. /// Set a streaming body and generate `HttpResponse`.
@ -547,6 +551,7 @@ impl HttpResponseBuilder {
pub fn take(&mut self) -> HttpResponseBuilder { pub fn take(&mut self) -> HttpResponseBuilder {
HttpResponseBuilder { HttpResponseBuilder {
response: self.response.take(), response: self.response.take(),
pool: self.pool.take(),
err: self.err.take(), err: self.err.take(),
cookies: self.cookies.take(), cookies: self.cookies.take(),
} }
@ -748,43 +753,45 @@ impl InnerHttpResponse {
/// Internal use only! unsafe /// Internal use only! unsafe
struct Pool(VecDeque<Box<InnerHttpResponse>>); struct Pool(VecDeque<Box<InnerHttpResponse>>);
thread_local!(static POOL: RefCell<Pool> = thread_local!(static POOL: Rc<UnsafeCell<Pool>> =
RefCell::new(Pool(VecDeque::with_capacity(128)))); Rc::new(UnsafeCell::new(Pool(VecDeque::with_capacity(128)))));
impl Pool { impl Pool {
#[inline] #[inline]
fn get(status: StatusCode) -> Box<InnerHttpResponse> { fn get(status: StatusCode) -> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) {
POOL.with(|pool| { POOL.with(|pool| {
if let Some(mut resp) = pool.borrow_mut().0.pop_front() { let p = unsafe{&mut *pool.as_ref().get()};
if let Some(mut resp) = p.0.pop_front() {
resp.body = Body::Empty; resp.body = Body::Empty;
resp.status = status; resp.status = status;
resp (resp, Rc::clone(pool))
} else { } else {
Box::new(InnerHttpResponse::new(status, Body::Empty)) (Box::new(InnerHttpResponse::new(status, Body::Empty)), Rc::clone(pool))
} }
}) })
} }
#[inline] #[inline]
fn with_body(status: StatusCode, body: Body) -> Box<InnerHttpResponse> { fn with_body(status: StatusCode, body: Body)
-> (Box<InnerHttpResponse>, Rc<UnsafeCell<Pool>>) {
POOL.with(|pool| { POOL.with(|pool| {
if let Some(mut resp) = pool.borrow_mut().0.pop_front() { let p = unsafe{&mut *pool.as_ref().get()};
if let Some(mut resp) = p.0.pop_front() {
resp.status = status; resp.status = status;
resp.body = body; resp.body = body;
resp (resp, Rc::clone(pool))
} else { } else {
Box::new(InnerHttpResponse::new(status, body)) (Box::new(InnerHttpResponse::new(status, body)), Rc::clone(pool))
} }
}) })
} }
#[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(mut inner: Box<InnerHttpResponse>) { fn release(pool: &Rc<UnsafeCell<Pool>>, mut inner: Box<InnerHttpResponse>) {
POOL.with(|pool| { let pool = unsafe{&mut *pool.as_ref().get()};
let v = &mut pool.borrow_mut().0; if pool.0.len() < 128 {
if v.len() < 128 {
inner.headers.clear(); inner.headers.clear();
inner.version = None; inner.version = None;
inner.chunked = None; inner.chunked = None;
@ -794,9 +801,8 @@ impl Pool {
inner.response_size = 0; inner.response_size = 0;
inner.error = None; inner.error = None;
inner.write_capacity = MAX_WRITE_BUFFER_SIZE; inner.write_capacity = MAX_WRITE_BUFFER_SIZE;
v.push_front(inner); pool.0.push_front(inner);
} }
})
} }
} }

View File

@ -490,6 +490,8 @@ impl Reader {
fn parse_message<H>(buf: &mut BytesMut, settings: &WorkerSettings<H>) fn parse_message<H>(buf: &mut BytesMut, settings: &WorkerSettings<H>)
-> Poll<(HttpRequest, Option<PayloadInfo>), ParseError> { -> Poll<(HttpRequest, Option<PayloadInfo>), ParseError> {
// Parse http message // Parse http message
let mut has_te = false;
let mut has_upgrade = false;
let msg = { let msg = {
let bytes_ptr = buf.as_ref().as_ptr() as usize; let bytes_ptr = buf.as_ref().as_ptr() as usize;
let mut headers: [httparse::Header; MAX_HEADERS] = let mut headers: [httparse::Header; MAX_HEADERS] =
@ -500,7 +502,7 @@ impl Reader {
let mut req = httparse::Request::new(&mut headers); let mut req = httparse::Request::new(&mut headers);
match req.parse(b)? { match req.parse(b)? {
httparse::Status::Complete(len) => { httparse::Status::Complete(len) => {
let method = Method::try_from(req.method.unwrap()) let method = Method::from_bytes(req.method.unwrap().as_bytes())
.map_err(|_| ParseError::Method)?; .map_err(|_| ParseError::Method)?;
//let path = req.path.unwrap(); //let path = req.path.unwrap();
//let path_start = path.as_ptr() as usize - bytes_ptr; //let path_start = path.as_ptr() as usize - bytes_ptr;
@ -527,7 +529,9 @@ impl Reader {
{ {
let msg_mut = msg.get_mut(); let msg_mut = msg.get_mut();
for header in headers[..headers_len].iter() { for header in headers[..headers_len].iter() {
if let Ok(name) = HeaderName::try_from(header.name) { if let Ok(name) = HeaderName::from_bytes(header.name.as_bytes()) {
has_te = has_te || name == header::TRANSFER_ENCODING;
has_upgrade = has_upgrade || name == header::UPGRADE;
let v_start = header.value.as_ptr() as usize - bytes_ptr; let v_start = header.value.as_ptr() as usize - bytes_ptr;
let v_end = v_start + header.value.len(); let v_end = v_start + header.value.len();
let value = unsafe { let value = unsafe {
@ -540,8 +544,6 @@ impl Reader {
} }
msg_mut.uri = path; msg_mut.uri = path;
//msg_mut.uri = Uri::from_shared(
//slice.slice(path.0, path.1)).map_err(ParseError::Uri)?;
msg_mut.method = method; msg_mut.method = method;
msg_mut.version = version; msg_mut.version = version;
} }
@ -563,12 +565,10 @@ impl Reader {
debug!("illegal Content-Length: {:?}", len); debug!("illegal Content-Length: {:?}", len);
return Err(ParseError::Header) return Err(ParseError::Header)
} }
} else if chunked(&msg.get_mut().headers)? { } else if has_te && chunked(&msg.get_mut().headers)? {
// Chunked encoding // Chunked encoding
Some(Decoder::chunked()) Some(Decoder::chunked())
} else if msg.get_ref().headers.contains_key(header::UPGRADE) || } else if has_upgrade || msg.get_ref().method == Method::CONNECT {
msg.get_ref().method == Method::CONNECT
{
Some(Decoder::eof()) Some(Decoder::eof())
} else { } else {
None None