diff --git a/Cargo.toml b/Cargo.toml
index 86a168093..8318ada5a 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -68,9 +68,9 @@ rust-tls = ["rustls", "actix-server/rust-tls"]
[dependencies]
actix-codec = "0.1.1"
-actix-service = "0.3.4"
+actix-service = "0.3.6"
actix-utils = "0.3.4"
-actix-router = "0.1.1"
+actix-router = "0.1.2"
actix-rt = "0.2.2"
actix-web-codegen = "0.1.0-alpha.1"
actix-http = { version = "0.1.0-alpha.3", features=["fail"] }
@@ -124,4 +124,4 @@ actix-web-codegen = { path = "actix-web-codegen" }
actix-web-actors = { path = "actix-web-actors" }
actix-session = { path = "actix-session" }
actix-files = { path = "actix-files" }
-awc = { path = "awc" }
+awc = { path = "awc" }
\ No newline at end of file
diff --git a/actix-files/src/lib.rs b/actix-files/src/lib.rs
index 6820d3622..e8eb8afda 100644
--- a/actix-files/src/lib.rs
+++ b/actix-files/src/lib.rs
@@ -423,7 +423,7 @@ impl
FilesService
{
> {
log::debug!("Files: Failed to handle {}: {}", req.path(), e);
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 {
Either::A(ok(ServiceResponse::from_err(e, req.clone())))
}
@@ -955,6 +955,7 @@ mod tests {
.method(Method::POST)
.to_http_request();
let resp = file.respond_to(&req).unwrap();
+ println!("RES: {:?}", resp);
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
let file = NamedFile::open("Cargo.toml").unwrap();
diff --git a/actix-http/src/client/h2proto.rs b/actix-http/src/client/h2proto.rs
index 222e442c5..da70a878e 100644
--- a/actix-http/src/client/h2proto.rs
+++ b/actix-http/src/client/h2proto.rs
@@ -104,9 +104,8 @@ where
let (parts, body) = resp.into_parts();
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.status = parts.status;
head.headers = parts.headers.into();
Ok((head, payload))
diff --git a/actix-http/src/client/pool.rs b/actix-http/src/client/pool.rs
index aff11181b..2d1785381 100644
--- a/actix-http/src/client/pool.rs
+++ b/actix-http/src/client/pool.rs
@@ -21,6 +21,7 @@ use tokio_timer::{sleep, Delay};
use super::connection::{ConnectionType, IoConnection};
use super::error::ConnectError;
+#[allow(dead_code)]
#[derive(Clone, Copy, PartialEq)]
pub enum Protocol {
Http1,
diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs
index 417441c6a..411649fc1 100644
--- a/actix-http/src/h1/decoder.rs
+++ b/actix-http/src/h1/decoder.rs
@@ -73,78 +73,75 @@ pub(crate) trait MessageType: Sized {
let headers = self.headers_mut();
for idx in raw_headers.iter() {
- if let Ok(name) = HeaderName::from_bytes(&slice[idx.name.0..idx.name.1])
- {
- // Unsafe: httparse check header value for valid utf-8
- let value = unsafe {
- HeaderValue::from_shared_unchecked(
- slice.slice(idx.value.0, idx.value.1),
- )
- };
- match name {
- header::CONTENT_LENGTH => {
- if let Ok(s) = value.to_str() {
- if let Ok(len) = s.parse::() {
- if len != 0 {
- content_length = Some(len);
- }
- } else {
- debug!("illegal Content-Length: {:?}", s);
- return Err(ParseError::Header);
+ let name =
+ HeaderName::from_bytes(&slice[idx.name.0..idx.name.1]).unwrap();
+
+ // Unsafe: httparse check header value for valid utf-8
+ let value = unsafe {
+ HeaderValue::from_shared_unchecked(
+ slice.slice(idx.value.0, idx.value.1),
+ )
+ };
+ match name {
+ header::CONTENT_LENGTH => {
+ if let Ok(s) = value.to_str() {
+ if let Ok(len) = s.parse::() {
+ if len != 0 {
+ content_length = Some(len);
}
} else {
- debug!("illegal Content-Length: {:?}", value);
+ debug!("illegal Content-Length: {:?}", s);
return Err(ParseError::Header);
}
+ } else {
+ debug!("illegal Content-Length: {:?}", value);
+ return Err(ParseError::Header);
}
- // transfer-encoding
- header::TRANSFER_ENCODING => {
- if let Ok(s) = value.to_str().map(|s| s.trim()) {
- chunked = s.eq_ignore_ascii_case("chunked");
- } else {
- return Err(ParseError::Header);
- }
+ }
+ // transfer-encoding
+ header::TRANSFER_ENCODING => {
+ if let Ok(s) = value.to_str().map(|s| s.trim()) {
+ chunked = s.eq_ignore_ascii_case("chunked");
+ } else {
+ return Err(ParseError::Header);
}
- // connection keep-alive state
- header::CONNECTION => {
- ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim())
- {
- if conn.eq_ignore_ascii_case("keep-alive") {
- Some(ConnectionType::KeepAlive)
- } else if conn.eq_ignore_ascii_case("close") {
- Some(ConnectionType::Close)
- } else if conn.eq_ignore_ascii_case("upgrade") {
- Some(ConnectionType::Upgrade)
- } else {
- None
- }
+ }
+ // connection keep-alive state
+ header::CONNECTION => {
+ ka = if let Ok(conn) = value.to_str().map(|conn| conn.trim()) {
+ if conn.eq_ignore_ascii_case("keep-alive") {
+ Some(ConnectionType::KeepAlive)
+ } else if conn.eq_ignore_ascii_case("close") {
+ Some(ConnectionType::Close)
+ } else if conn.eq_ignore_ascii_case("upgrade") {
+ Some(ConnectionType::Upgrade)
} else {
None
- };
- }
- header::UPGRADE => {
- has_upgrade = true;
- // check content-length, some clients (dart)
- // sends "content-length: 0" with websocket upgrade
- if let Ok(val) = value.to_str().map(|val| val.trim()) {
- if val.eq_ignore_ascii_case("websocket") {
- content_length = None;
- }
}
- }
- header::EXPECT => {
- let bytes = value.as_bytes();
- if bytes.len() >= 4 && &bytes[0..4] == b"100-" {
- expect = true;
- }
- }
- _ => (),
+ } else {
+ None
+ };
}
-
- headers.append(name, value);
- } else {
- return Err(ParseError::Header);
+ header::UPGRADE => {
+ has_upgrade = true;
+ // check content-length, some clients (dart)
+ // sends "content-length: 0" with websocket upgrade
+ if let Ok(val) = value.to_str().map(|val| val.trim()) {
+ if val.eq_ignore_ascii_case("websocket") {
+ content_length = None;
+ }
+ }
+ }
+ header::EXPECT => {
+ let bytes = value.as_bytes();
+ if bytes.len() >= 4 && &bytes[0..4] == b"100-" {
+ expect = true;
+ }
+ }
+ _ => (),
}
+
+ headers.append(name, value);
}
}
self.set_connection_type(ka);
@@ -217,10 +214,10 @@ impl MessageType for Request {
let mut msg = Request::new();
// 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
- let decoder = match len {
+ let decoder = match length {
PayloadLength::Payload(pl) => pl,
PayloadLength::Upgrade => {
// 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
- 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
- let decoder = if let PayloadLength::Payload(pl) = len {
+ let decoder = if let PayloadLength::Payload(pl) = length {
pl
} else if status == StatusCode::SWITCHING_PROTOCOLS {
// switching protocol or connect
@@ -305,9 +303,6 @@ impl MessageType for ResponseHead {
PayloadType::None
};
- msg.status = status;
- msg.version = ver;
-
Ok(Some((msg, decoder)))
}
}
diff --git a/actix-http/src/h1/dispatcher.rs b/actix-http/src/h1/dispatcher.rs
index bff05ab5c..a223161f9 100644
--- a/actix-http/src/h1/dispatcher.rs
+++ b/actix-http/src/h1/dispatcher.rs
@@ -218,7 +218,7 @@ where
{
fn can_read(&self) -> bool {
if self.flags.contains(Flags::READ_DISCONNECT) {
- return false;
+ false
} else if let Some(ref info) = self.payload {
info.need_read() == PayloadStatus::Read
} else {
diff --git a/actix-http/src/httpcodes.rs b/actix-http/src/httpcodes.rs
index 5dfeefa9e..85c384374 100644
--- a/actix-http/src/httpcodes.rs
+++ b/actix-http/src/httpcodes.rs
@@ -8,7 +8,7 @@ macro_rules! STATIC_RESP {
($name:ident, $status:expr) => {
#[allow(non_snake_case, missing_docs)]
pub fn $name() -> ResponseBuilder {
- Response::build($status)
+ ResponseBuilder::new($status)
}
};
}
diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs
index 5879e1915..5af802601 100644
--- a/actix-http/src/lib.rs
+++ b/actix-http/src/lib.rs
@@ -1,5 +1,9 @@
//! 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]
extern crate log;
diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs
index e1fb3a111..61ca5161e 100644
--- a/actix-http/src/message.rs
+++ b/actix-http/src/message.rs
@@ -1,5 +1,4 @@
use std::cell::{Ref, RefCell, RefMut};
-use std::collections::VecDeque;
use std::rc::Rc;
use bitflags::bitflags;
@@ -171,20 +170,20 @@ pub struct ResponseHead {
flags: Flags,
}
-impl Default for ResponseHead {
- fn default() -> ResponseHead {
+impl ResponseHead {
+ /// Create new instance of `ResponseHead` type
+ #[inline]
+ pub fn new(status: StatusCode) -> ResponseHead {
ResponseHead {
+ status,
version: Version::default(),
- status: StatusCode::OK,
headers: HeaderMap::with_capacity(12),
reason: None,
flags: Flags::empty(),
extensions: RefCell::new(Extensions::new()),
}
}
-}
-impl ResponseHead {
/// Message extensions
#[inline]
pub fn extensions(&self) -> Ref {
@@ -335,8 +334,8 @@ pub(crate) struct BoxedResponseHead {
impl BoxedResponseHead {
/// Get new message from the pool of objects
- pub fn new() -> Self {
- RESPONSE_POOL.with(|p| p.get_message())
+ pub fn new(status: StatusCode) -> Self {
+ RESPONSE_POOL.with(|p| p.get_message(status))
}
}
@@ -362,25 +361,25 @@ impl Drop for BoxedResponseHead {
#[doc(hidden)]
/// Request's objects pool
-pub struct MessagePool(RefCell>>);
+pub struct MessagePool(RefCell>>);
#[doc(hidden)]
/// Request's objects pool
-pub struct BoxedResponsePool(RefCell>>);
+pub struct BoxedResponsePool(RefCell>>);
thread_local!(static REQUEST_POOL: &'static MessagePool = MessagePool::::create());
thread_local!(static RESPONSE_POOL: &'static BoxedResponsePool = BoxedResponsePool::create());
impl MessagePool {
fn create() -> &'static MessagePool {
- let pool = MessagePool(RefCell::new(VecDeque::with_capacity(128)));
+ let pool = MessagePool(RefCell::new(Vec::with_capacity(128)));
Box::leak(Box::new(pool))
}
/// Get message from the pool
#[inline]
fn get_message(&'static self) -> Message {
- 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) {
r.clear();
}
@@ -397,28 +396,29 @@ impl MessagePool {
fn release(&self, msg: Rc) {
let v = &mut self.0.borrow_mut();
if v.len() < 128 {
- v.push_front(msg);
+ v.push(msg);
}
}
}
impl 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))
}
/// Get message from the pool
#[inline]
- fn get_message(&'static self) -> BoxedResponseHead {
- if let Some(mut head) = self.0.borrow_mut().pop_front() {
+ fn get_message(&'static self, status: StatusCode) -> BoxedResponseHead {
+ if let Some(mut head) = self.0.borrow_mut().pop() {
head.reason = None;
+ head.status = status;
head.headers.clear();
head.flags = Flags::empty();
BoxedResponseHead { head: Some(head) }
} else {
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) {
let v = &mut self.0.borrow_mut();
if v.len() < 128 {
- v.push_front(msg);
+ v.push(msg);
}
}
}
diff --git a/actix-http/src/response.rs b/actix-http/src/response.rs
index 707c9af63..330d33a45 100644
--- a/actix-http/src/response.rs
+++ b/actix-http/src/response.rs
@@ -41,11 +41,8 @@ impl Response {
/// Constructs a response
#[inline]
pub fn new(status: StatusCode) -> Response {
- let mut head = BoxedResponseHead::new();
- head.status = status;
-
Response {
- head,
+ head: BoxedResponseHead::new(status),
body: ResponseBody::Body(Body::Empty),
error: None,
}
@@ -78,6 +75,16 @@ impl Response {
}
impl Response {
+ /// Constructs a response with body
+ #[inline]
+ pub fn with_body(status: StatusCode, body: B) -> Response {
+ Response {
+ head: BoxedResponseHead::new(status),
+ body: ResponseBody::Body(body),
+ error: None,
+ }
+ }
+
#[inline]
/// Http message part of the response
pub fn head(&self) -> &ResponseHead {
@@ -90,18 +97,6 @@ impl Response {
&mut *self.head
}
- /// Constructs a response with body
- #[inline]
- pub fn with_body(status: StatusCode, body: B) -> Response {
- let mut head = BoxedResponseHead::new();
- head.status = status;
- Response {
- head,
- body: ResponseBody::Body(body),
- error: None,
- }
- }
-
/// The source `error` for this response
#[inline]
pub fn error(&self) -> Option<&Error> {
@@ -325,13 +320,11 @@ pub struct ResponseBuilder {
}
impl ResponseBuilder {
+ #[inline]
/// Create response builder
pub fn new(status: StatusCode) -> Self {
- let mut head = BoxedResponseHead::new();
- head.status = status;
-
ResponseBuilder {
- head: Some(head),
+ head: Some(BoxedResponseHead::new(status)),
err: None,
cookies: None,
}
@@ -555,15 +548,13 @@ impl ResponseBuilder {
/// }
/// ```
pub fn del_cookie<'a>(&mut self, cookie: &Cookie<'a>) -> &mut Self {
- {
- if self.cookies.is_none() {
- self.cookies = Some(CookieJar::new())
- }
- let jar = self.cookies.as_mut().unwrap();
- let cookie = cookie.clone().into_owned();
- jar.add_original(cookie.clone());
- jar.remove(cookie);
+ if self.cookies.is_none() {
+ self.cookies = Some(CookieJar::new())
}
+ let jar = self.cookies.as_mut().unwrap();
+ let cookie = cookie.clone().into_owned();
+ jar.add_original(cookie.clone());
+ jar.remove(cookie);
self
}
@@ -605,6 +596,7 @@ impl ResponseBuilder {
head.extensions.borrow_mut()
}
+ #[inline]
/// Set a body and generate `Response`.
///
/// `ResponseBuilder` can not be used after this call.
@@ -625,9 +617,7 @@ impl ResponseBuilder {
if let Some(ref jar) = self.cookies {
for cookie in jar.delta() {
match HeaderValue::from_str(&cookie.to_string()) {
- Ok(val) => {
- let _ = response.headers.append(header::SET_COOKIE, val);
- }
+ Ok(val) => response.headers.append(header::SET_COOKIE, val),
Err(e) => return Response::from(Error::from(e)).into_body(),
};
}
@@ -652,6 +642,7 @@ impl ResponseBuilder {
self.body(Body::from_message(BodyStream::new(stream)))
}
+ #[inline]
/// Set a json body and generate `Response`
///
/// `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.status = head.status;
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());
ResponseBuilder {
diff --git a/actix-http/src/test.rs b/actix-http/src/test.rs
index 302d75d73..2c5dc502b 100644
--- a/actix-http/src/test.rs
+++ b/actix-http/src/test.rs
@@ -178,6 +178,6 @@ impl TestRequest {
}
#[inline]
-fn parts<'a>(parts: &'a mut Option) -> &'a mut Inner {
+fn parts(parts: &mut Option) -> &mut Inner {
parts.as_mut().expect("cannot reuse test request builder")
}
diff --git a/awc/src/test.rs b/awc/src/test.rs
index 1c772905e..fbbadef3a 100644
--- a/awc/src/test.rs
+++ b/awc/src/test.rs
@@ -3,7 +3,7 @@ use std::fmt::Write as FmtWrite;
use actix_http::cookie::{Cookie, CookieJar};
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 bytes::Bytes;
#[cfg(test)]
@@ -49,7 +49,7 @@ pub struct TestResponse {
impl Default for TestResponse {
fn default() -> TestResponse {
TestResponse {
- head: ResponseHead::default(),
+ head: ResponseHead::new(StatusCode::OK),
cookies: CookieJar::new(),
payload: None,
}
diff --git a/src/app_service.rs b/src/app_service.rs
index 236eed9f9..593fbe673 100644
--- a/src/app_service.rs
+++ b/src/app_service.rs
@@ -14,6 +14,7 @@ use crate::config::{AppConfig, ServiceConfig};
use crate::data::{DataFactory, DataFactoryResult};
use crate::error::Error;
use crate::guard::Guard;
+use crate::request::{HttpRequest, HttpRequestPool};
use crate::rmap::ResourceMap;
use crate::service::{ServiceFactory, ServiceRequest, ServiceResponse};
@@ -21,7 +22,10 @@ type Guards = Vec>;
type HttpService = BoxedService, ServiceResponse, Error>;
type HttpNewService =
BoxedNewService<(), ServiceRequest
, ServiceResponse, Error, ()>;
-type BoxedResponse = Box>;
+type BoxedResponse = Either<
+ FutureResult,
+ Box>,
+>;
/// Service factory to convert `Request` to a `ServiceRequest`.
/// It also executes data factories.
@@ -191,6 +195,7 @@ where
chain: self.chain.take().unwrap(),
rmap: self.rmap.clone(),
config: self.config.clone(),
+ pool: HttpRequestPool::create(),
}
.and_then(self.endpoint.take().unwrap()),
))
@@ -208,6 +213,7 @@ where
chain: C,
rmap: Rc,
config: AppConfig,
+ pool: &'static HttpRequestPool,
}
impl Service for AppInitService
@@ -224,13 +230,24 @@ where
}
fn call(&mut self, req: Request) -> Self::Future {
- let req = ServiceRequest::new(
- Path::new(Url::new(req.uri().clone())),
- req,
- self.rmap.clone(),
- self.config.clone(),
- );
- self.chain.call(req)
+ let (head, payload) = req.into_parts();
+
+ 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.config.clone(),
+ self.pool,
+ )
+ };
+ self.chain.call(ServiceRequest::from_parts(req, payload))
}
}
@@ -353,7 +370,7 @@ impl Service for AppRouting
{
type Request = ServiceRequest
;
type Response = ServiceResponse;
type Error = Error;
- type Future = Either>;
+ type Future = BoxedResponse;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
if self.ready.is_none() {
@@ -376,12 +393,12 @@ impl Service for AppRouting
{
});
if let Some((srv, _info)) = res {
- Either::A(srv.call(req))
+ srv.call(req)
} else if let Some(ref mut default) = self.default {
- Either::A(default.call(req))
+ default.call(req)
} else {
let req = req.into_parts().0;
- Either::B(ok(ServiceResponse::new(req, Response::NotFound().finish())))
+ Either::A(ok(ServiceResponse::new(req, Response::NotFound().finish())))
}
}
}
diff --git a/src/handler.rs b/src/handler.rs
index 921b8334d..42a9d88d7 100644
--- a/src/handler.rs
+++ b/src/handler.rs
@@ -52,37 +52,21 @@ where
}
}
}
-impl NewService for Handler
+
+impl Clone for Handler
where
F: Factory,
R: Responder,
{
- type Request = (T, HttpRequest);
- type Response = ServiceResponse;
- type Error = Void;
- type InitError = ();
- type Service = HandlerService;
- type Future = FutureResult;
-
- fn new_service(&self, _: &()) -> Self::Future {
- ok(HandlerService {
+ fn clone(&self) -> Self {
+ Self {
hnd: self.hnd.clone(),
_t: PhantomData,
- })
+ }
}
}
-#[doc(hidden)]
-pub struct HandlerService
-where
- F: Factory,
- R: Responder,
-{
- hnd: F,
- _t: PhantomData<(T, R)>,
-}
-
-impl Service for HandlerService
+impl Service for Handler
where
F: Factory,
R: Responder,
@@ -184,41 +168,23 @@ where
}
}
}
-impl NewService for AsyncHandler
+
+impl Clone for AsyncHandler
where
F: AsyncFactory,
R: IntoFuture,
R::Item: Into,
R::Error: Into,
{
- type Request = (T, HttpRequest);
- type Response = ServiceResponse;
- type Error = Error;
- type InitError = ();
- type Service = AsyncHandlerService;
- type Future = FutureResult;
-
- fn new_service(&self, _: &()) -> Self::Future {
- ok(AsyncHandlerService {
+ fn clone(&self) -> Self {
+ AsyncHandler {
hnd: self.hnd.clone(),
_t: PhantomData,
- })
+ }
}
}
-#[doc(hidden)]
-pub struct AsyncHandlerService
-where
- F: AsyncFactory,
- R: IntoFuture,
- R::Item: Into,
- R::Error: Into,
-{
- hnd: F,
- _t: PhantomData<(T, R)>,
-}
-
-impl Service for AsyncHandlerService
+impl Service for AsyncHandler
where
F: AsyncFactory,
R: IntoFuture,
@@ -227,7 +193,7 @@ where
{
type Request = (T, HttpRequest);
type Response = ServiceResponse;
- type Error = Error;
+ type Error = Void;
type Future = AsyncHandlerServiceResponse;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@@ -255,7 +221,7 @@ where
T::Error: Into,
{
type Item = ServiceResponse;
- type Error = Error;
+ type Error = Void;
fn poll(&mut self) -> Poll {
match self.fut.poll() {
@@ -276,46 +242,58 @@ where
}
/// Extract arguments from request
-pub struct Extract> {
+pub struct Extract
, S> {
config: Rc>>>,
+ service: S,
_t: PhantomData<(P, T)>,
}
-impl> Extract
{
- pub fn new(config: Rc>>>) -> Self {
+impl, S> Extract
{
+ pub fn new(config: Rc>>>, service: S) -> Self {
Extract {
config,
+ service,
_t: PhantomData,
}
}
}
-impl> NewService for Extract
{
+impl
, S> NewService for Extract
+where
+ S: Service
+ + Clone,
+{
type Request = ServiceRequest;
- type Response = (T, HttpRequest);
+ type Response = ServiceResponse;
type Error = (Error, ServiceRequest
);
type InitError = ();
- type Service = ExtractService
;
+ type Service = ExtractService
;
type Future = FutureResult;
fn new_service(&self, _: &()) -> Self::Future {
ok(ExtractService {
_t: PhantomData,
config: self.config.borrow().clone(),
+ service: self.service.clone(),
})
}
}
-pub struct ExtractService> {
+pub struct ExtractService
, S> {
config: Option>,
+ service: S,
_t: PhantomData<(P, T)>,
}
-impl> Service for ExtractService
{
+impl
, S> Service for ExtractService
+where
+ S: Service
+ + Clone,
+{
type Request = ServiceRequest;
- type Response = (T, HttpRequest);
+ type Response = ServiceResponse;
type Error = (Error, ServiceRequest
);
- type Future = ExtractResponse
;
+ type Future = ExtractResponse
;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
Ok(Async::Ready(()))
@@ -328,28 +306,40 @@ impl
> Service for ExtractService
{
ExtractResponse {
fut,
+ fut_s: None,
req: Some((req, payload)),
+ service: self.service.clone(),
}
}
}
-pub struct ExtractResponse
> {
+pub struct ExtractResponse
, S: Service> {
req: Option<(HttpRequest, Payload
)>,
+ service: S,
fut: ::Future,
+ fut_s: Option,
}
-impl> Future for ExtractResponse
{
- type Item = (T, HttpRequest);
+impl
, S> Future for ExtractResponse
+where
+ S: Service,
+{
+ type Item = ServiceResponse;
type Error = (Error, ServiceRequest);
fn poll(&mut self) -> Poll {
+ 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 (req, payload) = self.req.take().unwrap();
let req = ServiceRequest::from_parts(req, payload);
(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()
}
}
diff --git a/src/request.rs b/src/request.rs
index ff38c879c..53d848f0d 100644
--- a/src/request.rs
+++ b/src/request.rs
@@ -1,4 +1,4 @@
-use std::cell::{Ref, RefMut};
+use std::cell::{Ref, RefCell, RefMut};
use std::fmt;
use std::rc::Rc;
@@ -15,29 +15,34 @@ use crate::rmap::ResourceMap;
#[derive(Clone)]
/// An HTTP Request
-pub struct HttpRequest {
+pub struct HttpRequest(pub(crate) Rc);
+
+pub(crate) struct HttpRequestInner {
pub(crate) head: Message,
pub(crate) path: Path,
rmap: Rc,
config: AppConfig,
route_data: Option>,
+ pool: &'static HttpRequestPool,
}
impl HttpRequest {
#[inline]
pub(crate) fn new(
- head: Message,
path: Path,
+ head: Message,
rmap: Rc,
config: AppConfig,
+ pool: &'static HttpRequestPool,
) -> HttpRequest {
- HttpRequest {
+ HttpRequest(Rc::new(HttpRequestInner {
head,
path,
rmap,
config,
+ pool,
route_data: None,
- }
+ }))
}
}
@@ -45,7 +50,14 @@ impl HttpRequest {
/// This method returns reference to the request head
#[inline]
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.
@@ -98,7 +110,12 @@ impl HttpRequest {
/// access the matched value for that segment.
#[inline]
pub fn match_info(&self) -> &Path {
- &self.path
+ &self.0.path
+ }
+
+ #[inline]
+ pub(crate) fn match_info_mut(&mut self) -> &mut Path {
+ &mut Rc::get_mut(&mut self.0).unwrap().path
}
/// Request extensions
@@ -141,7 +158,7 @@ impl HttpRequest {
U: IntoIterator- ,
I: AsRef,
{
- self.rmap.url_for(&self, name, elements)
+ self.0.rmap.url_for(&self, name, elements)
}
/// Generate url for named resource
@@ -162,13 +179,13 @@ impl HttpRequest {
/// App config
#[inline]
pub fn app_config(&self) -> &AppConfig {
- &self.config
+ &self.0.config
}
/// Get an application data stored with `App::data()` method during
/// application configuration.
pub fn app_data(&self) -> Option> {
- if let Some(st) = self.config.extensions().get::>() {
+ if let Some(st) = self.0.config.extensions().get::>() {
Some(st.clone())
} else {
None
@@ -178,7 +195,7 @@ impl HttpRequest {
/// Load route data. Route data could be set during
/// route configuration with `Route::data()` method.
pub fn route_data(&self) -> Option<&RouteData> {
- if let Some(ref ext) = self.route_data {
+ if let Some(ref ext) = self.0.route_data {
ext.get::>()
} else {
None
@@ -186,7 +203,7 @@ impl HttpRequest {
}
pub(crate) fn set_route_data(&mut self, data: Option>) {
- self.route_data = data;
+ Rc::get_mut(&mut self.0).unwrap().route_data = data;
}
}
@@ -202,13 +219,13 @@ impl HttpMessage for HttpRequest {
/// Request extensions
#[inline]
fn extensions(&self) -> Ref {
- self.head.extensions()
+ self.0.head.extensions()
}
/// Mutable reference to a the request's extensions
#[inline]
fn extensions_mut(&self) -> RefMut {
- self.head.extensions_mut()
+ self.0.head.extensions_mut()
}
#[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
///
/// ## Example
@@ -252,8 +280,8 @@ impl fmt::Debug for HttpRequest {
writeln!(
f,
"\nHttpRequest {:?} {}:{}",
- self.head.version,
- self.head.method,
+ self.0.head.version,
+ self.0.head.method,
self.path()
)?;
if !self.query_string().is_empty() {
@@ -270,6 +298,26 @@ impl fmt::Debug for HttpRequest {
}
}
+/// Request's objects pool
+pub(crate) struct HttpRequestPool(RefCell>>);
+
+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 {
+ if let Some(inner) = self.0.borrow_mut().pop() {
+ Some(HttpRequest(inner))
+ } else {
+ None
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/src/resource.rs b/src/resource.rs
index 957795cd7..313a3bc06 100644
--- a/src/resource.rs
+++ b/src/resource.rs
@@ -487,11 +487,8 @@ impl
Service for ResourceService
{
type Response = ServiceResponse;
type Error = Error;
type Future = Either<
+ FutureResult,
Box>,
- Either<
- Box>,
- FutureResult,
- >,
>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
@@ -501,17 +498,17 @@ impl Service for ResourceService
{
fn call(&mut self, mut req: ServiceRequest
) -> Self::Future {
for route in self.routes.iter_mut() {
if route.check(&mut req) {
- return Either::A(route.call(req));
+ return route.call(req);
}
}
if let Some(ref mut default) = self.default {
- Either::B(Either::A(default.call(req)))
+ default.call(req)
} else {
let req = req.into_parts().0;
- Either::B(Either::B(ok(ServiceResponse::new(
+ Either::A(ok(ServiceResponse::new(
req,
Response::MethodNotAllowed().finish(),
- ))))
+ )))
}
}
}
diff --git a/src/route.rs b/src/route.rs
index 349668ef4..8bff863fe 100644
--- a/src/route.rs
+++ b/src/route.rs
@@ -4,6 +4,7 @@ use std::rc::Rc;
use actix_http::{http::Method, Error, Extensions, Response};
use actix_service::{NewService, Service};
+use futures::future::{ok, Either, FutureResult};
use futures::{Async, Future, IntoFuture, Poll};
use crate::data::RouteData;
@@ -19,7 +20,10 @@ type BoxedRouteService = Box<
Request = Req,
Response = Res,
Error = Error,
- Future = Box>,
+ Future = Either<
+ FutureResult,
+ Box>,
+ >,
>,
>;
@@ -50,11 +54,10 @@ impl Route {
pub fn new() -> Route
{
let data_ref = Rc::new(RefCell::new(None));
Route {
- service: Box::new(RouteNewService::new(
- Extract::new(data_ref.clone()).and_then(
- Handler::new(HttpResponse::NotFound).map_err(|_| panic!()),
- ),
- )),
+ service: Box::new(RouteNewService::new(Extract::new(
+ data_ref.clone(),
+ Handler::new(|| HttpResponse::NotFound()),
+ ))),
guards: Rc::new(Vec::new()),
data: None,
data_ref,
@@ -131,7 +134,10 @@ impl
Service for RouteService
{
type Request = ServiceRequest
;
type Response = ServiceResponse;
type Error = Error;
- type Future = Box>;
+ type Future = Either<
+ FutureResult,
+ Box>,
+ >;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready()
@@ -235,10 +241,10 @@ impl Route {
T: FromRequest
+ 'static,
R: Responder + 'static,
{
- self.service = Box::new(RouteNewService::new(
- Extract::new(self.data_ref.clone())
- .and_then(Handler::new(handler).map_err(|_| panic!())),
- ));
+ self.service = Box::new(RouteNewService::new(Extract::new(
+ self.data_ref.clone(),
+ Handler::new(handler),
+ )));
self
}
@@ -277,10 +283,10 @@ impl Route {
R::Item: Into,
R::Error: Into,
{
- self.service = Box::new(RouteNewService::new(
- Extract::new(self.data_ref.clone())
- .and_then(AsyncHandler::new(handler).map_err(|_| panic!())),
- ));
+ self.service = Box::new(RouteNewService::new(Extract::new(
+ self.data_ref.clone(),
+ AsyncHandler::new(handler),
+ )));
self
}
@@ -394,17 +400,25 @@ where
type Request = ServiceRequest;
type Response = ServiceResponse;
type Error = Error;
- type Future = Box>;
+ type Future = Either<
+ FutureResult,
+ Box>,
+ >;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.service.poll_ready().map_err(|(e, _)| e)
}
fn call(&mut self, req: ServiceRequest) -> Self::Future {
- Box::new(self.service.call(req).then(|res| match res {
- Ok(res) => Ok(res),
- Err((err, req)) => Ok(req.error_response(err)),
- }))
+ 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),
+ Err((err, req)) => Ok(req.error_response(err)),
+ }))),
+ }
}
}
diff --git a/src/scope.rs b/src/scope.rs
index 7ad2d95eb..2cb01961a 100644
--- a/src/scope.rs
+++ b/src/scope.rs
@@ -24,7 +24,10 @@ type Guards = Vec>;
type HttpService = BoxedService, ServiceResponse, Error>;
type HttpNewService =
BoxedNewService<(), ServiceRequest
, ServiceResponse, Error, ()>;
-type BoxedResponse = Box>;
+type BoxedResponse = Either<
+ FutureResult,
+ Box>,
+>;
/// Resources scope.
///
diff --git a/src/service.rs b/src/service.rs
index 13eea9d14..f0ff02158 100644
--- a/src/service.rs
+++ b/src/service.rs
@@ -1,13 +1,12 @@
use std::cell::{Ref, RefMut};
use std::fmt;
use std::marker::PhantomData;
-use std::rc::Rc;
use actix_http::body::{Body, MessageBody, ResponseBody};
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
use actix_http::{
- Error, Extensions, HttpMessage, Payload, PayloadStream, Request, RequestHead,
- Response, ResponseHead,
+ Error, Extensions, HttpMessage, Payload, PayloadStream, RequestHead, Response,
+ ResponseHead,
};
use actix_router::{Path, Resource, Url};
use futures::future::{ok, FutureResult, IntoFuture};
@@ -15,7 +14,6 @@ use futures::future::{ok, FutureResult, IntoFuture};
use crate::config::{AppConfig, ServiceConfig};
use crate::data::Data;
use crate::request::HttpRequest;
-use crate::rmap::ResourceMap;
pub trait HttpServiceFactory {
fn register(self, config: &mut ServiceConfig
);
@@ -56,19 +54,6 @@ pub struct ServiceRequest
{
}
impl
ServiceRequest
{
- pub(crate) fn new(
- path: Path,
- request: Request,
- rmap: Rc,
- config: AppConfig,
- ) -> Self {
- let (head, payload) = request.into_parts();
- ServiceRequest {
- payload,
- req: HttpRequest::new(head, path, rmap, config),
- }
- }
-
/// Construct service request from parts
pub fn from_parts(req: HttpRequest, payload: Payload) -> Self {
ServiceRequest { req, payload }
@@ -95,13 +80,13 @@ impl
ServiceRequest
{
/// This method returns reference to the request head
#[inline]
pub fn head(&self) -> &RequestHead {
- &self.req.head
+ &self.req.head()
}
/// This method returns reference to the request head
#[inline]
pub fn head_mut(&mut self) -> &mut RequestHead {
- &mut self.req.head
+ self.req.head_mut()
}
/// Request's uri.
@@ -160,12 +145,12 @@ impl
ServiceRequest
{
/// access the matched value for that segment.
#[inline]
pub fn match_info(&self) -> &Path {
- &self.req.path
+ self.req.match_info()
}
#[inline]
pub fn match_info_mut(&mut self) -> &mut Path {
- &mut self.req.path
+ self.req.match_info_mut()
}
/// Service configuration
@@ -203,13 +188,13 @@ impl HttpMessage for ServiceRequest
{
/// Request extensions
#[inline]
fn extensions(&self) -> Ref {
- self.req.head.extensions()
+ self.req.extensions()
}
/// Mutable reference to a the request's extensions
#[inline]
fn extensions_mut(&self) -> RefMut {
- self.req.head.extensions_mut()
+ self.req.extensions_mut()
}
#[inline]
diff --git a/src/test.rs b/src/test.rs
index 58cb1211e..5444726e1 100644
--- a/src/test.rs
+++ b/src/test.rs
@@ -17,6 +17,7 @@ use futures::future::{lazy, Future};
use crate::config::{AppConfig, AppConfigInner};
use crate::data::RouteData;
use crate::dev::{Body, Payload};
+use crate::request::HttpRequestPool;
use crate::rmap::ResourceMap;
use crate::service::{ServiceRequest, ServiceResponse};
use crate::{Error, HttpRequest, HttpResponse};
@@ -326,14 +327,17 @@ impl TestRequest {
/// Complete request creation and generate `ServiceRequest` instance
pub fn to_srv_request(mut self) -> ServiceRequest {
- let req = self.req.finish();
+ let (head, payload) = self.req.finish().into_parts();
- ServiceRequest::new(
- Path::new(Url::new(req.uri().clone())),
- req,
+ let req = HttpRequest::new(
+ Path::new(Url::new(head.uri.clone())),
+ head,
Rc::new(self.rmap),
AppConfig::new(self.config),
- )
+ HttpRequestPool::create(),
+ );
+
+ ServiceRequest::from_parts(req, payload)
}
/// Complete request creation and generate `ServiceResponse` instance
@@ -343,34 +347,32 @@ impl TestRequest {
/// Complete request creation and generate `HttpRequest` instance
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(
- Path::new(Url::new(req.uri().clone())),
- req,
+ let mut req = HttpRequest::new(
+ Path::new(Url::new(head.uri.clone())),
+ head,
Rc::new(self.rmap),
AppConfig::new(self.config),
- )
- .into_parts()
- .0;
+ HttpRequestPool::create(),
+ );
req.set_route_data(Some(Rc::new(self.route_data)));
req
}
/// Complete request creation and generate `HttpRequest` and `Payload` instances
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(
- Path::new(Url::new(req.uri().clone())),
- req,
+ let mut req = HttpRequest::new(
+ Path::new(Url::new(head.uri.clone())),
+ head,
Rc::new(self.rmap),
AppConfig::new(self.config),
- )
- .into_parts();
-
+ HttpRequestPool::create(),
+ );
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