split up response module

This commit is contained in:
Rob Ede 2021-12-19 06:30:09 +00:00
parent 6134cbe25c
commit ccdab92eca
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
8 changed files with 241 additions and 228 deletions

View File

@ -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<ConnectionType>);
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>);
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<ConnectionType>) {
if let Some(ctype) = ctype {
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
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<ConnectionType>) {
if let Some(ctype) = ctype {
fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
if let Some(ctype) = conn_type {
ResponseHead::set_connection_type(self, ctype);
}
}

View File

@ -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! {

View File

@ -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};

View File

@ -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<Self>) -> R;
}
#[derive(Debug)]
pub struct ResponseHead {
pub version: Version,
pub status: StatusCode,
pub headers: HeaderMap,
pub reason: Option<&'static str>,
pub(crate) extensions: RefCell<Extensions>,
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("<unknown status code>")
})
}
#[inline]
pub(crate) fn conn_type(&self) -> Option<ConnectionType> {
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<T: Head> {
/// 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<T: Head> Drop for Message<T> {
}
}
pub(crate) struct BoxedResponseHead {
head: Option<Box<ResponseHead>>,
}
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<T: Head>(RefCell<Vec<Rc<T>>>);
@ -266,41 +103,3 @@ impl<T: Head> MessagePool<T> {
}
}
}
/// Response object caching pool.
#[doc(hidden)]
pub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell<Vec<Box<ResponseHead>>>);
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<ResponseHead>) {
let v = &mut self.0.borrow_mut();
if v.len() < 128 {
msg.extensions.get_mut().clear();
v.push(msg);
}
}
}

View File

@ -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.

View File

@ -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<Extensions>,
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("<unknown status code>")
})
}
#[inline]
pub(crate) fn conn_type(&self) -> Option<ConnectionType> {
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<Box<ResponseHead>>,
}
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<Vec<Box<ResponseHead>>>);
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<ResponseHead>) {
let pool = &mut self.0.borrow_mut();
if pool.len() < 128 {
msg.extensions.get_mut().clear();
pool.push(msg);
}
}
}

View File

@ -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;

View File

@ -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.