1
0
mirror of https://github.com/actix/actix-extras.git synced 2024-11-30 18:34:36 +01:00

use objects pool for HttpRequest; optimize nested services call

This commit is contained in:
Nikolay Kim 2019-04-07 23:06:21 -07:00
parent 75b213a6f0
commit aa78565453
20 changed files with 343 additions and 295 deletions

View File

@ -68,9 +68,9 @@ rust-tls = ["rustls", "actix-server/rust-tls"]
[dependencies] [dependencies]
actix-codec = "0.1.1" actix-codec = "0.1.1"
actix-service = "0.3.4" actix-service = "0.3.6"
actix-utils = "0.3.4" actix-utils = "0.3.4"
actix-router = "0.1.1" actix-router = "0.1.2"
actix-rt = "0.2.2" actix-rt = "0.2.2"
actix-web-codegen = "0.1.0-alpha.1" actix-web-codegen = "0.1.0-alpha.1"
actix-http = { version = "0.1.0-alpha.3", features=["fail"] } actix-http = { version = "0.1.0-alpha.3", features=["fail"] }

View File

@ -423,7 +423,7 @@ impl<P> FilesService<P> {
> { > {
log::debug!("Files: Failed to handle {}: {}", req.path(), e); log::debug!("Files: Failed to handle {}: {}", req.path(), e);
if let Some(ref mut default) = self.default { if let Some(ref mut default) = self.default {
Either::B(default.call(ServiceRequest::from_parts(req, payload))) default.call(ServiceRequest::from_parts(req, payload))
} else { } else {
Either::A(ok(ServiceResponse::from_err(e, req.clone()))) Either::A(ok(ServiceResponse::from_err(e, req.clone())))
} }
@ -955,6 +955,7 @@ mod tests {
.method(Method::POST) .method(Method::POST)
.to_http_request(); .to_http_request();
let resp = file.respond_to(&req).unwrap(); let resp = file.respond_to(&req).unwrap();
println!("RES: {:?}", resp);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED); assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let file = NamedFile::open("Cargo.toml").unwrap(); let file = NamedFile::open("Cargo.toml").unwrap();

View File

@ -104,9 +104,8 @@ where
let (parts, body) = resp.into_parts(); let (parts, body) = resp.into_parts();
let payload = if head_req { Payload::None } else { body.into() }; let payload = if head_req { Payload::None } else { body.into() };
let mut head = ResponseHead::default(); let mut head = ResponseHead::new(parts.status);
head.version = parts.version; head.version = parts.version;
head.status = parts.status;
head.headers = parts.headers.into(); head.headers = parts.headers.into();
Ok((head, payload)) Ok((head, payload))

View File

@ -21,6 +21,7 @@ use tokio_timer::{sleep, Delay};
use super::connection::{ConnectionType, IoConnection}; use super::connection::{ConnectionType, IoConnection};
use super::error::ConnectError; use super::error::ConnectError;
#[allow(dead_code)]
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum Protocol { pub enum Protocol {
Http1, Http1,

View File

@ -73,8 +73,9 @@ pub(crate) trait MessageType: Sized {
let headers = self.headers_mut(); let headers = self.headers_mut();
for idx in raw_headers.iter() { for idx in raw_headers.iter() {
if let Ok(name) = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]) let name =
{ HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
// Unsafe: httparse check header value for valid utf-8 // Unsafe: httparse check header value for valid utf-8
let value = unsafe { let value = unsafe {
HeaderValue::from_shared_unchecked( HeaderValue::from_shared_unchecked(
@ -107,8 +108,7 @@ pub(crate) trait MessageType: Sized {
} }
// connection keep-alive state // connection keep-alive state
header::CONNECTION => { header::CONNECTION => {
ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) {
{
if conn.eq_ignore_ascii_case("keep-alive") { if conn.eq_ignore_ascii_case("keep-alive") {
Some(ConnectionType::KeepAlive) Some(ConnectionType::KeepAlive)
} else if conn.eq_ignore_ascii_case("close") { } else if conn.eq_ignore_ascii_case("close") {
@ -142,9 +142,6 @@ pub(crate) trait MessageType: Sized {
} }
headers.append(name, value); headers.append(name, value);
} else {
return Err(ParseError::Header);
}
} }
} }
self.set_connection_type(ka); self.set_connection_type(ka);
@ -217,10 +214,10 @@ impl MessageType for Request {
let mut msg = Request::new(); let mut msg = Request::new();
// convert headers // convert headers
let len = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?;
// payload decoder // payload decoder
let decoder = match len { let decoder = match length {
PayloadLength::Payload(pl) => pl, PayloadLength::Payload(pl) => pl,
PayloadLength::Upgrade => { PayloadLength::Upgrade => {
// upgrade(websocket) // upgrade(websocket)
@ -287,13 +284,14 @@ impl MessageType for ResponseHead {
} }
}; };
let mut msg = ResponseHead::default(); let mut msg = ResponseHead::new(status);
msg.version = ver;
// convert headers // convert headers
let len = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?; let length = msg.set_headers(&src.split_to(len).freeze(), &headers[..h_len])?;
// message payload // message payload
let decoder = if let PayloadLength::Payload(pl) = len { let decoder = if let PayloadLength::Payload(pl) = length {
pl pl
} else if status == StatusCode::SWITCHING_PROTOCOLS { } else if status == StatusCode::SWITCHING_PROTOCOLS {
// switching protocol or connect // switching protocol or connect
@ -305,9 +303,6 @@ impl MessageType for ResponseHead {
PayloadType::None PayloadType::None
}; };
msg.status = status;
msg.version = ver;
Ok(Some((msg, decoder))) Ok(Some((msg, decoder)))
} }
} }

View File

@ -218,7 +218,7 @@ where
{ {
fn can_read(&self) -> bool { fn can_read(&self) -> bool {
if self.flags.contains(Flags::READ_DISCONNECT) { if self.flags.contains(Flags::READ_DISCONNECT) {
return false; false
} else if let Some(ref info) = self.payload { } else if let Some(ref info) = self.payload {
info.need_read() == PayloadStatus::Read info.need_read() == PayloadStatus::Read
} else { } else {

View File

@ -8,7 +8,7 @@ macro_rules! STATIC_RESP {
($name:ident, $status:expr) => { ($name:ident, $status:expr) => {
#[allow(non_snake_case, missing_docs)] #[allow(non_snake_case, missing_docs)]
pub fn $name() -> ResponseBuilder { pub fn $name() -> ResponseBuilder {
Response::build($status) ResponseBuilder::new($status)
} }
}; };
} }

View File

@ -1,5 +1,9 @@
//! Basic http primitives for actix-net framework. //! Basic http primitives for actix-net framework.
#![allow(clippy::type_complexity, clippy::new_without_default)] #![allow(
clippy::type_complexity,
clippy::new_without_default,
clippy::borrow_interior_mutable_const
)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;

View File

@ -1,5 +1,4 @@
use std::cell::{Ref, RefCell, RefMut}; use std::cell::{Ref, RefCell, RefMut};
use std::collections::VecDeque;
use std::rc::Rc; use std::rc::Rc;
use bitflags::bitflags; use bitflags::bitflags;
@ -171,20 +170,20 @@ pub struct ResponseHead {
flags: Flags, flags: Flags,
} }
impl Default for ResponseHead { impl ResponseHead {
fn default() -> ResponseHead { /// Create new instance of `ResponseHead` type
#[inline]
pub fn new(status: StatusCode) -> ResponseHead {
ResponseHead { ResponseHead {
status,
version: Version::default(), version: Version::default(),
status: StatusCode::OK,
headers: HeaderMap::with_capacity(12), headers: HeaderMap::with_capacity(12),
reason: None, reason: None,
flags: Flags::empty(), flags: Flags::empty(),
extensions: RefCell::new(Extensions::new()), extensions: RefCell::new(Extensions::new()),
} }
} }
}
impl ResponseHead {
/// Message extensions /// Message extensions
#[inline] #[inline]
pub fn extensions(&self) -> Ref<Extensions> { pub fn extensions(&self) -> Ref<Extensions> {
@ -335,8 +334,8 @@ pub(crate) struct BoxedResponseHead {
impl BoxedResponseHead { impl BoxedResponseHead {
/// Get new message from the pool of objects /// Get new message from the pool of objects
pub fn new() -> Self { pub fn new(status: StatusCode) -> Self {
RESPONSE_POOL.with(|p| p.get_message()) RESPONSE_POOL.with(|p| p.get_message(status))
} }
} }
@ -362,25 +361,25 @@ impl Drop for BoxedResponseHead {
#[doc(hidden)] #[doc(hidden)]
/// Request's objects pool /// Request's objects pool
pub struct MessagePool<T: Head>(RefCell<VecDeque<Rc<T>>>); pub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>);
#[doc(hidden)] #[doc(hidden)]
/// Request's objects pool /// Request's objects pool
pub struct BoxedResponsePool(RefCell<VecDeque<Box<ResponseHead>>>); pub struct BoxedResponsePool(RefCell<Vec<Box<ResponseHead>>>);
thread_local!(static REQUEST_POOL: &'static MessagePool<RequestHead> = MessagePool::<RequestHead>::create()); thread_local!(static REQUEST_POOL: &'static MessagePool<RequestHead> = MessagePool::<RequestHead>::create());
thread_local!(static RESPONSE_POOL: &'static BoxedResponsePool = BoxedResponsePool::create()); thread_local!(static RESPONSE_POOL: &'static BoxedResponsePool = BoxedResponsePool::create());
impl<T: Head> MessagePool<T> { impl<T: Head> MessagePool<T> {
fn create() -> &'static MessagePool<T> { fn create() -> &'static MessagePool<T> {
let pool = MessagePool(RefCell::new(VecDeque::with_capacity(128))); let pool = MessagePool(RefCell::new(Vec::with_capacity(128)));
Box::leak(Box::new(pool)) Box::leak(Box::new(pool))
} }
/// Get message from the pool /// Get message from the pool
#[inline] #[inline]
fn get_message(&'static self) -> Message<T> { fn get_message(&'static self) -> Message<T> {
if let Some(mut msg) = self.0.borrow_mut().pop_front() { if let Some(mut msg) = self.0.borrow_mut().pop() {
if let Some(r) = Rc::get_mut(&mut msg) { if let Some(r) = Rc::get_mut(&mut msg) {
r.clear(); r.clear();
} }
@ -397,28 +396,29 @@ impl<T: Head> MessagePool<T> {
fn release(&self, msg: Rc<T>) { fn release(&self, msg: Rc<T>) {
let v = &mut self.0.borrow_mut(); let v = &mut self.0.borrow_mut();
if v.len() < 128 { if v.len() < 128 {
v.push_front(msg); v.push(msg);
} }
} }
} }
impl BoxedResponsePool { impl BoxedResponsePool {
fn create() -> &'static BoxedResponsePool { fn create() -> &'static BoxedResponsePool {
let pool = BoxedResponsePool(RefCell::new(VecDeque::with_capacity(128))); let pool = BoxedResponsePool(RefCell::new(Vec::with_capacity(128)));
Box::leak(Box::new(pool)) Box::leak(Box::new(pool))
} }
/// Get message from the pool /// Get message from the pool
#[inline] #[inline]
fn get_message(&'static self) -> BoxedResponseHead { fn get_message(&'static self, status: StatusCode) -> BoxedResponseHead {
if let Some(mut head) = self.0.borrow_mut().pop_front() { if let Some(mut head) = self.0.borrow_mut().pop() {
head.reason = None; head.reason = None;
head.status = status;
head.headers.clear(); head.headers.clear();
head.flags = Flags::empty(); head.flags = Flags::empty();
BoxedResponseHead { head: Some(head) } BoxedResponseHead { head: Some(head) }
} else { } else {
BoxedResponseHead { BoxedResponseHead {
head: Some(Box::alloc().init(ResponseHead::default())), head: Some(Box::alloc().init(ResponseHead::new(status))),
} }
} }
} }
@ -428,7 +428,7 @@ impl BoxedResponsePool {
fn release(&self, msg: Box<ResponseHead>) { fn release(&self, msg: Box<ResponseHead>) {
let v = &mut self.0.borrow_mut(); let v = &mut self.0.borrow_mut();
if v.len() < 128 { if v.len() < 128 {
v.push_front(msg); v.push(msg);
} }
} }
} }

View File

@ -41,11 +41,8 @@ impl Response<Body> {
/// Constructs a response /// Constructs a response
#[inline] #[inline]
pub fn new(status: StatusCode) -> Response { pub fn new(status: StatusCode) -> Response {
let mut head = BoxedResponseHead::new();
head.status = status;
Response { Response {
head, head: BoxedResponseHead::new(status),
body: ResponseBody::Body(Body::Empty), body: ResponseBody::Body(Body::Empty),
error: None, error: None,
} }
@ -78,6 +75,16 @@ impl Response<Body> {
} }
impl<B> Response<B> { impl<B> Response<B> {
/// Constructs a response with body
#[inline]
pub fn with_body(status: StatusCode, body: B) -> Response<B> {
Response {
head: BoxedResponseHead::new(status),
body: ResponseBody::Body(body),
error: None,
}
}
#[inline] #[inline]
/// Http message part of the response /// Http message part of the response
pub fn head(&self) -> &ResponseHead { pub fn head(&self) -> &ResponseHead {
@ -90,18 +97,6 @@ impl<B> Response<B> {
&mut *self.head &mut *self.head
} }
/// Constructs a response with body
#[inline]
pub fn with_body(status: StatusCode, body: B) -> Response<B> {
let mut head = BoxedResponseHead::new();
head.status = status;
Response {
head,
body: ResponseBody::Body(body),
error: None,
}
}
/// The source `error` for this response /// The source `error` for this response
#[inline] #[inline]
pub fn error(&self) -> Option<&Error> { pub fn error(&self) -> Option<&Error> {
@ -325,13 +320,11 @@ pub struct ResponseBuilder {
} }
impl ResponseBuilder { impl ResponseBuilder {
#[inline]
/// Create response builder /// Create response builder
pub fn new(status: StatusCode) -> Self { pub fn new(status: StatusCode) -> Self {
let mut head = BoxedResponseHead::new();
head.status = status;
ResponseBuilder { ResponseBuilder {
head: Some(head), head: Some(BoxedResponseHead::new(status)),
err: None, err: None,
cookies: None, cookies: None,
} }
@ -555,7 +548,6 @@ impl ResponseBuilder {
/// } /// }
/// ``` /// ```
pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self { pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self {
{
if self.cookies.is_none() { if self.cookies.is_none() {
self.cookies = Some(CookieJar::new()) self.cookies = Some(CookieJar::new())
} }
@ -563,7 +555,6 @@ impl ResponseBuilder {
let cookie = cookie.clone().into_owned(); let cookie = cookie.clone().into_owned();
jar.add_original(cookie.clone()); jar.add_original(cookie.clone());
jar.remove(cookie); jar.remove(cookie);
}
self self
} }
@ -605,6 +596,7 @@ impl ResponseBuilder {
head.extensions.borrow_mut() head.extensions.borrow_mut()
} }
#[inline]
/// Set a body and generate `Response`. /// Set a body and generate `Response`.
/// ///
/// `ResponseBuilder` can not be used after this call. /// `ResponseBuilder` can not be used after this call.
@ -625,9 +617,7 @@ impl ResponseBuilder {
if let Some(ref jar) = self.cookies { if let Some(ref jar) = self.cookies {
for cookie in jar.delta() { for cookie in jar.delta() {
match HeaderValue::from_str(&cookie.to_string()) { match HeaderValue::from_str(&cookie.to_string()) {
Ok(val) => { Ok(val) => response.headers.append(header::SET_COOKIE, val),
let _ = response.headers.append(header::SET_COOKIE, val);
}
Err(e) => return Response::from(Error::from(e)).into_body(), Err(e) => return Response::from(Error::from(e)).into_body(),
}; };
} }
@ -652,6 +642,7 @@ impl ResponseBuilder {
self.body(Body::from_message(BodyStream::new(stream))) self.body(Body::from_message(BodyStream::new(stream)))
} }
#[inline]
/// Set a json body and generate `Response` /// Set a json body and generate `Response`
/// ///
/// `ResponseBuilder` can not be used after this call. /// `ResponseBuilder` can not be used after this call.
@ -751,11 +742,12 @@ impl<'a> From<&'a ResponseHead> for ResponseBuilder {
} }
} }
let mut msg = BoxedResponseHead::new(); let mut msg = BoxedResponseHead::new(head.status);
msg.version = head.version; msg.version = head.version;
msg.status = head.status;
msg.reason = head.reason; msg.reason = head.reason;
// msg.headers = head.headers.clone(); for (k, v) in &head.headers {
msg.headers.append(k.clone(), v.clone());
}
msg.no_chunking(!head.chunked()); msg.no_chunking(!head.chunked());
ResponseBuilder { ResponseBuilder {

View File

@ -178,6 +178,6 @@ impl TestRequest {
} }
#[inline] #[inline]
fn parts<'a>(parts: &'a mut Option<Inner>) -> &'a mut Inner { fn parts(parts: &mut Option<Inner>) -> &mut Inner {
parts.as_mut().expect("cannot reuse test request builder") parts.as_mut().expect("cannot reuse test request builder")
} }

View File

@ -3,7 +3,7 @@ use std::fmt::Write as FmtWrite;
use actix_http::cookie::{Cookie, CookieJar}; use actix_http::cookie::{Cookie, CookieJar};
use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue}; use actix_http::http::header::{self, Header, HeaderValue, IntoHeaderValue};
use actix_http::http::{HeaderName, HttpTryFrom, Version}; use actix_http::http::{HeaderName, HttpTryFrom, StatusCode, Version};
use actix_http::{h1, Payload, ResponseHead}; use actix_http::{h1, Payload, ResponseHead};
use bytes::Bytes; use bytes::Bytes;
#[cfg(test)] #[cfg(test)]
@ -49,7 +49,7 @@ pub struct TestResponse {
impl Default for TestResponse { impl Default for TestResponse {
fn default() -> TestResponse { fn default() -> TestResponse {
TestResponse { TestResponse {
head: ResponseHead::default(), head: ResponseHead::new(StatusCode::OK),
cookies: CookieJar::new(), cookies: CookieJar::new(),
payload: None, payload: None,
} }

View File

@ -14,6 +14,7 @@ use crate::config::{AppConfig, ServiceConfig};
use crate::data::{DataFactory, DataFactoryResult}; use crate::data::{DataFactory, DataFactoryResult};
use crate::error::Error; use crate::error::Error;
use crate::guard::Guard; use crate::guard::Guard;
use crate::request::{HttpRequest, HttpRequestPool};
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse}; use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse};
@ -21,7 +22,10 @@ type Guards = Vec<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, Error>; type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, Error>;
type HttpNewService<P> = type HttpNewService<P> =
BoxedNewService<(), ServiceRequest<P>, ServiceResponse, Error, ()>; BoxedNewService<(), ServiceRequest<P>, ServiceResponse, Error, ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = Error>>; type BoxedResponse = Either<
FutureResult<ServiceResponse, Error>,
Box<Future<Item = ServiceResponse, Error = Error>>,
>;
/// Service factory to convert `Request` to a `ServiceRequest<S>`. /// Service factory to convert `Request` to a `ServiceRequest<S>`.
/// It also executes data factories. /// It also executes data factories.
@ -191,6 +195,7 @@ where
chain: self.chain.take().unwrap(), chain: self.chain.take().unwrap(),
rmap: self.rmap.clone(), rmap: self.rmap.clone(),
config: self.config.clone(), config: self.config.clone(),
pool: HttpRequestPool::create(),
} }
.and_then(self.endpoint.take().unwrap()), .and_then(self.endpoint.take().unwrap()),
)) ))
@ -208,6 +213,7 @@ where
chain: C, chain: C,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
config: AppConfig, config: AppConfig,
pool: &'static HttpRequestPool,
} }
impl<C, P> Service for AppInitService<C, P> impl<C, P> Service for AppInitService<C, P>
@ -224,13 +230,24 @@ where
} }
fn call(&mut self, req: Request) -> Self::Future { fn call(&mut self, req: Request) -> Self::Future {
let req = ServiceRequest::new( let (head, payload) = req.into_parts();
Path::new(Url::new(req.uri().clone())),
req, let req = if let Some(mut req) = self.pool.get_request() {
let inner = Rc::get_mut(&mut req.0).unwrap();
inner.path.get_mut().update(&head.uri);
inner.path.reset();
inner.head = head;
req
} else {
HttpRequest::new(
Path::new(Url::new(head.uri.clone())),
head,
self.rmap.clone(), self.rmap.clone(),
self.config.clone(), self.config.clone(),
); self.pool,
self.chain.call(req) )
};
self.chain.call(ServiceRequest::from_parts(req, payload))
} }
} }
@ -353,7 +370,7 @@ impl<P> Service for AppRouting<P> {
type Request = ServiceRequest<P>; type Request = ServiceRequest<P>;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either<BoxedResponse, FutureResult<Self::Response, Self::Error>>; type Future = BoxedResponse;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
if self.ready.is_none() { if self.ready.is_none() {
@ -376,12 +393,12 @@ impl<P> Service for AppRouting<P> {
}); });
if let Some((srv, _info)) = res { if let Some((srv, _info)) = res {
Either::A(srv.call(req)) srv.call(req)
} else if let Some(ref mut default) = self.default { } else if let Some(ref mut default) = self.default {
Either::A(default.call(req)) default.call(req)
} else { } else {
let req = req.into_parts().0; let req = req.into_parts().0;
Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish()))) Either::A(ok(ServiceResponse::new(req, Response::NotFound().finish())))
} }
} }
} }

View File

@ -52,37 +52,21 @@ where
} }
} }
} }
impl<F, T, R> NewService for Handler<F, T, R>
impl<F, T, R> Clone for Handler<F, T, R>
where where
F: Factory<T, R>, F: Factory<T, R>,
R: Responder, R: Responder,
{ {
type Request = (T, HttpRequest); fn clone(&self) -> Self {
type Response = ServiceResponse; Self {
type Error = Void;
type InitError = ();
type Service = HandlerService<F, T, R>;
type Future = FutureResult<Self::Service, ()>;
fn new_service(&self, _: &()) -> Self::Future {
ok(HandlerService {
hnd: self.hnd.clone(), hnd: self.hnd.clone(),
_t: PhantomData, _t: PhantomData,
}) }
} }
} }
#[doc(hidden)] impl<F, T, R> Service for Handler<F, T, R>
pub struct HandlerService<F, T, R>
where
F: Factory<T, R>,
R: Responder,
{
hnd: F,
_t: PhantomData<(T, R)>,
}
impl<F, T, R> Service for HandlerService<F, T, R>
where where
F: Factory<T, R>, F: Factory<T, R>,
R: Responder, R: Responder,
@ -184,41 +168,23 @@ where
} }
} }
} }
impl<F, T, R> NewService for AsyncHandler<F, T, R>
impl<F, T, R> Clone for AsyncHandler<F, T, R>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R>,
R: IntoFuture, R: IntoFuture,
R::Item: Into<Response>, R::Item: Into<Response>,
R::Error: Into<Error>, R::Error: Into<Error>,
{ {
type Request = (T, HttpRequest); fn clone(&self) -> Self {
type Response = ServiceResponse; AsyncHandler {
type Error = Error;
type InitError = ();
type Service = AsyncHandlerService<F, T, R>;
type Future = FutureResult<Self::Service, ()>;
fn new_service(&self, _: &()) -> Self::Future {
ok(AsyncHandlerService {
hnd: self.hnd.clone(), hnd: self.hnd.clone(),
_t: PhantomData, _t: PhantomData,
}) }
} }
} }
#[doc(hidden)] impl<F, T, R> Service for AsyncHandler<F, T, R>
pub struct AsyncHandlerService<F, T, R>
where
F: AsyncFactory<T, R>,
R: IntoFuture,
R::Item: Into<Response>,
R::Error: Into<Error>,
{
hnd: F,
_t: PhantomData<(T, R)>,
}
impl<F, T, R> Service for AsyncHandlerService<F, T, R>
where where
F: AsyncFactory<T, R>, F: AsyncFactory<T, R>,
R: IntoFuture, R: IntoFuture,
@ -227,7 +193,7 @@ where
{ {
type Request = (T, HttpRequest); type Request = (T, HttpRequest);
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Void;
type Future = AsyncHandlerServiceResponse<R::Future>; type Future = AsyncHandlerServiceResponse<R::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@ -255,7 +221,7 @@ where
T::Error: Into<Error>, T::Error: Into<Error>,
{ {
type Item = ServiceResponse; type Item = ServiceResponse;
type Error = Error; type Error = Void;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.fut.poll() { match self.fut.poll() {
@ -276,46 +242,58 @@ where
} }
/// Extract arguments from request /// Extract arguments from request
pub struct Extract<P, T: FromRequest<P>> { pub struct Extract<P, T: FromRequest<P>, S> {
config: Rc<RefCell<Option<Rc<Extensions>>>>, config: Rc<RefCell<Option<Rc<Extensions>>>>,
service: S,
_t: PhantomData<(P, T)>, _t: PhantomData<(P, T)>,
} }
impl<P, T: FromRequest<P>> Extract<P, T> { impl<P, T: FromRequest<P>, S> Extract<P, T, S> {
pub fn new(config: Rc<RefCell<Option<Rc<Extensions>>>>) -> Self { pub fn new(config: Rc<RefCell<Option<Rc<Extensions>>>>, service: S) -> Self {
Extract { Extract {
config, config,
service,
_t: PhantomData, _t: PhantomData,
} }
} }
} }
impl<P, T: FromRequest<P>> NewService for Extract<P, T> { impl<P, T: FromRequest<P>, S> NewService for Extract<P, T, S>
where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void>
+ Clone,
{
type Request = ServiceRequest<P>; type Request = ServiceRequest<P>;
type Response = (T, HttpRequest); type Response = ServiceResponse;
type Error = (Error, ServiceRequest<P>); type Error = (Error, ServiceRequest<P>);
type InitError = (); type InitError = ();
type Service = ExtractService<P, T>; type Service = ExtractService<P, T, S>;
type Future = FutureResult<Self::Service, ()>; type Future = FutureResult<Self::Service, ()>;
fn new_service(&self, _: &()) -> Self::Future { fn new_service(&self, _: &()) -> Self::Future {
ok(ExtractService { ok(ExtractService {
_t: PhantomData, _t: PhantomData,
config: self.config.borrow().clone(), config: self.config.borrow().clone(),
service: self.service.clone(),
}) })
} }
} }
pub struct ExtractService<P, T: FromRequest<P>> { pub struct ExtractService<P, T: FromRequest<P>, S> {
config: Option<Rc<Extensions>>, config: Option<Rc<Extensions>>,
service: S,
_t: PhantomData<(P, T)>, _t: PhantomData<(P, T)>,
} }
impl<P, T: FromRequest<P>> Service for ExtractService<P, T> { impl<P, T: FromRequest<P>, S> Service for ExtractService<P, T, S>
where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void>
+ Clone,
{
type Request = ServiceRequest<P>; type Request = ServiceRequest<P>;
type Response = (T, HttpRequest); type Response = ServiceResponse;
type Error = (Error, ServiceRequest<P>); type Error = (Error, ServiceRequest<P>);
type Future = ExtractResponse<P, T>; type Future = ExtractResponse<P, T, S>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
Ok(Async::Ready(())) Ok(Async::Ready(()))
@ -328,28 +306,40 @@ impl<P, T: FromRequest<P>> Service for ExtractService<P, T> {
ExtractResponse { ExtractResponse {
fut, fut,
fut_s: None,
req: Some((req, payload)), req: Some((req, payload)),
service: self.service.clone(),
} }
} }
} }
pub struct ExtractResponse<P, T: FromRequest<P>> { pub struct ExtractResponse<P, T: FromRequest<P>, S: Service> {
req: Option<(HttpRequest, Payload<P>)>, req: Option<(HttpRequest, Payload<P>)>,
service: S,
fut: <T::Future as IntoFuture>::Future, fut: <T::Future as IntoFuture>::Future,
fut_s: Option<S::Future>,
} }
impl<P, T: FromRequest<P>> Future for ExtractResponse<P, T> { impl<P, T: FromRequest<P>, S> Future for ExtractResponse<P, T, S>
type Item = (T, HttpRequest); where
S: Service<Request = (T, HttpRequest), Response = ServiceResponse, Error = Void>,
{
type Item = ServiceResponse;
type Error = (Error, ServiceRequest<P>); type Error = (Error, ServiceRequest<P>);
fn poll(&mut self) -> Poll<Self::Item, Self::Error> { fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
if let Some(ref mut fut) = self.fut_s {
return fut.poll().map_err(|_| panic!());
}
let item = try_ready!(self.fut.poll().map_err(|e| { let item = try_ready!(self.fut.poll().map_err(|e| {
let (req, payload) = self.req.take().unwrap(); let (req, payload) = self.req.take().unwrap();
let req = ServiceRequest::from_parts(req, payload); let req = ServiceRequest::from_parts(req, payload);
(e.into(), req) (e.into(), req)
})); }));
Ok(Async::Ready((item, self.req.take().unwrap().0))) self.fut_s = Some(self.service.call((item, self.req.take().unwrap().0)));
self.poll()
} }
} }

View File

@ -1,4 +1,4 @@
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefCell, RefMut};
use std::fmt; use std::fmt;
use std::rc::Rc; use std::rc::Rc;
@ -15,29 +15,34 @@ use crate::rmap::ResourceMap;
#[derive(Clone)] #[derive(Clone)]
/// An HTTP Request /// An HTTP Request
pub struct HttpRequest { pub struct HttpRequest(pub(crate) Rc<HttpRequestInner>);
pub(crate) struct HttpRequestInner {
pub(crate) head: Message<RequestHead>, pub(crate) head: Message<RequestHead>,
pub(crate) path: Path<Url>, pub(crate) path: Path<Url>,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
config: AppConfig, config: AppConfig,
route_data: Option<Rc<Extensions>>, route_data: Option<Rc<Extensions>>,
pool: &'static HttpRequestPool,
} }
impl HttpRequest { impl HttpRequest {
#[inline] #[inline]
pub(crate) fn new( pub(crate) fn new(
head: Message<RequestHead>,
path: Path<Url>, path: Path<Url>,
head: Message<RequestHead>,
rmap: Rc<ResourceMap>, rmap: Rc<ResourceMap>,
config: AppConfig, config: AppConfig,
pool: &'static HttpRequestPool,
) -> HttpRequest { ) -> HttpRequest {
HttpRequest { HttpRequest(Rc::new(HttpRequestInner {
head, head,
path, path,
rmap, rmap,
config, config,
pool,
route_data: None, route_data: None,
} }))
} }
} }
@ -45,7 +50,14 @@ impl HttpRequest {
/// This method returns reference to the request head /// This method returns reference to the request head
#[inline] #[inline]
pub fn head(&self) -> &RequestHead { pub fn head(&self) -> &RequestHead {
&self.head &self.0.head
}
/// This method returns muttable reference to the request head.
/// panics if multiple references of http request exists.
#[inline]
pub(crate) fn head_mut(&mut self) -> &mut RequestHead {
&mut Rc::get_mut(&mut self.0).unwrap().head
} }
/// Request's uri. /// Request's uri.
@ -98,7 +110,12 @@ impl HttpRequest {
/// access the matched value for that segment. /// access the matched value for that segment.
#[inline] #[inline]
pub fn match_info(&self) -> &Path<Url> { pub fn match_info(&self) -> &Path<Url> {
&self.path &self.0.path
}
#[inline]
pub(crate) fn match_info_mut(&mut self) -> &mut Path<Url> {
&mut Rc::get_mut(&mut self.0).unwrap().path
} }
/// Request extensions /// Request extensions
@ -141,7 +158,7 @@ impl HttpRequest {
U: IntoIterator<Item = I>, U: IntoIterator<Item = I>,
I: AsRef<str>, I: AsRef<str>,
{ {
self.rmap.url_for(&self, name, elements) self.0.rmap.url_for(&self, name, elements)
} }
/// Generate url for named resource /// Generate url for named resource
@ -162,13 +179,13 @@ impl HttpRequest {
/// App config /// App config
#[inline] #[inline]
pub fn app_config(&self) -> &AppConfig { pub fn app_config(&self) -> &AppConfig {
&self.config &self.0.config
} }
/// Get an application data stored with `App::data()` method during /// Get an application data stored with `App::data()` method during
/// application configuration. /// application configuration.
pub fn app_data<T: 'static>(&self) -> Option<Data<T>> { pub fn app_data<T: 'static>(&self) -> Option<Data<T>> {
if let Some(st) = self.config.extensions().get::<Data<T>>() { if let Some(st) = self.0.config.extensions().get::<Data<T>>() {
Some(st.clone()) Some(st.clone())
} else { } else {
None None
@ -178,7 +195,7 @@ impl HttpRequest {
/// Load route data. Route data could be set during /// Load route data. Route data could be set during
/// route configuration with `Route::data()` method. /// route configuration with `Route::data()` method.
pub fn route_data<T: 'static>(&self) -> Option<&RouteData<T>> { pub fn route_data<T: 'static>(&self) -> Option<&RouteData<T>> {
if let Some(ref ext) = self.route_data { if let Some(ref ext) = self.0.route_data {
ext.get::<RouteData<T>>() ext.get::<RouteData<T>>()
} else { } else {
None None
@ -186,7 +203,7 @@ impl HttpRequest {
} }
pub(crate) fn set_route_data(&mut self, data: Option<Rc<Extensions>>) { pub(crate) fn set_route_data(&mut self, data: Option<Rc<Extensions>>) {
self.route_data = data; Rc::get_mut(&mut self.0).unwrap().route_data = data;
} }
} }
@ -202,13 +219,13 @@ impl HttpMessage for HttpRequest {
/// Request extensions /// Request extensions
#[inline] #[inline]
fn extensions(&self) -> Ref<Extensions> { fn extensions(&self) -> Ref<Extensions> {
self.head.extensions() self.0.head.extensions()
} }
/// Mutable reference to a the request's extensions /// Mutable reference to a the request's extensions
#[inline] #[inline]
fn extensions_mut(&self) -> RefMut<Extensions> { fn extensions_mut(&self) -> RefMut<Extensions> {
self.head.extensions_mut() self.0.head.extensions_mut()
} }
#[inline] #[inline]
@ -217,6 +234,17 @@ impl HttpMessage for HttpRequest {
} }
} }
impl Drop for HttpRequest {
fn drop(&mut self) {
if Rc::strong_count(&self.0) == 1 {
let v = &mut self.0.pool.0.borrow_mut();
if v.len() < 128 {
v.push(self.0.clone());
}
}
}
}
/// It is possible to get `HttpRequest` as an extractor handler parameter /// It is possible to get `HttpRequest` as an extractor handler parameter
/// ///
/// ## Example /// ## Example
@ -252,8 +280,8 @@ impl fmt::Debug for HttpRequest {
writeln!( writeln!(
f, f,
"\nHttpRequest {:?} {}:{}", "\nHttpRequest {:?} {}:{}",
self.head.version, self.0.head.version,
self.head.method, self.0.head.method,
self.path() self.path()
)?; )?;
if !self.query_string().is_empty() { if !self.query_string().is_empty() {
@ -270,6 +298,26 @@ impl fmt::Debug for HttpRequest {
} }
} }
/// Request's objects pool
pub(crate) struct HttpRequestPool(RefCell<Vec<Rc<HttpRequestInner>>>);
impl HttpRequestPool {
pub(crate) fn create() -> &'static HttpRequestPool {
let pool = HttpRequestPool(RefCell::new(Vec::with_capacity(128)));
Box::leak(Box::new(pool))
}
/// Get message from the pool
#[inline]
pub(crate) fn get_request(&self) -> Option<HttpRequest> {
if let Some(inner) = self.0.borrow_mut().pop() {
Some(HttpRequest(inner))
} else {
None
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -487,11 +487,8 @@ impl<P> Service for ResourceService<P> {
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Either< type Future = Either<
FutureResult<ServiceResponse, Error>,
Box<Future<Item = ServiceResponse, Error = Error>>, Box<Future<Item = ServiceResponse, Error = Error>>,
Either<
Box<Future<Item = Self::Response, Error = Self::Error>>,
FutureResult<Self::Response, Self::Error>,
>,
>; >;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@ -501,17 +498,17 @@ impl<P> Service for ResourceService<P> {
fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future { fn call(&mut self, mut req: ServiceRequest<P>) -> Self::Future {
for route in self.routes.iter_mut() { for route in self.routes.iter_mut() {
if route.check(&mut req) { if route.check(&mut req) {
return Either::A(route.call(req)); return route.call(req);
} }
} }
if let Some(ref mut default) = self.default { if let Some(ref mut default) = self.default {
Either::B(Either::A(default.call(req))) default.call(req)
} else { } else {
let req = req.into_parts().0; let req = req.into_parts().0;
Either::B(Either::B(ok(ServiceResponse::new( Either::A(ok(ServiceResponse::new(
req, req,
Response::MethodNotAllowed().finish(), Response::MethodNotAllowed().finish(),
)))) )))
} }
} }
} }

View File

@ -4,6 +4,7 @@ use std::rc::Rc;
use actix_http::{http::Method, Error, Extensions, Response}; use actix_http::{http::Method, Error, Extensions, Response};
use actix_service::{NewService, Service}; use actix_service::{NewService, Service};
use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, IntoFuture, Poll}; use futures::{Async, Future, IntoFuture, Poll};
use crate::data::RouteData; use crate::data::RouteData;
@ -19,7 +20,10 @@ type BoxedRouteService<Req, Res> = Box<
Request = Req, Request = Req,
Response = Res, Response = Res,
Error = Error, Error = Error,
Future = Box<Future<Item = Res, Error = Error>>, Future = Either<
FutureResult<Res, Error>,
Box<Future<Item = Res, Error = Error>>,
>,
>, >,
>; >;
@ -50,11 +54,10 @@ impl<P: 'static> Route<P> {
pub fn new() -> Route<P> { pub fn new() -> Route<P> {
let data_ref = Rc::new(RefCell::new(None)); let data_ref = Rc::new(RefCell::new(None));
Route { Route {
service: Box::new(RouteNewService::new( service: Box::new(RouteNewService::new(Extract::new(
Extract::new(data_ref.clone()).and_then( data_ref.clone(),
Handler::new(HttpResponse::NotFound).map_err(|_| panic!()), Handler::new(|| HttpResponse::NotFound()),
), ))),
)),
guards: Rc::new(Vec::new()), guards: Rc::new(Vec::new()),
data: None, data: None,
data_ref, data_ref,
@ -131,7 +134,10 @@ impl<P> Service for RouteService<P> {
type Request = ServiceRequest<P>; type Request = ServiceRequest<P>;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>; type Future = Either<
FutureResult<Self::Response, Self::Error>,
Box<Future<Item = Self::Response, Error = Self::Error>>,
>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready() self.service.poll_ready()
@ -235,10 +241,10 @@ impl<P: 'static> Route<P> {
T: FromRequest<P> + 'static, T: FromRequest<P> + 'static,
R: Responder + 'static, R: Responder + 'static,
{ {
self.service = Box::new(RouteNewService::new( self.service = Box::new(RouteNewService::new(Extract::new(
Extract::new(self.data_ref.clone()) self.data_ref.clone(),
.and_then(Handler::new(handler).map_err(|_| panic!())), Handler::new(handler),
)); )));
self self
} }
@ -277,10 +283,10 @@ impl<P: 'static> Route<P> {
R::Item: Into<Response>, R::Item: Into<Response>,
R::Error: Into<Error>, R::Error: Into<Error>,
{ {
self.service = Box::new(RouteNewService::new( self.service = Box::new(RouteNewService::new(Extract::new(
Extract::new(self.data_ref.clone()) self.data_ref.clone(),
.and_then(AsyncHandler::new(handler).map_err(|_| panic!())), AsyncHandler::new(handler),
)); )));
self self
} }
@ -394,17 +400,25 @@ where
type Request = ServiceRequest<P>; type Request = ServiceRequest<P>;
type Response = ServiceResponse; type Response = ServiceResponse;
type Error = Error; type Error = Error;
type Future = Box<Future<Item = Self::Response, Error = Self::Error>>; type Future = Either<
FutureResult<Self::Response, Self::Error>,
Box<Future<Item = Self::Response, Error = Self::Error>>,
>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> { fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready().map_err(|(e, _)| e) self.service.poll_ready().map_err(|(e, _)| e)
} }
fn call(&mut self, req: ServiceRequest<P>) -> Self::Future { fn call(&mut self, req: ServiceRequest<P>) -> Self::Future {
Box::new(self.service.call(req).then(|res| match res { let mut fut = self.service.call(req);
match fut.poll() {
Ok(Async::Ready(res)) => Either::A(ok(res)),
Err((e, req)) => Either::A(ok(req.error_response(e))),
Ok(Async::NotReady) => Either::B(Box::new(fut.then(|res| match res {
Ok(res) => Ok(res), Ok(res) => Ok(res),
Err((err, req)) => Ok(req.error_response(err)), Err((err, req)) => Ok(req.error_response(err)),
})) }))),
}
} }
} }

View File

@ -24,7 +24,10 @@ type Guards = Vec<Box<Guard>>;
type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, Error>; type HttpService<P> = BoxedService<ServiceRequest<P>, ServiceResponse, Error>;
type HttpNewService<P> = type HttpNewService<P> =
BoxedNewService<(), ServiceRequest<P>, ServiceResponse, Error, ()>; BoxedNewService<(), ServiceRequest<P>, ServiceResponse, Error, ()>;
type BoxedResponse = Box<Future<Item = ServiceResponse, Error = Error>>; type BoxedResponse = Either<
FutureResult<ServiceResponse, Error>,
Box<Future<Item = ServiceResponse, Error = Error>>,
>;
/// Resources scope. /// Resources scope.
/// ///

View File

@ -1,13 +1,12 @@
use std::cell::{Ref, RefMut}; use std::cell::{Ref, RefMut};
use std::fmt; use std::fmt;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::rc::Rc;
use actix_http::body::{Body, MessageBody, ResponseBody}; use actix_http::body::{Body, MessageBody, ResponseBody};
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version}; use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
use actix_http::{ use actix_http::{
Error, Extensions, HttpMessage, Payload, PayloadStream, Request, RequestHead, Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response,
Response, ResponseHead, ResponseHead,
}; };
use actix_router::{Path, Resource, Url}; use actix_router::{Path, Resource, Url};
use futures::future::{ok, FutureResult, IntoFuture}; use futures::future::{ok, FutureResult, IntoFuture};
@ -15,7 +14,6 @@ use futures::future::{ok, FutureResult, IntoFuture};
use crate::config::{AppConfig, ServiceConfig}; use crate::config::{AppConfig, ServiceConfig};
use crate::data::Data; use crate::data::Data;
use crate::request::HttpRequest; use crate::request::HttpRequest;
use crate::rmap::ResourceMap;
pub trait HttpServiceFactory<P> { pub trait HttpServiceFactory<P> {
fn register(self, config: &mut ServiceConfig<P>); fn register(self, config: &mut ServiceConfig<P>);
@ -56,19 +54,6 @@ pub struct ServiceRequest<P = PayloadStream> {
} }
impl<P> ServiceRequest<P> { impl<P> ServiceRequest<P> {
pub(crate) fn new(
path: Path<Url>,
request: Request<P>,
rmap: Rc<ResourceMap>,
config: AppConfig,
) -> Self {
let (head, payload) = request.into_parts();
ServiceRequest {
payload,
req: HttpRequest::new(head, path, rmap, config),
}
}
/// Construct service request from parts /// Construct service request from parts
pub fn from_parts(req: HttpRequest, payload: Payload<P>) -> Self { pub fn from_parts(req: HttpRequest, payload: Payload<P>) -> Self {
ServiceRequest { req, payload } ServiceRequest { req, payload }
@ -95,13 +80,13 @@ impl<P> ServiceRequest<P> {
/// This method returns reference to the request head /// This method returns reference to the request head
#[inline] #[inline]
pub fn head(&self) -> &RequestHead { pub fn head(&self) -> &RequestHead {
&self.req.head &self.req.head()
} }
/// This method returns reference to the request head /// This method returns reference to the request head
#[inline] #[inline]
pub fn head_mut(&mut self) -> &mut RequestHead { pub fn head_mut(&mut self) -> &mut RequestHead {
&mut self.req.head self.req.head_mut()
} }
/// Request's uri. /// Request's uri.
@ -160,12 +145,12 @@ impl<P> ServiceRequest<P> {
/// access the matched value for that segment. /// access the matched value for that segment.
#[inline] #[inline]
pub fn match_info(&self) -> &Path<Url> { pub fn match_info(&self) -> &Path<Url> {
&self.req.path self.req.match_info()
} }
#[inline] #[inline]
pub fn match_info_mut(&mut self) -> &mut Path<Url> { pub fn match_info_mut(&mut self) -> &mut Path<Url> {
&mut self.req.path self.req.match_info_mut()
} }
/// Service configuration /// Service configuration
@ -203,13 +188,13 @@ impl<P> HttpMessage for ServiceRequest<P> {
/// Request extensions /// Request extensions
#[inline] #[inline]
fn extensions(&self) -> Ref<Extensions> { fn extensions(&self) -> Ref<Extensions> {
self.req.head.extensions() self.req.extensions()
} }
/// Mutable reference to a the request's extensions /// Mutable reference to a the request's extensions
#[inline] #[inline]
fn extensions_mut(&self) -> RefMut<Extensions> { fn extensions_mut(&self) -> RefMut<Extensions> {
self.req.head.extensions_mut() self.req.extensions_mut()
} }
#[inline] #[inline]

View File

@ -17,6 +17,7 @@ use futures::future::{lazy, Future};
use crate::config::{AppConfig, AppConfigInner}; use crate::config::{AppConfig, AppConfigInner};
use crate::data::RouteData; use crate::data::RouteData;
use crate::dev::{Body, Payload}; use crate::dev::{Body, Payload};
use crate::request::HttpRequestPool;
use crate::rmap::ResourceMap; use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse}; use crate::service::{ServiceRequest, ServiceResponse};
use crate::{Error, HttpRequest, HttpResponse}; use crate::{Error, HttpRequest, HttpResponse};
@ -326,14 +327,17 @@ impl TestRequest {
/// Complete request creation and generate `ServiceRequest` instance /// Complete request creation and generate `ServiceRequest` instance
pub fn to_srv_request(mut self) -> ServiceRequest<PayloadStream> { pub fn to_srv_request(mut self) -> ServiceRequest<PayloadStream> {
let req = self.req.finish(); let (head, payload) = self.req.finish().into_parts();
ServiceRequest::new( let req = HttpRequest::new(
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(head.uri.clone())),
req, head,
Rc::new(self.rmap), Rc::new(self.rmap),
AppConfig::new(self.config), AppConfig::new(self.config),
) HttpRequestPool::create(),
);
ServiceRequest::from_parts(req, payload)
} }
/// Complete request creation and generate `ServiceResponse` instance /// Complete request creation and generate `ServiceResponse` instance
@ -343,34 +347,32 @@ impl TestRequest {
/// Complete request creation and generate `HttpRequest` instance /// Complete request creation and generate `HttpRequest` instance
pub fn to_http_request(mut self) -> HttpRequest { pub fn to_http_request(mut self) -> HttpRequest {
let req = self.req.finish(); let (head, _) = self.req.finish().into_parts();
let mut req = ServiceRequest::new( let mut req = HttpRequest::new(
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(head.uri.clone())),
req, head,
Rc::new(self.rmap), Rc::new(self.rmap),
AppConfig::new(self.config), AppConfig::new(self.config),
) HttpRequestPool::create(),
.into_parts() );
.0;
req.set_route_data(Some(Rc::new(self.route_data))); req.set_route_data(Some(Rc::new(self.route_data)));
req req
} }
/// Complete request creation and generate `HttpRequest` and `Payload` instances /// Complete request creation and generate `HttpRequest` and `Payload` instances
pub fn to_http_parts(mut self) -> (HttpRequest, Payload) { pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
let req = self.req.finish(); let (head, payload) = self.req.finish().into_parts();
let (mut req, pl) = ServiceRequest::new( let mut req = HttpRequest::new(
Path::new(Url::new(req.uri().clone())), Path::new(Url::new(head.uri.clone())),
req, head,
Rc::new(self.rmap), Rc::new(self.rmap),
AppConfig::new(self.config), AppConfig::new(self.config),
) HttpRequestPool::create(),
.into_parts(); );
req.set_route_data(Some(Rc::new(self.route_data))); req.set_route_data(Some(Rc::new(self.route_data)));
(req, pl) (req, payload)
} }
/// Runs the provided future, blocking the current thread until the future /// Runs the provided future, blocking the current thread until the future