From 5d791142390ff55887d7d7e8157f3b4615498427 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 4 Jul 2018 22:52:49 +0600 Subject: [PATCH] optimize Request handling --- src/httprequest.rs | 32 ++++++++----- src/server/h1.rs | 2 +- src/server/h1decoder.rs | 4 +- src/server/h2.rs | 17 ++++--- src/server/message.rs | 102 +++++++++++++++++++++++++--------------- src/server/settings.rs | 4 +- src/test.rs | 52 ++++++++++++-------- 7 files changed, 134 insertions(+), 79 deletions(-) diff --git a/src/httprequest.rs b/src/httprequest.rs index 186568550..56e95e9e6 100644 --- a/src/httprequest.rs +++ b/src/httprequest.rs @@ -28,7 +28,7 @@ struct Cookies(Vec>); /// An HTTP Request pub struct HttpRequest { - req: Rc, + req: Option, state: Rc, route: RouteInfo, } @@ -38,12 +38,12 @@ impl HttpMessage for HttpRequest { #[inline] fn headers(&self) -> &HeaderMap { - self.req.headers() + self.request().headers() } #[inline] fn payload(&self) -> Payload { - if let Some(payload) = self.req.inner.payload.borrow_mut().take() { + if let Some(payload) = self.request().inner.payload.borrow_mut().take() { payload } else { Payload::empty() @@ -55,7 +55,7 @@ impl Deref for HttpRequest { type Target = Request; fn deref(&self) -> &Request { - self.req.as_ref() + self.request() } } @@ -65,7 +65,7 @@ impl HttpRequest { HttpRequest { state, route, - req: Rc::new(req), + req: Some(req), } } @@ -98,38 +98,40 @@ impl HttpRequest { #[inline] /// Server request pub fn request(&self) -> &Request { - &self.req + self.req.as_ref().unwrap() } /// Request extensions #[inline] pub fn extensions(&self) -> Ref { - self.req.extensions() + self.request().extensions() } /// Mutable reference to a the request's extensions #[inline] pub fn extensions_mut(&self) -> RefMut { - self.req.extensions_mut() + self.request().extensions_mut() } /// Default `CpuPool` #[inline] #[doc(hidden)] pub fn cpu_pool(&self) -> &CpuPool { - self.req.server_settings().cpu_pool() + self.request().server_settings().cpu_pool() } #[inline] /// Create http response pub fn response(&self, status: StatusCode, body: Body) -> HttpResponse { - self.req.server_settings().get_response(status, body) + self.request().server_settings().get_response(status, body) } #[inline] /// Create http response builder pub fn build_response(&self, status: StatusCode) -> HttpResponseBuilder { - self.req.server_settings().get_response_builder(status) + self.request() + .server_settings() + .get_response_builder(status) } /// Read the Request Uri. @@ -314,6 +316,14 @@ impl HttpRequest { } } +impl Drop for HttpRequest { + fn drop(&mut self) { + if let Some(req) = self.req.take() { + req.release(); + } + } +} + impl Clone for HttpRequest { fn clone(&self) -> HttpRequest { HttpRequest { diff --git a/src/server/h1.rs b/src/server/h1.rs index c3d44c30e..e55596635 100644 --- a/src/server/h1.rs +++ b/src/server/h1.rs @@ -365,7 +365,7 @@ where } // set remote addr - msg.inner.addr = self.addr; + msg.inner_mut().addr = self.addr; // stop keepalive timer self.keepalive_timer.take(); diff --git a/src/server/h1decoder.rs b/src/server/h1decoder.rs index 9f14bb478..977e89a8e 100644 --- a/src/server/h1decoder.rs +++ b/src/server/h1decoder.rs @@ -118,9 +118,9 @@ impl H1Decoder { let slice = buf.split_to(len).freeze(); // convert headers - let mut msg = settings.get_request_context(); + let mut msg = settings.get_request(); { - let inner = &mut msg.inner; + let inner = msg.inner_mut(); inner .flags .get_mut() diff --git a/src/server/h2.rs b/src/server/h2.rs index 001a46b7c..d812f6f53 100644 --- a/src/server/h2.rs +++ b/src/server/h2.rs @@ -329,13 +329,16 @@ impl Entry { // Payload and Content-Encoding let (psender, payload) = Payload::new(false); - let mut msg = settings.get_request_context(); - msg.inner.url = Url::new(parts.uri); - msg.inner.method = parts.method; - msg.inner.version = parts.version; - msg.inner.headers = parts.headers; - *msg.inner.payload.borrow_mut() = Some(payload); - msg.inner.addr = addr; + let mut msg = settings.get_request(); + { + let inner = msg.inner_mut(); + inner.url = Url::new(parts.uri); + inner.method = parts.method; + inner.version = parts.version; + inner.headers = parts.headers; + *inner.payload.borrow_mut() = Some(payload); + inner.addr = addr; + } // Payload sender let psender = PayloadType::new(msg.headers(), psender); diff --git a/src/server/message.rs b/src/server/message.rs index 61f418939..eb1395ac3 100644 --- a/src/server/message.rs +++ b/src/server/message.rs @@ -1,6 +1,7 @@ use std::cell::{Cell, Ref, RefCell, RefMut}; use std::collections::VecDeque; use std::net::SocketAddr; +use std::rc::Rc; use http::{header, HeaderMap, Method, Uri, Version}; @@ -19,8 +20,9 @@ bitflags! { } /// Request's context +#[derive(Clone)] pub struct Request { - pub(crate) inner: Box, + pub(crate) inner: Rc, } pub(crate) struct InnerRequest { @@ -34,6 +36,18 @@ pub(crate) struct InnerRequest { pub(crate) info: RefCell, pub(crate) payload: RefCell>, pub(crate) settings: ServerSettings, + pool: &'static RequestPool, +} + +impl InnerRequest { + #[inline] + /// Reset request instance + pub fn reset(&mut self) { + self.headers.clear(); + self.extensions.borrow_mut().clear(); + self.flags.set(MessageFlags::empty()); + *self.payload.borrow_mut() = None; + } } impl HttpMessage for Request { @@ -55,9 +69,10 @@ impl HttpMessage for Request { impl Request { /// Create new RequestContext instance - pub fn new(settings: ServerSettings) -> Request { + pub(crate) fn new(pool: &'static RequestPool, settings: ServerSettings) -> Request { Request { - inner: Box::new(InnerRequest { + inner: Rc::new(InnerRequest { + pool, settings, method: Method::GET, url: InnerUrl::default(), @@ -72,45 +87,55 @@ impl Request { } } + #[inline] + pub(crate) fn inner(&self) -> &InnerRequest { + self.inner.as_ref() + } + + #[inline] + pub(crate) fn inner_mut(&mut self) -> &mut InnerRequest { + Rc::get_mut(&mut self.inner).expect("Multiple copies exist") + } + #[inline] pub(crate) fn url(&self) -> &InnerUrl { - &self.inner.url + &self.inner().url } /// Read the Request Uri. #[inline] pub fn uri(&self) -> &Uri { - self.inner.url.uri() + self.inner().url.uri() } /// Read the Request method. #[inline] pub fn method(&self) -> &Method { - &self.inner.method + &self.inner().method } /// Read the Request Version. #[inline] pub fn version(&self) -> Version { - self.inner.version + self.inner().version } /// The target path of this Request. #[inline] pub fn path(&self) -> &str { - self.inner.url.path() + self.inner().url.path() } #[inline] /// Returns Request's headers. pub fn headers(&self) -> &HeaderMap { - &self.inner.headers + &self.inner().headers } #[inline] /// Returns mutable Request's headers. pub fn headers_mut(&mut self) -> &mut HeaderMap { - &mut self.inner.headers + &mut self.inner_mut().headers } /// Peer socket address @@ -121,67 +146,71 @@ impl Request { /// To get client connection information `connection_info()` method should /// be used. pub fn peer_addr(&self) -> Option { - self.inner.addr + self.inner().addr } /// Checks if a connection should be kept alive. #[inline] pub fn keep_alive(&self) -> bool { - self.inner.flags.get().contains(MessageFlags::KEEPALIVE) + self.inner().flags.get().contains(MessageFlags::KEEPALIVE) } /// Request extensions #[inline] pub fn extensions(&self) -> Ref { - self.inner.extensions.borrow() + self.inner().extensions.borrow() } /// Mutable reference to a the request's extensions #[inline] pub fn extensions_mut(&self) -> RefMut { - self.inner.extensions.borrow_mut() + self.inner().extensions.borrow_mut() } /// Check if request requires connection upgrade pub fn upgrade(&self) -> bool { - if let Some(conn) = self.inner.headers.get(header::CONNECTION) { + if let Some(conn) = self.inner().headers.get(header::CONNECTION) { if let Ok(s) = conn.to_str() { return s.to_lowercase().contains("upgrade"); } } - self.inner.method == Method::CONNECT + self.inner().method == Method::CONNECT } /// Get *ConnectionInfo* for the correct request. pub fn connection_info(&self) -> Ref { - if self.inner.flags.get().contains(MessageFlags::CONN_INFO) { - self.inner.info.borrow() + if self.inner().flags.get().contains(MessageFlags::CONN_INFO) { + self.inner().info.borrow() } else { - let mut flags = self.inner.flags.get(); + let mut flags = self.inner().flags.get(); flags.insert(MessageFlags::CONN_INFO); - self.inner.flags.set(flags); - self.inner.info.borrow_mut().update(self); - self.inner.info.borrow() + self.inner().flags.set(flags); + self.inner().info.borrow_mut().update(self); + self.inner().info.borrow() } } /// Server settings #[inline] pub fn server_settings(&self) -> &ServerSettings { - &self.inner.settings + &self.inner().settings } - #[inline] - /// Reset request instance - pub fn reset(&mut self) { - self.inner.headers.clear(); - self.inner.extensions.borrow_mut().clear(); - self.inner.flags.set(MessageFlags::empty()); - *self.inner.payload.borrow_mut() = None; + pub(crate) fn release(self) { + let mut inner = self.inner; + if let Some(r) = Rc::get_mut(&mut inner) { + r.reset(); + } else { + return; + } + inner.pool.release(inner); } } -pub(crate) struct RequestPool(RefCell>, RefCell); +pub(crate) struct RequestPool( + RefCell>>, + RefCell, +); thread_local!(static POOL: &'static RequestPool = RequestPool::create()); @@ -202,20 +231,19 @@ impl RequestPool { } #[inline] - pub fn get(&self) -> Request { - if let Some(msg) = self.0.borrow_mut().pop_front() { - msg + pub fn get(pool: &'static RequestPool) -> Request { + if let Some(msg) = pool.0.borrow_mut().pop_front() { + Request { inner: msg } } else { - Request::new(self.1.borrow().clone()) + Request::new(pool, pool.1.borrow().clone()) } } #[inline] /// Release request instance - pub fn release(&self, mut msg: Request) { + pub fn release(&self, msg: Rc) { let v = &mut self.0.borrow_mut(); if v.len() < 128 { - msg.reset(); v.push_front(msg); } } diff --git a/src/server/settings.rs b/src/server/settings.rs index ceb362ac2..0347241bc 100644 --- a/src/server/settings.rs +++ b/src/server/settings.rs @@ -212,8 +212,8 @@ impl WorkerSettings { self.bytes.release_bytes(bytes) } - pub fn get_request_context(&self) -> Request { - self.messages.get() + pub fn get_request(&self) -> Request { + RequestPool::get(self.messages) } pub fn add_channel(&self) { diff --git a/src/test.rs b/src/test.rs index bc34472e4..704292df1 100644 --- a/src/test.rs +++ b/src/test.rs @@ -29,7 +29,8 @@ use param::Params; use payload::Payload; use resource::ResourceHandler; use router::Router; -use server::{HttpServer, IntoHttpHandler, Request, ServerSettings}; +use server::message::{Request, RequestPool}; +use server::{HttpServer, IntoHttpHandler, ServerSettings}; use uri::Url as InnerUrl; use ws; @@ -540,12 +541,16 @@ impl TestRequest { } = self; let (router, _) = Router::new::("/", Vec::new()); - let mut req = Request::new(ServerSettings::default()); - req.inner.method = method; - req.inner.url = InnerUrl::new(uri); - req.inner.version = version; - req.inner.headers = headers; - *req.inner.payload.borrow_mut() = payload; + let pool = RequestPool::pool(ServerSettings::default()); + let mut req = RequestPool::get(pool); + { + let inner = req.inner_mut(); + inner.method = method; + inner.url = InnerUrl::new(uri); + inner.version = version; + inner.headers = headers; + *inner.payload.borrow_mut() = payload; + } let mut req = HttpRequest::new(req, Rc::new(state), router.route_info_params(params)); @@ -567,12 +572,16 @@ impl TestRequest { payload, } = self; - let mut req = Request::new(ServerSettings::default()); - req.inner.method = method; - req.inner.url = InnerUrl::new(uri); - req.inner.version = version; - req.inner.headers = headers; - *req.inner.payload.borrow_mut() = payload; + let pool = RequestPool::pool(ServerSettings::default()); + let mut req = RequestPool::get(pool); + { + let inner = req.inner_mut(); + inner.method = method; + inner.url = InnerUrl::new(uri); + inner.version = version; + inner.headers = headers; + *inner.payload.borrow_mut() = payload; + } let mut req = HttpRequest::new(req, Rc::new(state), router.route_info_params(params)); req.set_cookies(cookies); @@ -589,12 +598,17 @@ impl TestRequest { payload, .. } = self; - let mut req = Request::new(ServerSettings::default()); - req.inner.method = method; - req.inner.url = InnerUrl::new(uri); - req.inner.version = version; - req.inner.headers = headers; - *req.inner.payload.borrow_mut() = payload; + + let pool = RequestPool::pool(ServerSettings::default()); + let mut req = RequestPool::get(pool); + { + let inner = req.inner_mut(); + inner.method = method; + inner.url = InnerUrl::new(uri); + inner.version = version; + inner.headers = headers; + *inner.payload.borrow_mut() = payload; + } req }