diff --git a/actix-http/src/h1/decoder.rs b/actix-http/src/h1/decoder.rs index 64bb84e82..3d3a3ceac 100644 --- a/actix-http/src/h1/decoder.rs +++ b/actix-http/src/h1/decoder.rs @@ -2,14 +2,14 @@ use std::{convert::TryFrom, io, marker::PhantomData, mem::MaybeUninit, task::Pol use actix_codec::Decoder; use bytes::{Bytes, BytesMut}; -use http::header::{HeaderName, HeaderValue}; -use http::{header, Method, StatusCode, Uri, Version}; +use http::{ + header::{self, HeaderName, HeaderValue}, + Method, StatusCode, Uri, Version, +}; use log::{debug, error, trace}; use super::chunked::ChunkedState; -use crate::{ - error::ParseError, header::HeaderMap, message::ResponseHead, ConnectionType, Request, -}; +use crate::{error::ParseError, header::HeaderMap, ConnectionType, Request, ResponseHead}; pub(crate) const MAX_BUFFER_SIZE: usize = 131_072; const MAX_HEADERS: usize = 96; @@ -47,7 +47,7 @@ pub(crate) enum PayloadLength { } pub(crate) trait MessageType: Sized { - fn set_connection_type(&mut self, ctype: Option); + fn set_connection_type(&mut self, conn_type: Option); fn set_expect(&mut self); @@ -190,8 +190,8 @@ pub(crate) trait MessageType: Sized { } impl MessageType for Request { - fn set_connection_type(&mut self, ctype: Option) { - if let Some(ctype) = ctype { + fn set_connection_type(&mut self, conn_type: Option) { + if let Some(ctype) = conn_type { self.head_mut().set_connection_type(ctype); } } @@ -275,8 +275,8 @@ impl MessageType for Request { } impl MessageType for ResponseHead { - fn set_connection_type(&mut self, ctype: Option) { - if let Some(ctype) = ctype { + fn set_connection_type(&mut self, conn_type: Option) { + if let Some(ctype) = conn_type { ResponseHead::set_connection_type(self, ctype); } } diff --git a/actix-http/src/h1/utils.rs b/actix-http/src/h1/utils.rs index c8d79f0cd..131c7f1ed 100644 --- a/actix-http/src/h1/utils.rs +++ b/actix-http/src/h1/utils.rs @@ -9,9 +9,8 @@ use pin_project_lite::pin_project; use crate::{ body::{BodySize, MessageBody}, - error::Error, h1::{Codec, Message}, - response::Response, + Error, Response, }; pin_project! { diff --git a/actix-http/src/lib.rs b/actix-http/src/lib.rs index 9f873aeec..ae822a055 100644 --- a/actix-http/src/lib.rs +++ b/actix-http/src/lib.rs @@ -31,23 +31,20 @@ extern crate log; pub mod body; mod builder; mod config; - #[cfg(feature = "__compress")] pub mod encoding; +pub mod error; mod extensions; +pub mod h1; +pub mod h2; pub mod header; mod helpers; mod http_message; mod message; mod payload; mod requests; -mod response; -mod response_builder; +mod responses; mod service; - -pub mod error; -pub mod h1; -pub mod h2; pub mod test; pub mod ws; @@ -58,11 +55,10 @@ pub use self::extensions::Extensions; pub use self::header::ContentEncoding; pub use self::http_message::HttpMessage; pub use self::message::ConnectionType; -pub use self::message::{Message, ResponseHead}; +pub use self::message::Message; pub use self::payload::{Payload, PayloadStream}; pub use self::requests::{Request, RequestHead, RequestHeadType}; -pub use self::response::Response; -pub use self::response_builder::ResponseBuilder; +pub use self::responses::{Response, ResponseBuilder, ResponseHead}; pub use self::service::HttpService; pub use ::http::{uri, uri::Uri}; diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index 161482c91..2692a4bee 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -1,12 +1,7 @@ -use std::{ - cell::{Ref, RefCell, RefMut}, - rc::Rc, -}; +use std::{cell::RefCell, rc::Rc}; use bitflags::bitflags; -use crate::{header::HeaderMap, Extensions, StatusCode, Version}; - /// Represents various types of connection #[derive(Copy, Clone, PartialEq, Debug)] pub enum ConnectionType { @@ -40,131 +35,6 @@ pub trait Head: Default + 'static { F: FnOnce(&MessagePool) -> R; } -#[derive(Debug)] -pub struct ResponseHead { - pub version: Version, - pub status: StatusCode, - pub headers: HeaderMap, - pub reason: Option<&'static str>, - pub(crate) extensions: RefCell, - flags: Flags, -} - -impl ResponseHead { - /// Create new instance of `ResponseHead` type - #[inline] - pub fn new(status: StatusCode) -> ResponseHead { - ResponseHead { - status, - version: Version::default(), - headers: HeaderMap::with_capacity(12), - reason: None, - flags: Flags::empty(), - extensions: RefCell::new(Extensions::new()), - } - } - - /// Message extensions - #[inline] - pub fn extensions(&self) -> Ref<'_, Extensions> { - self.extensions.borrow() - } - - /// Mutable reference to a the message's extensions - #[inline] - pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.extensions.borrow_mut() - } - - #[inline] - /// Read the message headers. - pub fn headers(&self) -> &HeaderMap { - &self.headers - } - - #[inline] - /// Mutable reference to the message headers. - pub fn headers_mut(&mut self) -> &mut HeaderMap { - &mut self.headers - } - - #[inline] - /// Set connection type of the message - pub fn set_connection_type(&mut self, ctype: ConnectionType) { - match ctype { - ConnectionType::Close => self.flags.insert(Flags::CLOSE), - ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE), - ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE), - } - } - - #[inline] - pub fn connection_type(&self) -> ConnectionType { - if self.flags.contains(Flags::CLOSE) { - ConnectionType::Close - } else if self.flags.contains(Flags::KEEP_ALIVE) { - ConnectionType::KeepAlive - } else if self.flags.contains(Flags::UPGRADE) { - ConnectionType::Upgrade - } else if self.version < Version::HTTP_11 { - ConnectionType::Close - } else { - ConnectionType::KeepAlive - } - } - - /// Check if keep-alive is enabled - #[inline] - pub fn keep_alive(&self) -> bool { - self.connection_type() == ConnectionType::KeepAlive - } - - /// Check upgrade status of this message - #[inline] - pub fn upgrade(&self) -> bool { - self.connection_type() == ConnectionType::Upgrade - } - - /// Get custom reason for the response - #[inline] - pub fn reason(&self) -> &str { - self.reason.unwrap_or_else(|| { - self.status - .canonical_reason() - .unwrap_or("") - }) - } - - #[inline] - pub(crate) fn conn_type(&self) -> Option { - if self.flags.contains(Flags::CLOSE) { - Some(ConnectionType::Close) - } else if self.flags.contains(Flags::KEEP_ALIVE) { - Some(ConnectionType::KeepAlive) - } else if self.flags.contains(Flags::UPGRADE) { - Some(ConnectionType::Upgrade) - } else { - None - } - } - - #[inline] - /// Get response body chunking state - pub fn chunked(&self) -> bool { - !self.flags.contains(Flags::NO_CHUNKING) - } - - #[inline] - /// Set no chunking for payload - pub fn no_chunking(&mut self, val: bool) { - if val { - self.flags.insert(Flags::NO_CHUNKING); - } else { - self.flags.remove(Flags::NO_CHUNKING); - } - } -} - pub struct Message { /// Rc here should not be cloned by anyone. /// It's used to reuse allocation of T and no shared ownership is allowed. @@ -198,39 +68,6 @@ impl Drop for Message { } } -pub(crate) struct BoxedResponseHead { - head: Option>, -} - -impl BoxedResponseHead { - /// Get new message from the pool of objects - pub fn new(status: StatusCode) -> Self { - RESPONSE_POOL.with(|p| p.get_message(status)) - } -} - -impl std::ops::Deref for BoxedResponseHead { - type Target = ResponseHead; - - fn deref(&self) -> &Self::Target { - self.head.as_ref().unwrap() - } -} - -impl std::ops::DerefMut for BoxedResponseHead { - fn deref_mut(&mut self) -> &mut Self::Target { - self.head.as_mut().unwrap() - } -} - -impl Drop for BoxedResponseHead { - fn drop(&mut self) { - if let Some(head) = self.head.take() { - RESPONSE_POOL.with(move |p| p.release(head)) - } - } -} - #[doc(hidden)] /// Request's objects pool pub struct MessagePool(RefCell>>); @@ -266,41 +103,3 @@ impl MessagePool { } } } - -/// Response object caching pool. -#[doc(hidden)] -pub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell>>); - -thread_local!(static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create()); - -impl BoxedResponsePool { - fn create() -> BoxedResponsePool { - BoxedResponsePool(RefCell::new(Vec::with_capacity(128))) - } - - /// Get message from the pool - #[inline] - fn get_message(&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::new(ResponseHead::new(status))), - } - } - } - - #[inline] - /// Release request instance - fn release(&self, mut msg: Box) { - let v = &mut self.0.borrow_mut(); - if v.len() < 128 { - msg.extensions.get_mut().clear(); - v.push(msg); - } - } -} diff --git a/actix-http/src/response_builder.rs b/actix-http/src/responses/builder.rs similarity index 99% rename from actix-http/src/response_builder.rs rename to actix-http/src/responses/builder.rs index adbe86fca..5854863de 100644 --- a/actix-http/src/response_builder.rs +++ b/actix-http/src/responses/builder.rs @@ -9,8 +9,8 @@ use crate::{ body::{EitherBody, MessageBody}, error::{Error, HttpError}, header::{self, TryIntoHeaderPair, TryIntoHeaderValue}, - message::{BoxedResponseHead, ConnectionType, ResponseHead}, - Extensions, Response, StatusCode, + responses::{BoxedResponseHead, ResponseHead}, + ConnectionType, Extensions, Response, StatusCode, }; /// An HTTP response builder. diff --git a/actix-http/src/responses/head.rs b/actix-http/src/responses/head.rs new file mode 100644 index 000000000..78d9536e5 --- /dev/null +++ b/actix-http/src/responses/head.rs @@ -0,0 +1,208 @@ +//! Response head type and caching pool. + +use std::{ + cell::{Ref, RefCell, RefMut}, + ops, +}; + +use crate::{ + header::HeaderMap, message::Flags, ConnectionType, Extensions, StatusCode, Version, +}; + +thread_local! { + static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create(); +} + +#[derive(Debug)] +pub struct ResponseHead { + pub version: Version, + pub status: StatusCode, + pub headers: HeaderMap, + pub reason: Option<&'static str>, + pub(crate) extensions: RefCell, + flags: Flags, +} + +impl ResponseHead { + /// Create new instance of `ResponseHead` type + #[inline] + pub fn new(status: StatusCode) -> ResponseHead { + ResponseHead { + status, + version: Version::HTTP_11, + headers: HeaderMap::with_capacity(12), + reason: None, + flags: Flags::empty(), + extensions: RefCell::new(Extensions::new()), + } + } + + #[inline] + /// Read the message headers. + pub fn headers(&self) -> &HeaderMap { + &self.headers + } + + #[inline] + /// Mutable reference to the message headers. + pub fn headers_mut(&mut self) -> &mut HeaderMap { + &mut self.headers + } + + /// Message extensions + #[inline] + pub fn extensions(&self) -> Ref<'_, Extensions> { + self.extensions.borrow() + } + + /// Mutable reference to a the message's extensions + #[inline] + pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { + self.extensions.borrow_mut() + } + + #[inline] + /// Set connection type of the message + pub fn set_connection_type(&mut self, ctype: ConnectionType) { + match ctype { + ConnectionType::Close => self.flags.insert(Flags::CLOSE), + ConnectionType::KeepAlive => self.flags.insert(Flags::KEEP_ALIVE), + ConnectionType::Upgrade => self.flags.insert(Flags::UPGRADE), + } + } + + #[inline] + pub fn connection_type(&self) -> ConnectionType { + if self.flags.contains(Flags::CLOSE) { + ConnectionType::Close + } else if self.flags.contains(Flags::KEEP_ALIVE) { + ConnectionType::KeepAlive + } else if self.flags.contains(Flags::UPGRADE) { + ConnectionType::Upgrade + } else if self.version < Version::HTTP_11 { + ConnectionType::Close + } else { + ConnectionType::KeepAlive + } + } + + /// Check if keep-alive is enabled + #[inline] + pub fn keep_alive(&self) -> bool { + self.connection_type() == ConnectionType::KeepAlive + } + + /// Check upgrade status of this message + #[inline] + pub fn upgrade(&self) -> bool { + self.connection_type() == ConnectionType::Upgrade + } + + /// Get custom reason for the response + #[inline] + pub fn reason(&self) -> &str { + self.reason.unwrap_or_else(|| { + self.status + .canonical_reason() + .unwrap_or("") + }) + } + + #[inline] + pub(crate) fn conn_type(&self) -> Option { + if self.flags.contains(Flags::CLOSE) { + Some(ConnectionType::Close) + } else if self.flags.contains(Flags::KEEP_ALIVE) { + Some(ConnectionType::KeepAlive) + } else if self.flags.contains(Flags::UPGRADE) { + Some(ConnectionType::Upgrade) + } else { + None + } + } + + #[inline] + /// Get response body chunking state + pub fn chunked(&self) -> bool { + !self.flags.contains(Flags::NO_CHUNKING) + } + + #[inline] + /// Set no chunking for payload + pub fn no_chunking(&mut self, val: bool) { + if val { + self.flags.insert(Flags::NO_CHUNKING); + } else { + self.flags.remove(Flags::NO_CHUNKING); + } + } +} + +pub(crate) struct BoxedResponseHead { + head: Option>, +} + +impl BoxedResponseHead { + /// Get new message from the pool of objects + pub fn new(status: StatusCode) -> Self { + RESPONSE_POOL.with(|p| p.get_message(status)) + } +} + +impl ops::Deref for BoxedResponseHead { + type Target = ResponseHead; + + fn deref(&self) -> &Self::Target { + self.head.as_ref().unwrap() + } +} + +impl ops::DerefMut for BoxedResponseHead { + fn deref_mut(&mut self) -> &mut Self::Target { + self.head.as_mut().unwrap() + } +} + +impl Drop for BoxedResponseHead { + fn drop(&mut self) { + if let Some(head) = self.head.take() { + RESPONSE_POOL.with(move |p| p.release(head)) + } + } +} + +/// Request's objects pool +#[doc(hidden)] +pub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell>>); + +impl BoxedResponsePool { + fn create() -> BoxedResponsePool { + BoxedResponsePool(RefCell::new(Vec::with_capacity(128))) + } + + /// Get message from the pool + #[inline] + fn get_message(&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::new(ResponseHead::new(status))), + } + } + } + + /// Release request instance + #[inline] + fn release(&self, mut msg: Box) { + let pool = &mut self.0.borrow_mut(); + if pool.len() < 128 { + msg.extensions.get_mut().clear(); + pool.push(msg); + } + } +} diff --git a/actix-http/src/responses/mod.rs b/actix-http/src/responses/mod.rs new file mode 100644 index 000000000..899232b9f --- /dev/null +++ b/actix-http/src/responses/mod.rs @@ -0,0 +1,11 @@ +//! HTTP response. + +mod builder; +mod head; +#[allow(clippy::module_inception)] +mod response; + +pub use self::builder::ResponseBuilder; +pub(crate) use self::head::BoxedResponseHead; +pub use self::head::ResponseHead; +pub use self::response::Response; diff --git a/actix-http/src/response.rs b/actix-http/src/responses/response.rs similarity index 99% rename from actix-http/src/response.rs rename to actix-http/src/responses/response.rs index a0e6d9b7c..d781bdfaa 100644 --- a/actix-http/src/response.rs +++ b/actix-http/src/responses/response.rs @@ -12,8 +12,8 @@ use crate::{ body::{BoxBody, MessageBody}, extensions::Extensions, header::{self, HeaderMap, TryIntoHeaderValue}, - message::{BoxedResponseHead, ResponseHead}, - Error, ResponseBuilder, StatusCode, + responses::{BoxedResponseHead, ResponseBuilder, ResponseHead}, + Error, StatusCode, }; /// An HTTP response.