diff --git a/actix-http/examples/echo.rs b/actix-http/examples/echo.rs index 22f553f38..0b1a5081c 100644 --- a/actix-http/examples/echo.rs +++ b/actix-http/examples/echo.rs @@ -15,6 +15,7 @@ async fn main() -> io::Result<()> { HttpService::build() .client_timeout(1000) .client_disconnect(1000) + // handles HTTP/1.1 and HTTP/2 .finish(|mut req: Request| async move { let mut body = BytesMut::new(); while let Some(item) = req.payload().next().await { @@ -23,12 +24,15 @@ async fn main() -> io::Result<()> { log::info!("request body: {:?}", body); - Ok::<_, Error>( - Response::build(StatusCode::OK) - .insert_header(("x-head", HeaderValue::from_static("dummy value!"))) - .body(body), - ) + let res = Response::build(StatusCode::OK) + .insert_header(("x-head", HeaderValue::from_static("dummy value!"))) + .body(body); + + res.req_data_mut().insert(5usize); + + Ok::<_, Error>(res) }) + // No TLS .tcp() })? .run() diff --git a/actix-http/examples/echo2.rs b/actix-http/examples/echo2.rs index e3b915e05..605572d8b 100644 --- a/actix-http/examples/echo2.rs +++ b/actix-http/examples/echo2.rs @@ -1,32 +1,34 @@ use std::io; use actix_http::{ - body::MessageBody, header::HeaderValue, Error, HttpService, Request, Response, StatusCode, + body::{BodyStream, MessageBody}, + header, Error, HttpMessage, HttpService, Request, Response, StatusCode, }; -use actix_server::Server; -use bytes::BytesMut; -use futures_util::StreamExt as _; async fn handle_request(mut req: Request) -> Result, Error> { - let mut body = BytesMut::new(); - while let Some(item) = req.payload().next().await { - body.extend_from_slice(&item?) + let mut res = Response::build(StatusCode::OK); + + if let Some(ct) = req.headers().get(header::CONTENT_TYPE) { + res.insert_header((header::CONTENT_TYPE, ct)); } - log::info!("request body: {:?}", body); + // echo request payload stream as (chunked) response body + let res = res.message_body(BodyStream::new(req.payload().take()))?; - Ok(Response::build(StatusCode::OK) - .insert_header(("x-head", HeaderValue::from_static("dummy value!"))) - .body(body)) + Ok(res) } #[actix_rt::main] async fn main() -> io::Result<()> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); - Server::build() + actix_server::Server::build() .bind("echo", ("127.0.0.1", 8080), || { - HttpService::build().finish(handle_request).tcp() + HttpService::build() + // handles HTTP/1.1 only + .h1(handle_request) + // No TLS + .tcp() })? .run() .await diff --git a/actix-http/src/message.rs b/actix-http/src/message.rs index ecd08fbb3..5616a4762 100644 --- a/actix-http/src/message.rs +++ b/actix-http/src/message.rs @@ -5,13 +5,13 @@ use bitflags::bitflags; /// Represents various types of connection #[derive(Copy, Clone, PartialEq, Debug)] pub enum ConnectionType { - /// Close connection after response + /// Close connection after response. Close, - /// Keep connection alive after response + /// Keep connection alive after response. KeepAlive, - /// Connection is upgraded to different type + /// Connection is upgraded to different type. Upgrade, } @@ -69,8 +69,8 @@ impl Drop for Message { } } +/// Generic `Head` object pool. #[doc(hidden)] -/// Request's objects pool pub struct MessagePool(RefCell>>); impl MessagePool { diff --git a/actix-http/src/requests/head.rs b/actix-http/src/requests/head.rs index 524075b61..06fd0429e 100644 --- a/actix-http/src/requests/head.rs +++ b/actix-http/src/requests/head.rs @@ -142,8 +142,8 @@ impl RequestHead { } } -#[derive(Debug)] #[allow(clippy::large_enum_variant)] +#[derive(Debug)] pub enum RequestHeadType { Owned(RequestHead), Rc(Rc, Option), diff --git a/actix-http/src/responses/builder.rs b/actix-http/src/responses/builder.rs index 5854863de..4a67423b1 100644 --- a/actix-http/src/responses/builder.rs +++ b/actix-http/src/responses/builder.rs @@ -1,9 +1,6 @@ //! HTTP response builder. -use std::{ - cell::{Ref, RefMut}, - fmt, str, -}; +use std::{cell::RefCell, fmt, str}; use crate::{ body::{EitherBody, MessageBody}, @@ -202,20 +199,6 @@ impl ResponseBuilder { self } - /// Responses extensions - #[inline] - pub fn extensions(&self) -> Ref<'_, Extensions> { - let head = self.head.as_ref().expect("cannot reuse response builder"); - head.extensions.borrow() - } - - /// Mutable reference to a the response's extensions - #[inline] - pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { - let head = self.head.as_ref().expect("cannot reuse response builder"); - head.extensions.borrow_mut() - } - /// Generate response with a wrapped body. /// /// This `ResponseBuilder` will be left in a useless state. @@ -238,7 +221,12 @@ impl ResponseBuilder { } let head = self.head.take().expect("cannot reuse response builder"); - Ok(Response { head, body }) + + Ok(Response { + head, + body, + extensions: RefCell::new(Extensions::new()), + }) } /// Generate response with an empty body. diff --git a/actix-http/src/responses/head.rs b/actix-http/src/responses/head.rs index d11ba8fde..91e96a928 100644 --- a/actix-http/src/responses/head.rs +++ b/actix-http/src/responses/head.rs @@ -1,25 +1,19 @@ //! Response head type and caching pool. -use std::{ - cell::{Ref, RefCell, RefMut}, - ops, -}; +use std::{cell::RefCell, ops}; -use crate::{ - header::HeaderMap, message::Flags, ConnectionType, Extensions, StatusCode, Version, -}; +use crate::{header::HeaderMap, message::Flags, ConnectionType, StatusCode, Version}; thread_local! { static RESPONSE_POOL: BoxedResponsePool = BoxedResponsePool::create(); } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct ResponseHead { pub version: Version, pub status: StatusCode, pub headers: HeaderMap, pub reason: Option<&'static str>, - pub(crate) extensions: RefCell, pub(crate) flags: Flags, } @@ -33,18 +27,17 @@ impl ResponseHead { headers: HeaderMap::with_capacity(12), reason: None, flags: Flags::empty(), - extensions: RefCell::new(Extensions::new()), } } - #[inline] /// Read the message headers. + #[inline] pub fn headers(&self) -> &HeaderMap { &self.headers } - #[inline] /// Mutable reference to the message headers. + #[inline] pub fn headers_mut(&mut self) -> &mut HeaderMap { &mut self.headers } @@ -61,20 +54,8 @@ impl ResponseHead { } } - /// 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 + #[inline] pub fn set_connection_type(&mut self, ctype: ConnectionType) { match ctype { ConnectionType::Close => self.flags.insert(Flags::CLOSE), @@ -133,14 +114,14 @@ impl ResponseHead { } } - #[inline] /// Get response body chunking state + #[inline] pub fn chunked(&self) -> bool { !self.flags.contains(Flags::NO_CHUNKING) } - #[inline] /// Set no chunking for payload + #[inline] pub fn no_chunking(&mut self, val: bool) { if val { self.flags.insert(Flags::NO_CHUNKING); @@ -183,7 +164,7 @@ impl Drop for BoxedResponseHead { } } -/// Request's objects pool +/// Response head object pool. #[doc(hidden)] pub struct BoxedResponsePool(#[allow(clippy::vec_box)] RefCell>>); @@ -192,7 +173,7 @@ impl BoxedResponsePool { BoxedResponsePool(RefCell::new(Vec::with_capacity(128))) } - /// Get message from the pool + /// Get message from the pool. #[inline] fn get_message(&self, status: StatusCode) -> BoxedResponseHead { if let Some(mut head) = self.0.borrow_mut().pop() { @@ -208,12 +189,12 @@ impl BoxedResponsePool { } } - /// Release request instance + /// Release request instance. #[inline] - fn release(&self, mut msg: Box) { + fn release(&self, 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/response.rs b/actix-http/src/responses/response.rs index ec9157afb..08dddb90a 100644 --- a/actix-http/src/responses/response.rs +++ b/actix-http/src/responses/response.rs @@ -1,7 +1,7 @@ //! HTTP response. use std::{ - cell::{Ref, RefMut}, + cell::{Ref, RefCell, RefMut}, fmt, str, }; @@ -9,7 +9,7 @@ use bytes::{Bytes, BytesMut}; use bytestring::ByteString; use crate::{ - body::{BoxBody, MessageBody}, + body::{BoxBody, EitherBody, MessageBody}, header::{self, HeaderMap, TryIntoHeaderValue}, responses::BoxedResponseHead, Error, Extensions, ResponseBuilder, ResponseHead, StatusCode, @@ -19,6 +19,7 @@ use crate::{ pub struct Response { pub(crate) head: BoxedResponseHead, pub(crate) body: B, + pub(crate) extensions: RefCell, } impl Response { @@ -28,6 +29,7 @@ impl Response { Response { head: BoxedResponseHead::new(status), body: BoxBody::new(()), + extensions: RefCell::new(Extensions::new()), } } @@ -74,6 +76,7 @@ impl Response { Response { head: BoxedResponseHead::new(status), body, + extensions: RefCell::new(Extensions::new()), } } @@ -120,6 +123,7 @@ impl Response { } /// Returns true if keep-alive is enabled. + #[inline] pub fn keep_alive(&self) -> bool { self.head.keep_alive() } @@ -127,13 +131,13 @@ impl Response { /// Returns a reference to the extensions of this response. #[inline] pub fn extensions(&self) -> Ref<'_, Extensions> { - self.head.extensions.borrow() + self.extensions.borrow() } /// Returns a mutable reference to the extensions of this response. #[inline] - pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { - self.head.extensions.borrow_mut() + pub fn extensions_mut(&self) -> RefMut<'_, Extensions> { + self.extensions.borrow_mut() } /// Returns a reference to the body of this response. @@ -143,24 +147,29 @@ impl Response { } /// Sets new body. + #[inline] pub fn set_body(self, body: B2) -> Response { Response { head: self.head, body, + extensions: self.extensions, } } /// Drops body and returns new response. + #[inline] pub fn drop_body(self) -> Response<()> { self.set_body(()) } /// Sets new body, returning new response and previous body value. + #[inline] pub(crate) fn replace_body(self, body: B2) -> (Response, B) { ( Response { head: self.head, body, + extensions: self.extensions, }, self.body, ) @@ -171,11 +180,13 @@ impl Response { /// # Implementation Notes /// Due to internal performance optimizations, the first element of the returned tuple is a /// `Response` as well but only contains the head of the response this was called on. + #[inline] pub fn into_parts(self) -> (Response<()>, B) { self.replace_body(()) } /// Returns new response with mapped body. + #[inline] pub fn map_body(mut self, f: F) -> Response where F: FnOnce(&mut ResponseHead, B) -> B2, @@ -185,6 +196,7 @@ impl Response { Response { head: self.head, body, + extensions: self.extensions, } } @@ -197,6 +209,7 @@ impl Response { } /// Returns body, consuming this response. + #[inline] pub fn into_body(self) -> B { self.body } @@ -239,9 +252,9 @@ impl>, E: Into> From> for Response } } -impl From for Response { +impl From for Response> { fn from(mut builder: ResponseBuilder) -> Self { - builder.finish().map_into_boxed_body() + builder.finish() } } diff --git a/awc/src/responses/response.rs b/awc/src/responses/response.rs index 0197265f1..017c59744 100644 --- a/awc/src/responses/response.rs +++ b/awc/src/responses/response.rs @@ -1,5 +1,5 @@ use std::{ - cell::{Ref, RefMut}, + cell::{Ref, RefCell, RefMut}, fmt, mem, pin::Pin, task::{Context, Poll}, @@ -28,6 +28,8 @@ pin_project! { #[pin] pub(crate) payload: Payload, pub(crate) timeout: ResponseTimeout, + pub(crate) extensions: RefCell, + } } @@ -38,6 +40,7 @@ impl ClientResponse { head, payload, timeout: ResponseTimeout::default(), + extensions: RefCell::new(Extensions::new()), } } @@ -75,6 +78,7 @@ impl ClientResponse { payload, head: self.head, timeout: self.timeout, + extensions: self.extensions, } } @@ -101,6 +105,7 @@ impl ClientResponse { payload: self.payload, head: self.head, timeout, + extensions: self.extensions, } } @@ -112,6 +117,18 @@ impl ClientResponse { self } + /// Returns a reference to the extensions of this response. + #[inline] + pub fn req_data(&self) -> Ref<'_, Extensions> { + self.extensions.borrow() + } + + /// Returns a mutable reference to the extensions of this response. + #[inline] + pub fn req_data_mut(&self) -> RefMut<'_, Extensions> { + self.extensions.borrow_mut() + } + /// Load request cookies. #[cfg(feature = "cookies")] pub fn cookies(&self) -> Result>>, CookieParseError> { @@ -224,11 +241,11 @@ impl HttpMessage for ClientResponse { } fn extensions(&self) -> Ref<'_, Extensions> { - self.head.extensions() + self.req_data() } fn extensions_mut(&self) -> RefMut<'_, Extensions> { - self.head.extensions_mut() + self.req_data_mut() } } diff --git a/src/request.rs b/src/request.rs index e876c3b4d..2eed75a36 100644 --- a/src/request.rs +++ b/src/request.rs @@ -5,10 +5,7 @@ use std::{ str, }; -use actix_http::{ - header::HeaderMap, Extensions, HttpMessage, Message, Method, Payload, RequestHead, Uri, - Version, -}; +use actix_http::{Message, RequestHead}; use actix_router::{Path, Url}; use actix_utils::future::{ok, Ready}; #[cfg(feature = "cookies")] @@ -16,8 +13,14 @@ use cookie::{Cookie, ParseError as CookieParseError}; use smallvec::SmallVec; use crate::{ - app_service::AppInitServiceState, config::AppConfig, error::UrlGenerationError, - info::ConnectionInfo, rmap::ResourceMap, Error, FromRequest, + app_service::AppInitServiceState, + config::AppConfig, + dev::{Extensions, Payload}, + error::UrlGenerationError, + http::{header::HeaderMap, Method, Uri, Version}, + info::ConnectionInfo, + rmap::ResourceMap, + Error, FromRequest, HttpMessage, }; #[cfg(feature = "cookies")] diff --git a/src/response/builder.rs b/src/response/builder.rs index bdb0aaa12..740c890f6 100644 --- a/src/response/builder.rs +++ b/src/response/builder.rs @@ -1,5 +1,4 @@ use std::{ - cell::{Ref, RefMut}, convert::TryInto, future::Future, pin::Pin, @@ -7,21 +6,16 @@ use std::{ }; use actix_http::{ - body::{BodyStream, BoxBody, MessageBody}, error::HttpError, header::{self, HeaderName, TryIntoHeaderPair, TryIntoHeaderValue}, - ConnectionType, Extensions, Response, ResponseHead, StatusCode, + ConnectionType, Response, ResponseHead, StatusCode, }; use bytes::Bytes; use futures_core::Stream; use serde::Serialize; -#[cfg(feature = "cookies")] -use actix_http::header::HeaderValue; -#[cfg(feature = "cookies")] -use cookie::{Cookie, CookieJar}; - use crate::{ + body::{BodyStream, BoxBody, MessageBody}, error::{Error, JsonPayloadError}, BoxError, HttpRequest, HttpResponse, Responder, }; @@ -33,7 +27,7 @@ pub struct HttpResponseBuilder { res: Option>, err: Option, #[cfg(feature = "cookies")] - cookies: Option, + cookies: Option, } impl HttpResponseBuilder { @@ -242,9 +236,9 @@ impl HttpResponseBuilder { /// .finish(); /// ``` #[cfg(feature = "cookies")] - pub fn cookie<'c>(&mut self, cookie: Cookie<'c>) -> &mut Self { + pub fn cookie<'c>(&mut self, cookie: cookie::Cookie<'c>) -> &mut Self { if self.cookies.is_none() { - let mut jar = CookieJar::new(); + let mut jar = cookie::CookieJar::new(); jar.add(cookie.into_owned()); self.cookies = Some(jar) } else { @@ -271,9 +265,9 @@ impl HttpResponseBuilder { /// } /// ``` #[cfg(feature = "cookies")] - pub fn del_cookie(&mut self, cookie: &Cookie<'_>) -> &mut Self { + pub fn del_cookie(&mut self, cookie: &cookie::Cookie<'_>) -> &mut Self { if self.cookies.is_none() { - self.cookies = Some(CookieJar::new()) + self.cookies = Some(cookie::CookieJar::new()) } let jar = self.cookies.as_mut().unwrap(); let cookie = cookie.clone().into_owned(); @@ -282,22 +276,22 @@ impl HttpResponseBuilder { self } - /// Responses extensions - #[inline] - pub fn extensions(&self) -> Ref<'_, Extensions> { - self.res - .as_ref() - .expect("cannot reuse response builder") - .extensions() - } + // /// Responses extensions + // #[inline] + // pub fn extensions(&self) -> Ref<'_, Extensions> { + // self.res + // .as_ref() + // .expect("cannot reuse response builder") + // .extensions() + // } - /// Mutable reference to a the response's extensions - pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { - self.res - .as_mut() - .expect("cannot reuse response builder") - .extensions_mut() - } + // /// Mutable reference to a the response's extensions + // pub fn extensions_mut(&mut self) -> RefMut<'_, Extensions> { + // self.res + // .as_mut() + // .expect("cannot reuse response builder") + // .extensions_mut() + // } /// Set a body and build the `HttpResponse`. /// @@ -332,7 +326,7 @@ impl HttpResponseBuilder { #[cfg(feature = "cookies")] if let Some(ref jar) = self.cookies { for cookie in jar.delta() { - match HeaderValue::from_str(&cookie.to_string()) { + match actix_http::header::HeaderValue::from_str(&cookie.to_string()) { Ok(val) => res.headers_mut().append(header::SET_COOKIE, val), Err(err) => return Err(err.into()), }; @@ -435,10 +429,9 @@ impl Responder for HttpResponseBuilder { #[cfg(test)] mod tests { - use actix_http::body; - use super::*; use crate::{ + body, http::{ header::{self, HeaderValue, CONTENT_TYPE}, StatusCode, diff --git a/src/service.rs b/src/service.rs index f15cbfc9f..4ca2b518a 100644 --- a/src/service.rs +++ b/src/service.rs @@ -103,6 +103,7 @@ impl ServiceRequest { /// Construct request from request. /// /// The returned `ServiceRequest` would have no payload. + #[inline] pub fn from_request(req: HttpRequest) -> Self { ServiceRequest { req, @@ -546,14 +547,12 @@ impl WebService { /// Ok(req.into_response(HttpResponse::Ok().finish())) /// } /// - /// fn main() { - /// let app = App::new() - /// .service( - /// web::service("/app") - /// .guard(guard::Header("content-type", "text/plain")) - /// .finish(index) - /// ); - /// } + /// let app = App::new() + /// .service( + /// web::service("/app") + /// .guard(guard::Header("content-type", "text/plain")) + /// .finish(index) + /// ); /// ``` pub fn guard(mut self, guard: G) -> Self { self.guards.push(Box::new(guard));