1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

split request and response modules (#2530)

This commit is contained in:
Rob Ede 2021-12-19 17:05:27 +00:00 committed by GitHub
parent 2e00776d5e
commit 17f636a183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 467 additions and 450 deletions

View File

@ -1,8 +1,8 @@
# Changes # Changes
## Unreleased - 2021-xx-xx ## Unreleased - 2021-xx-xx
### Removed ### Changes
* `header::map::GetAll` iterator, its `Iterator::size_hint` method was wrongly implemented. Replaced with `std::slice::Iter`. [#2527] * `HeaderMap::get_all` now returns a `std::slice::Iter`. [#2527]
[#2527]: https://github.com/actix/actix-web/pull/2527 [#2527]: https://github.com/actix/actix-web/pull/2527

View File

@ -5,13 +5,15 @@ use bitflags::bitflags;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use http::{Method, Version}; use http::{Method, Version};
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; use super::{
use super::{decoder, encoder, reserve_readbuf}; decoder::{self, PayloadDecoder, PayloadItem, PayloadType},
use super::{Message, MessageType}; encoder, reserve_readbuf, Message, MessageType,
use crate::body::BodySize; };
use crate::config::ServiceConfig; use crate::{
use crate::error::{ParseError, PayloadError}; body::BodySize,
use crate::message::{ConnectionType, RequestHeadType, ResponseHead}; error::{ParseError, PayloadError},
ConnectionType, RequestHeadType, ResponseHead, ServiceConfig,
};
bitflags! { bitflags! {
struct Flags: u8 { struct Flags: u8 {

View File

@ -5,15 +5,13 @@ use bitflags::bitflags;
use bytes::BytesMut; use bytes::BytesMut;
use http::{Method, Version}; use http::{Method, Version};
use super::decoder::{PayloadDecoder, PayloadItem, PayloadType}; use super::{
use super::{decoder, encoder}; decoder::{self, PayloadDecoder, PayloadItem, PayloadType},
use super::{Message, MessageType}; encoder, Message, MessageType,
use crate::body::BodySize; };
use crate::config::ServiceConfig; use crate::{
use crate::error::ParseError; body::BodySize, error::ParseError, ConnectionType, Request, Response, ServiceConfig,
use crate::message::ConnectionType; };
use crate::request::Request;
use crate::response::Response;
bitflags! { bitflags! {
struct Flags: u8 { struct Flags: u8 {
@ -199,7 +197,7 @@ mod tests {
use http::Method; use http::Method;
use super::*; use super::*;
use crate::HttpMessage; use crate::HttpMessage as _;
#[actix_rt::test] #[actix_rt::test]
async fn test_http_request_chunked_payload_and_next_message() { async fn test_http_request_chunked_payload_and_next_message() {

View File

@ -2,17 +2,14 @@ use std::{convert::TryFrom, io, marker::PhantomData, mem::MaybeUninit, task::Pol
use actix_codec::Decoder; use actix_codec::Decoder;
use bytes::{Bytes, BytesMut}; use bytes::{Bytes, BytesMut};
use http::header::{HeaderName, HeaderValue}; use http::{
use http::{header, Method, StatusCode, Uri, Version}; header::{self, HeaderName, HeaderValue},
Method, StatusCode, Uri, Version,
};
use log::{debug, error, trace}; use log::{debug, error, trace};
use super::chunked::ChunkedState; use super::chunked::ChunkedState;
use crate::{ use crate::{error::ParseError, header::HeaderMap, ConnectionType, Request, ResponseHead};
error::ParseError,
header::HeaderMap,
message::{ConnectionType, ResponseHead},
request::Request,
};
pub(crate) const MAX_BUFFER_SIZE: usize = 131_072; pub(crate) const MAX_BUFFER_SIZE: usize = 131_072;
const MAX_HEADERS: usize = 96; const MAX_HEADERS: usize = 96;
@ -50,7 +47,7 @@ pub(crate) enum PayloadLength {
} }
pub(crate) trait MessageType: Sized { 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); fn set_expect(&mut self);
@ -193,8 +190,8 @@ pub(crate) trait MessageType: Sized {
} }
impl MessageType for Request { impl MessageType for Request {
fn set_connection_type(&mut self, ctype: Option<ConnectionType>) { fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
if let Some(ctype) = ctype { if let Some(ctype) = conn_type {
self.head_mut().set_connection_type(ctype); self.head_mut().set_connection_type(ctype);
} }
} }
@ -278,8 +275,8 @@ impl MessageType for Request {
} }
impl MessageType for ResponseHead { impl MessageType for ResponseHead {
fn set_connection_type(&mut self, ctype: Option<ConnectionType>) { fn set_connection_type(&mut self, conn_type: Option<ConnectionType>) {
if let Some(ctype) = ctype { if let Some(ctype) = conn_type {
ResponseHead::set_connection_type(self, ctype); ResponseHead::set_connection_type(self, ctype);
} }
} }

View File

@ -1,19 +1,19 @@
use std::io::Write; use std::{
use std::marker::PhantomData; cmp,
use std::ptr::copy_nonoverlapping; io::{self, Write as _},
use std::slice::from_raw_parts_mut; marker::PhantomData,
use std::{cmp, io}; ptr::copy_nonoverlapping,
slice::from_raw_parts_mut,
};
use bytes::{BufMut, BytesMut}; use bytes::{BufMut, BytesMut};
use crate::{ use crate::{
body::BodySize, body::BodySize,
config::ServiceConfig, header::{
header::{map::Value, HeaderMap, HeaderName}, map::Value, HeaderMap, HeaderName, CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING,
header::{CONNECTION, CONTENT_LENGTH, DATE, TRANSFER_ENCODING}, },
helpers, helpers, ConnectionType, RequestHeadType, Response, ServiceConfig, StatusCode, Version,
message::{ConnectionType, RequestHeadType},
Response, StatusCode, Version,
}; };
const AVERAGE_HEADER_SIZE: usize = 30; const AVERAGE_HEADER_SIZE: usize = 30;

View File

@ -1,8 +1,7 @@
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use actix_utils::future::{ready, Ready}; use actix_utils::future::{ready, Ready};
use crate::error::Error; use crate::{Error, Request};
use crate::request::Request;
pub struct ExpectHandler; pub struct ExpectHandler;

View File

@ -59,7 +59,7 @@ pub(crate) fn reserve_readbuf(src: &mut BytesMut) {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::request::Request; use crate::Request;
impl Message<Request> { impl Message<Request> {
pub fn message(self) -> Request { pub fn message(self) -> Request {

View File

@ -2,9 +2,7 @@ use actix_codec::Framed;
use actix_service::{Service, ServiceFactory}; use actix_service::{Service, ServiceFactory};
use futures_core::future::LocalBoxFuture; use futures_core::future::LocalBoxFuture;
use crate::error::Error; use crate::{h1::Codec, Error, Request};
use crate::h1::Codec;
use crate::request::Request;
pub struct UpgradeHandler; pub struct UpgradeHandler;

View File

@ -9,9 +9,8 @@ use pin_project_lite::pin_project;
use crate::{ use crate::{
body::{BodySize, MessageBody}, body::{BodySize, MessageBody},
error::Error,
h1::{Codec, Message}, h1::{Codec, Message},
response::Response, Error, Response,
}; };
pin_project! { pin_project! {

View File

@ -31,23 +31,20 @@ extern crate log;
pub mod body; pub mod body;
mod builder; mod builder;
mod config; mod config;
#[cfg(feature = "__compress")] #[cfg(feature = "__compress")]
pub mod encoding; pub mod encoding;
pub mod error;
mod extensions; mod extensions;
pub mod h1;
pub mod h2;
pub mod header; pub mod header;
mod helpers; mod helpers;
mod http_message; mod http_message;
mod message; mod message;
mod payload; mod payload;
mod request; mod requests;
mod response; mod responses;
mod response_builder;
mod service; mod service;
pub mod error;
pub mod h1;
pub mod h2;
pub mod test; pub mod test;
pub mod ws; pub mod ws;
@ -58,11 +55,10 @@ pub use self::extensions::Extensions;
pub use self::header::ContentEncoding; pub use self::header::ContentEncoding;
pub use self::http_message::HttpMessage; pub use self::http_message::HttpMessage;
pub use self::message::ConnectionType; pub use self::message::ConnectionType;
pub use self::message::{Message, RequestHead, RequestHeadType, ResponseHead}; pub use self::message::Message;
pub use self::payload::{Payload, PayloadStream}; pub use self::payload::{Payload, PayloadStream};
pub use self::request::Request; pub use self::requests::{Request, RequestHead, RequestHeadType};
pub use self::response::Response; pub use self::responses::{Response, ResponseBuilder, ResponseHead};
pub use self::response_builder::ResponseBuilder;
pub use self::service::HttpService; pub use self::service::HttpService;
pub use ::http::{uri, uri::Uri}; pub use ::http::{uri, uri::Uri};

View File

@ -1,16 +1,7 @@
use std::{ use std::{cell::RefCell, rc::Rc};
cell::{Ref, RefCell, RefMut},
net,
rc::Rc,
};
use bitflags::bitflags; use bitflags::bitflags;
use crate::{
header::{self, HeaderMap},
Extensions, Method, StatusCode, Uri, Version,
};
/// Represents various types of connection /// Represents various types of connection
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum ConnectionType { pub enum ConnectionType {
@ -44,294 +35,6 @@ pub trait Head: Default + 'static {
F: FnOnce(&MessagePool<Self>) -> R; F: FnOnce(&MessagePool<Self>) -> R;
} }
#[derive(Debug, Clone)]
pub struct RequestHead {
pub method: Method,
pub uri: Uri,
pub version: Version,
pub headers: HeaderMap,
pub peer_addr: Option<net::SocketAddr>,
flags: Flags,
}
impl Default for RequestHead {
fn default() -> RequestHead {
RequestHead {
method: Method::default(),
uri: Uri::default(),
version: Version::HTTP_11,
headers: HeaderMap::with_capacity(16),
peer_addr: None,
flags: Flags::empty(),
}
}
}
impl Head for RequestHead {
fn clear(&mut self) {
self.flags = Flags::empty();
self.headers.clear();
}
fn with_pool<F, R>(f: F) -> R
where
F: FnOnce(&MessagePool<Self>) -> R,
{
REQUEST_POOL.with(|p| f(p))
}
}
impl RequestHead {
/// Read the message headers.
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// Mutable reference to the message headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
/// Is to uppercase headers with Camel-Case.
/// Default is `false`
#[inline]
pub fn camel_case_headers(&self) -> bool {
self.flags.contains(Flags::CAMEL_CASE)
}
/// Set `true` to send headers which are formatted as Camel-Case.
#[inline]
pub fn set_camel_case_headers(&mut self, val: bool) {
if val {
self.flags.insert(Flags::CAMEL_CASE);
} else {
self.flags.remove(Flags::CAMEL_CASE);
}
}
#[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]
/// Connection type
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
}
}
/// Connection upgrade status
pub fn upgrade(&self) -> bool {
self.headers()
.get(header::CONNECTION)
.map(|hdr| {
if let Ok(s) = hdr.to_str() {
s.to_ascii_lowercase().contains("upgrade")
} else {
false
}
})
.unwrap_or(false)
}
#[inline]
/// Get response body chunking state
pub fn chunked(&self) -> bool {
!self.flags.contains(Flags::NO_CHUNKING)
}
#[inline]
pub fn no_chunking(&mut self, val: bool) {
if val {
self.flags.insert(Flags::NO_CHUNKING);
} else {
self.flags.remove(Flags::NO_CHUNKING);
}
}
#[inline]
/// Request contains `EXPECT` header
pub fn expect(&self) -> bool {
self.flags.contains(Flags::EXPECT)
}
#[inline]
pub(crate) fn set_expect(&mut self) {
self.flags.insert(Flags::EXPECT);
}
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum RequestHeadType {
Owned(RequestHead),
Rc(Rc<RequestHead>, Option<HeaderMap>),
}
impl RequestHeadType {
pub fn extra_headers(&self) -> Option<&HeaderMap> {
match self {
RequestHeadType::Owned(_) => None,
RequestHeadType::Rc(_, headers) => headers.as_ref(),
}
}
}
impl AsRef<RequestHead> for RequestHeadType {
fn as_ref(&self) -> &RequestHead {
match self {
RequestHeadType::Owned(head) => head,
RequestHeadType::Rc(head, _) => head.as_ref(),
}
}
}
impl From<RequestHead> for RequestHeadType {
fn from(head: RequestHead) -> Self {
RequestHeadType::Owned(head)
}
}
#[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> { pub struct Message<T: Head> {
/// Rc here should not be cloned by anyone. /// Rc here should not be cloned by anyone.
/// It's used to reuse allocation of T and no shared ownership is allowed. /// It's used to reuse allocation of T and no shared ownership is allowed.
@ -365,53 +68,12 @@ 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)] #[doc(hidden)]
/// Request's objects pool /// Request's objects pool
pub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>); pub struct MessagePool<T: Head>(RefCell<Vec<Rc<T>>>);
#[doc(hidden)]
#[allow(clippy::vec_box)]
/// Request's objects pool
pub struct BoxedResponsePool(RefCell<Vec<Box<ResponseHead>>>);
thread_local!(static REQUEST_POOL: MessagePool<RequestHead> = MessagePool::<RequestHead>::create());
thread_local!(static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create());
impl<T: Head> MessagePool<T> { impl<T: Head> MessagePool<T> {
fn create() -> MessagePool<T> { pub(crate) fn create() -> MessagePool<T> {
MessagePool(RefCell::new(Vec::with_capacity(128))) MessagePool(RefCell::new(Vec::with_capacity(128)))
} }
@ -433,43 +95,11 @@ impl<T: Head> MessagePool<T> {
} }
#[inline] #[inline]
/// Release request instance /// Release message instance
fn release(&self, msg: Rc<T>) { fn release(&self, msg: Rc<T>) {
let v = &mut self.0.borrow_mut(); let pool = &mut self.0.borrow_mut();
if v.len() < 128 { if pool.len() < 128 {
v.push(msg); pool.push(msg);
}
}
}
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

@ -7,6 +7,7 @@ use h2::RecvStream;
use crate::error::PayloadError; use crate::error::PayloadError;
// TODO: rename to boxed payload
/// A boxed payload. /// A boxed payload.
pub type PayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>; pub type PayloadStream = Pin<Box<dyn Stream<Item = Result<Bytes, PayloadError>>>>;

View File

@ -0,0 +1,174 @@
use std::{net, rc::Rc};
use crate::{
header::{self, HeaderMap},
message::{Flags, Head, MessagePool},
ConnectionType, Method, Uri, Version,
};
thread_local! {
static REQUEST_POOL: MessagePool<RequestHead> = MessagePool::<RequestHead>::create()
}
#[derive(Debug, Clone)]
pub struct RequestHead {
pub method: Method,
pub uri: Uri,
pub version: Version,
pub headers: HeaderMap,
pub peer_addr: Option<net::SocketAddr>,
flags: Flags,
}
impl Default for RequestHead {
fn default() -> RequestHead {
RequestHead {
method: Method::default(),
uri: Uri::default(),
version: Version::HTTP_11,
headers: HeaderMap::with_capacity(16),
peer_addr: None,
flags: Flags::empty(),
}
}
}
impl Head for RequestHead {
fn clear(&mut self) {
self.flags = Flags::empty();
self.headers.clear();
}
fn with_pool<F, R>(f: F) -> R
where
F: FnOnce(&MessagePool<Self>) -> R,
{
REQUEST_POOL.with(|p| f(p))
}
}
impl RequestHead {
/// Read the message headers.
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
/// Mutable reference to the message headers.
pub fn headers_mut(&mut self) -> &mut HeaderMap {
&mut self.headers
}
/// Is to uppercase headers with Camel-Case.
/// Default is `false`
#[inline]
pub fn camel_case_headers(&self) -> bool {
self.flags.contains(Flags::CAMEL_CASE)
}
/// Set `true` to send headers which are formatted as Camel-Case.
#[inline]
pub fn set_camel_case_headers(&mut self, val: bool) {
if val {
self.flags.insert(Flags::CAMEL_CASE);
} else {
self.flags.remove(Flags::CAMEL_CASE);
}
}
#[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]
/// Connection type
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
}
}
/// Connection upgrade status
pub fn upgrade(&self) -> bool {
self.headers()
.get(header::CONNECTION)
.map(|hdr| {
if let Ok(s) = hdr.to_str() {
s.to_ascii_lowercase().contains("upgrade")
} else {
false
}
})
.unwrap_or(false)
}
#[inline]
/// Get response body chunking state
pub fn chunked(&self) -> bool {
!self.flags.contains(Flags::NO_CHUNKING)
}
#[inline]
pub fn no_chunking(&mut self, val: bool) {
if val {
self.flags.insert(Flags::NO_CHUNKING);
} else {
self.flags.remove(Flags::NO_CHUNKING);
}
}
#[inline]
/// Request contains `EXPECT` header
pub fn expect(&self) -> bool {
self.flags.contains(Flags::EXPECT)
}
#[inline]
pub(crate) fn set_expect(&mut self) {
self.flags.insert(Flags::EXPECT);
}
}
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum RequestHeadType {
Owned(RequestHead),
Rc(Rc<RequestHead>, Option<HeaderMap>),
}
impl RequestHeadType {
pub fn extra_headers(&self) -> Option<&HeaderMap> {
match self {
RequestHeadType::Owned(_) => None,
RequestHeadType::Rc(_, headers) => headers.as_ref(),
}
}
}
impl AsRef<RequestHead> for RequestHeadType {
fn as_ref(&self) -> &RequestHead {
match self {
RequestHeadType::Owned(head) => head,
RequestHeadType::Rc(head, _) => head.as_ref(),
}
}
}
impl From<RequestHead> for RequestHeadType {
fn from(head: RequestHead) -> Self {
RequestHeadType::Owned(head)
}
}

View File

@ -0,0 +1,7 @@
//! HTTP requests.
mod head;
mod request;
pub use self::head::{RequestHead, RequestHeadType};
pub use self::request::Request;

View File

@ -10,11 +10,7 @@ use std::{
use http::{header, Method, Uri, Version}; use http::{header, Method, Uri, Version};
use crate::{ use crate::{
extensions::Extensions, header::HeaderMap, Extensions, HttpMessage, Message, Payload, PayloadStream, RequestHead,
header::HeaderMap,
message::{Message, RequestHead},
payload::{Payload, PayloadStream},
HttpMessage,
}; };
/// An HTTP request. /// An HTTP request.
@ -206,7 +202,7 @@ impl<P> Request<P> {
/// Returns the request data container, leaving an empty one in it's place. /// Returns the request data container, leaving an empty one in it's place.
pub fn take_req_data(&mut self) -> Extensions { pub fn take_req_data(&mut self) -> Extensions {
mem::take(&mut self.req_data.get_mut()) mem::take(self.req_data.get_mut())
} }
} }

View File

@ -9,8 +9,8 @@ use crate::{
body::{EitherBody, MessageBody}, body::{EitherBody, MessageBody},
error::{Error, HttpError}, error::{Error, HttpError},
header::{self, TryIntoHeaderPair, TryIntoHeaderValue}, header::{self, TryIntoHeaderPair, TryIntoHeaderValue},
message::{BoxedResponseHead, ConnectionType, ResponseHead}, responses::{BoxedResponseHead, ResponseHead},
Extensions, Response, StatusCode, ConnectionType, Extensions, Response, StatusCode,
}; };
/// An HTTP response builder. /// 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}, body::{BoxBody, MessageBody},
extensions::Extensions, extensions::Extensions,
header::{self, HeaderMap, TryIntoHeaderValue}, header::{self, HeaderMap, TryIntoHeaderValue},
message::{BoxedResponseHead, ResponseHead}, responses::{BoxedResponseHead, ResponseBuilder, ResponseHead},
Error, ResponseBuilder, StatusCode, Error, StatusCode,
}; };
/// An HTTP response. /// An HTTP response.

View File

@ -9,7 +9,7 @@ use derive_more::{Display, Error, From};
use http::{header, Method, StatusCode}; use http::{header, Method, StatusCode};
use crate::body::BoxBody; use crate::body::BoxBody;
use crate::{header::HeaderValue, message::RequestHead, response::Response, ResponseBuilder}; use crate::{header::HeaderValue, RequestHead, Response, ResponseBuilder};
mod codec; mod codec;
mod dispatcher; mod dispatcher;

View File

@ -221,6 +221,7 @@ where
req_data, req_data,
) )
}; };
self.service.call(ServiceRequest::new(req, payload)) self.service.call(ServiceRequest::new(req, payload))
} }
} }